From f4b921b7fc84759dfc62cb01193b7d917ca94ce0 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 7 Feb 2020 06:03:55 -0700 Subject: [PATCH 01/38] Plugin error stack traces (#369) Because of how the Log4J2 api works exception stack traces were not being printed. Update to use the explicit "throwable" overloaded methods. Signed-off-by: Danno Ferrin --- .../besu/services/BesuPluginContextImpl.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java b/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java index 791edb39506..2a5531be785 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/BesuPluginContextImpl.java @@ -96,8 +96,9 @@ public void registerPlugins(final Path pluginsDir) { addPluginVersion(plugin); } catch (final Exception e) { LOG.error( - "Error registering plugin of type {}, start and stop will not be called. \n{}", - plugin.getClass(), + "Error registering plugin of type " + + plugin.getClass().getName() + + ", start and stop will not be called.", e); continue; } @@ -140,8 +141,9 @@ public void startPlugins() { LOG.debug("Started plugin of type {}.", plugin.getClass().getName()); } catch (final Exception e) { LOG.error( - "Error starting plugin of type {}, stop will not be called. \n{}", - plugin.getClass(), + "Error starting plugin of type " + + plugin.getClass().getName() + + ", stop will not be called.", e); pluginsIterator.remove(); } @@ -164,7 +166,7 @@ public void stopPlugins() { plugin.stop(); LOG.debug("Stopped plugin of type {}.", plugin.getClass().getName()); } catch (final Exception e) { - LOG.error("Error stopping plugin of type {}. \n{}", plugin.getClass(), e); + LOG.error("Error stopping plugin of type " + plugin.getClass().getName(), e); } } From 059f0ae5f525b9ccb36bf9cc6ea997765759d307 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 7 Feb 2020 07:33:56 -0700 Subject: [PATCH 02/38] VM Trace fixes (#372) * correct refund addresses * correct returned memory from static precompiled calls. * update integration test * precompiles sometimes get plain old CALLs Signed-off-by: Danno Ferrin --- .../tracing/flat/FlatTraceGenerator.java | 2 +- .../results/tracing/vm/VmTraceGenerator.java | 52 +++++++++++++------ .../vm/TraceTransactionIntegrationTest.java | 6 ++- .../ethereum/vm/AbstractCallOperation.java | 4 +- .../ethereum/vm/DebugOperationTracer.java | 2 +- .../operations/ReturnDataCopyOperation.java | 2 +- 6 files changed, 47 insertions(+), 21 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java index b188fecdc9f..30d2b577d41 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java @@ -233,7 +233,7 @@ private static FlatTrace.Context handleSelfDestruct( final List flatTraces, final Deque tracesContexts) { final Bytes32[] stack = traceFrame.getStack().orElseThrow(); - final Address refundAddress = toAddress(stack[0]); + final Address refundAddress = toAddress(stack[stack.length - 1]); final FlatTrace.Builder subTraceBuilder = FlatTrace.builder() .type("suicide") diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java index 1ba59fe2abe..9209265c8c2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java @@ -40,6 +40,7 @@ public class VmTraceGenerator { private int currentIndex = 0; private VmTrace currentTrace; private TraceFrame currentTraceFrame; + private String currentOperation; private final TransactionTrace transactionTrace; private final VmTrace rootVmTrace = new VmTrace(); private final Deque parentTraces = new ArrayDeque<>(); @@ -117,10 +118,9 @@ private void completeStep(final VmOperation op, final VmOperationExecutionReport private void handleDepthIncreased(final VmOperation op, final VmOperationExecutionReport report) { // check if next frame depth has increased i.e the current operation is a call - if (currentTraceFrame.depthHasIncreased()) { - op.setCost(currentTraceFrame.getGasRemainingPostExecution().toLong() + op.getCost()); - final VmTrace newSubTrace = new VmTrace(); - parentTraces.addLast(newSubTrace); + if (currentTraceFrame.depthHasIncreased() + || "STATICCALL".equals(currentOperation) + || "CALL".equals(currentOperation)) { findLastFrameInCall(currentTraceFrame, currentIndex) .ifPresent( lastFrameInCall -> { @@ -138,13 +138,21 @@ private void handleDepthIncreased(final VmOperation op, final VmOperationExecuti break; default: lastFrameInCall - .getMemory() - .map(mem -> mem.length > 0 ? new Mem(mem[0].toHexString(), 0) : null) + .getMaybeUpdatedMemory() + .map( + mem -> + new Mem(mem.getValue().toHexString(), mem.getOffset().intValue())) .ifPresent(report::setMem); } }); - - op.setSub(newSubTrace); + if (currentTraceFrame.depthHasIncreased()) { + op.setCost(currentTraceFrame.getGasRemainingPostExecution().toLong() + op.getCost()); + final VmTrace newSubTrace = new VmTrace(); + parentTraces.addLast(newSubTrace); + op.setSub(newSubTrace); + } else { + op.setSub(new VmTrace()); + } } } @@ -173,13 +181,26 @@ private VmOperationExecutionReport generateExecutionReport() { } private void generateTracingMemory(final VmOperationExecutionReport report) { - currentTraceFrame - .getMaybeUpdatedMemory() - .map( - updatedMemory -> - new Mem( - updatedMemory.getValue().toHexString(), updatedMemory.getOffset().intValue())) - .ifPresent(report::setMem); + switch (currentOperation) { + case "CALLDATACOPY": + case "CODECOPY": + case "EXTCODECOPY": + case "MLOAD": + case "MSTORE": + case "MSTORE8": + case "RETURNDATACOPY": + currentTraceFrame + .getMaybeUpdatedMemory() + .map( + updatedMemory -> + new Mem( + updatedMemory.getValue().toHexString(), + updatedMemory.getOffset().intValue())) + .ifPresent(report::setMem); + break; + default: + break; + } } private void generateTracingPush(final VmOperationExecutionReport report) { @@ -214,6 +235,7 @@ private void generateTracingStorage(final VmOperationExecutionReport report) { */ private void initStep(final TraceFrame frame) { this.currentTraceFrame = frame; + this.currentOperation = frame.getOpcode(); currentTrace = parentTraces.getLast(); // set smart contract code currentTrace.setCode( diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java index f5ef715df5d..927f162663d 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/vm/TraceTransactionIntegrationTest.java @@ -202,7 +202,11 @@ public void shouldTraceContractCreation() { frame, "0000000000000000000000000000000000000000000000000000000000000080", "0000000000000000000000000000000000000000000000000000000000000040"); - assertMemoryContainsExactly(frame); + assertMemoryContainsExactly( + frame, + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000080"); assertStorageContainsExactly(frame); // Reference implementation actually records the memory after expansion but before the store. // assertMemoryContainsExactly(frame, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/AbstractCallOperation.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/AbstractCallOperation.java index e54f0d6c105..a918206f034 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/AbstractCallOperation.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/AbstractCallOperation.java @@ -206,9 +206,9 @@ public void complete(final MessageFrame frame, final MessageFrame childFrame) { if (outputSizeAsInt > outputData.size()) { frame.expandMemory(outputOffset.toLong(), outputSizeAsInt); - frame.writeMemory(outputOffset, UInt256.valueOf(outputData.size()), outputData); + frame.writeMemory(outputOffset, UInt256.valueOf(outputData.size()), outputData, true); } else { - frame.writeMemory(outputOffset, outputSize, outputData); + frame.writeMemory(outputOffset, outputSize, outputData, true); } frame.setReturnData(outputData); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java index 63977b68c72..047e417297c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java @@ -61,7 +61,6 @@ public void traceExecution( EnumSet.copyOf(frame.getExceptionalHaltReasons()); final Bytes inputData = frame.getInputData(); final Optional stack = captureStack(frame); - final Optional memory = captureMemory(frame); final Optional> storagePreExecution = captureStorage(frame); final WorldUpdater worldUpdater = frame.getWorldState(); final Optional stackPostExecution; @@ -70,6 +69,7 @@ public void traceExecution( executeOperation.execute(); } finally { final Bytes outputData = frame.getOutputData(); + final Optional memory = captureMemory(frame); stackPostExecution = captureStack(frame); memoryPostExecution = captureMemory(frame); if (lastFrame != null) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/ReturnDataCopyOperation.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/ReturnDataCopyOperation.java index 6e631a08e29..c6935ff16a1 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/ReturnDataCopyOperation.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/ReturnDataCopyOperation.java @@ -49,7 +49,7 @@ public void execute(final MessageFrame frame) { final UInt256 sourceOffset = UInt256.fromBytes(frame.popStackItem()); final UInt256 numBytes = UInt256.fromBytes(frame.popStackItem()); - frame.writeMemory(memOffset, sourceOffset, numBytes, returnData); + frame.writeMemory(memOffset, sourceOffset, numBytes, returnData, true); } @Override From 9b35c3b65844580c1e897a9790d6d392d3945b3f Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Mon, 10 Feb 2020 11:02:23 +1000 Subject: [PATCH 03/38] Validate private transaction before sending to enclave (#356) Signed-off-by: Jason Frame --- .../methods/PrivacySendTransaction.java | 115 ------------ .../methods/eea/EeaSendRawTransaction.java | 79 ++++----- .../priv/PrivDistributeRawTransaction.java | 61 +++---- .../response/JsonRpcErrorResponse.java | 6 + .../eea/EeaSendRawTransactionTest.java | 166 +++++++++--------- .../PrivDistributeRawTransactionTest.java | 12 +- .../mainnet/TransactionValidator.java | 4 +- .../privacy/DefaultPrivacyController.java | 29 +-- .../MultiTenancyPrivacyController.java | 9 +- .../ethereum/privacy/PrivacyController.java | 5 +- .../ethereum/privacy/PrivacyGroupUtil.java | 50 ++++++ .../ethereum/privacy/PrivateTransaction.java | 16 ++ .../privacy/PrivateTransactionValidator.java | 27 ++- .../privacy/SendTransactionResponse.java | 33 ---- .../core/PrivateTransactionTestFixture.java | 9 +- .../privacy/DefaultPrivacyControllerTest.java | 32 ++-- .../MultiTenancyPrivacyControllerTest.java | 14 +- .../privacy/PrivacyGroupUtilTest.java | 84 +++++++++ .../PrivateTransactionValidatorTest.java | 63 ++++++- 19 files changed, 431 insertions(+), 383 deletions(-) delete mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/PrivacySendTransaction.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyGroupUtil.java delete mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/SendTransactionResponse.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivacyGroupUtilTest.java diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/PrivacySendTransaction.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/PrivacySendTransaction.java deleted file mode 100644 index b2640e0926f..00000000000 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/PrivacySendTransaction.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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.privacy.methods; - -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcRequestException; -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.privacy.PrivacyController; -import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; -import org.hyperledger.besu.ethereum.privacy.Restriction; -import org.hyperledger.besu.ethereum.rlp.RLP; -import org.hyperledger.besu.ethereum.rlp.RLPException; - -import java.util.function.Supplier; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.apache.tuweni.bytes.Bytes; - -public class PrivacySendTransaction { - - private static final Logger LOG = LogManager.getLogger(); - - protected final PrivacyController privacyController; - private final EnclavePublicKeyProvider enclavePublicKeyProvider; - - public PrivacySendTransaction( - final PrivacyController privacyController, - final EnclavePublicKeyProvider enclavePublicKeyProvider) { - this.privacyController = privacyController; - this.enclavePublicKeyProvider = enclavePublicKeyProvider; - } - - public PrivateTransaction validateAndDecodeRequest(final JsonRpcRequestContext request) - throws ErrorResponseException { - if (request.getRequest().getParamLength() != 1) { - throw new ErrorResponseException( - new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS)); - } - final String rawPrivateTransaction = request.getRequiredParameter(0, String.class); - final PrivateTransaction privateTransaction; - try { - privateTransaction = decodeRawTransaction(rawPrivateTransaction); - } catch (final InvalidJsonRpcRequestException e) { - throw new ErrorResponseException( - new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.DECODE_ERROR)); - } - if (!privateTransaction.getValue().isZero()) { - throw new ErrorResponseException( - new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.VALUE_NOT_ZERO)); - } - if (!privateTransaction.getRestriction().equals(Restriction.RESTRICTED)) { - throw new ErrorResponseException( - new JsonRpcErrorResponse( - request.getRequest().getId(), JsonRpcError.UNIMPLEMENTED_PRIVATE_TRANSACTION_TYPE)); - } - return privateTransaction; - } - - public JsonRpcResponse validateAndExecute( - final JsonRpcRequestContext request, - final PrivateTransaction privateTransaction, - final String privacyGroupId, - final Supplier successfulJsonRpcResponse) { - return privacyController - .validatePrivateTransaction( - privateTransaction, - privacyGroupId, - enclavePublicKeyProvider.getEnclaveKey(request.getUser())) - .either( - successfulJsonRpcResponse, - (errorReason) -> - new JsonRpcErrorResponse( - request.getRequest().getId(), - JsonRpcErrorConverter.convertTransactionInvalidReason(errorReason))); - } - - private PrivateTransaction decodeRawTransaction(final String hash) - throws InvalidJsonRpcRequestException { - try { - return PrivateTransaction.readFrom(RLP.input(Bytes.fromHexString(hash))); - } catch (final IllegalArgumentException | RLPException e) { - LOG.debug(e); - throw new InvalidJsonRpcRequestException("Invalid raw private transaction hex", e); - } - } - - public static class ErrorResponseException extends Exception { - private final JsonRpcResponse response; - - private ErrorResponseException(final JsonRpcResponse response) { - super(); - this.response = response; - } - - public JsonRpcResponse getResponse() { - return response; - } - } -} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java index a7a049ba152..26b9aec2301 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransaction.java @@ -16,34 +16,36 @@ import static org.apache.logging.log4j.LogManager.getLogger; import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason; +import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter.convertTransactionInvalidReason; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.DECODE_ERROR; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.ENCLAVE_ERROR; -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacySendTransaction; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacySendTransaction.ErrorResponseException; 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.core.Transaction; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; +import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason; +import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException; import org.hyperledger.besu.ethereum.privacy.PrivacyController; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; -import org.hyperledger.besu.ethereum.privacy.SendTransactionResponse; +import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.ethereum.rlp.RLPException; import org.apache.logging.log4j.Logger; +import org.apache.tuweni.bytes.Bytes; public class EeaSendRawTransaction implements JsonRpcMethod { private static final Logger LOG = getLogger(); - private final PrivacySendTransaction privacySendTransaction; - private final EnclavePublicKeyProvider enclavePublicKeyProvider; private final TransactionPool transactionPool; private final PrivacyController privacyController; + private final EnclavePublicKeyProvider enclavePublicKeyProvider; public EeaSendRawTransaction( final TransactionPool transactionPool, @@ -51,8 +53,6 @@ public EeaSendRawTransaction( final EnclavePublicKeyProvider enclavePublicKeyProvider) { this.transactionPool = transactionPool; this.privacyController = privacyController; - this.privacySendTransaction = - new PrivacySendTransaction(privacyController, enclavePublicKeyProvider); this.enclavePublicKeyProvider = enclavePublicKeyProvider; } @@ -63,45 +63,40 @@ public String getName() { @Override public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { - final PrivateTransaction privateTransaction; - try { - privateTransaction = privacySendTransaction.validateAndDecodeRequest(requestContext); - } catch (final ErrorResponseException e) { - return e.getResponse(); - } + final String rawPrivateTransaction = requestContext.getRequiredParameter(0, String.class); + final Object id = requestContext.getRequest().getId(); - final SendTransactionResponse sendTransactionResponse; try { - sendTransactionResponse = - privacyController.sendTransaction( - privateTransaction, enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser())); + final PrivateTransaction privateTransaction = + PrivateTransaction.readFrom(RLP.input(Bytes.fromHexString(rawPrivateTransaction))); + + final String enclavePublicKey = + enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()); + final ValidationResult validationResult = + privacyController.validatePrivateTransaction(privateTransaction, enclavePublicKey); + if (!validationResult.isValid()) { + return new JsonRpcErrorResponse( + id, convertTransactionInvalidReason(validationResult.getInvalidReason())); + } + + final String enclaveKey = + privacyController.sendTransaction(privateTransaction, enclavePublicKey); + final Transaction privacyMarkerTransaction = + privacyController.createPrivacyMarkerTransaction(enclaveKey, privateTransaction); + + return transactionPool + .addLocalTransaction(privacyMarkerTransaction) + .either( + () -> new JsonRpcSuccessResponse(id, privacyMarkerTransaction.getHash().toString()), + errorReason -> + new JsonRpcErrorResponse(id, convertTransactionInvalidReason(errorReason))); } catch (final MultiTenancyValidationException e) { LOG.error("Unauthorized privacy multi-tenancy rpc request. {}", e.getMessage()); - return new JsonRpcErrorResponse(requestContext.getRequest().getId(), ENCLAVE_ERROR); + return new JsonRpcErrorResponse(id, ENCLAVE_ERROR); + } catch (final IllegalArgumentException | RLPException e) { + return new JsonRpcErrorResponse(id, DECODE_ERROR); } catch (final Exception e) { - return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), convertEnclaveInvalidReason(e.getMessage())); + return new JsonRpcErrorResponse(id, convertEnclaveInvalidReason(e.getMessage())); } - - return privacySendTransaction.validateAndExecute( - requestContext, - privateTransaction, - sendTransactionResponse.getPrivacyGroupId(), - () -> { - final Transaction privacyMarkerTransaction = - privacyController.createPrivacyMarkerTransaction( - sendTransactionResponse.getEnclaveKey(), privateTransaction); - return transactionPool - .addLocalTransaction(privacyMarkerTransaction) - .either( - () -> - new JsonRpcSuccessResponse( - requestContext.getRequest().getId(), - privacyMarkerTransaction.getHash().toString()), - errorReason -> - new JsonRpcErrorResponse( - requestContext.getRequest().getId(), - JsonRpcErrorConverter.convertTransactionInvalidReason(errorReason))); - }); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransaction.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransaction.java index 96c67f0bbc0..dce8b7065cf 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransaction.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransaction.java @@ -15,22 +15,25 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv; import static org.apache.logging.log4j.LogManager.getLogger; +import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason; +import static org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcErrorConverter.convertTransactionInvalidReason; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.DECODE_ERROR; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.ENCLAVE_ERROR; -import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcEnclaveErrorConverter; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacySendTransaction; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.PrivacySendTransaction.ErrorResponseException; 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.mainnet.TransactionValidator.TransactionInvalidReason; +import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException; import org.hyperledger.besu.ethereum.privacy.PrivacyController; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; -import org.hyperledger.besu.ethereum.privacy.SendTransactionResponse; +import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.ethereum.rlp.RLPException; import java.util.Base64; @@ -41,15 +44,12 @@ public class PrivDistributeRawTransaction implements JsonRpcMethod { private static final Logger LOG = getLogger(); private final PrivacyController privacyController; - private final PrivacySendTransaction privacySendTransaction; private final EnclavePublicKeyProvider enclavePublicKeyProvider; public PrivDistributeRawTransaction( final PrivacyController privacyController, final EnclavePublicKeyProvider enclavePublicKeyProvider) { this.privacyController = privacyController; - this.privacySendTransaction = - new PrivacySendTransaction(privacyController, enclavePublicKeyProvider); this.enclavePublicKeyProvider = enclavePublicKeyProvider; } @@ -60,35 +60,36 @@ public String getName() { @Override public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { - final PrivateTransaction privateTransaction; - try { - privateTransaction = privacySendTransaction.validateAndDecodeRequest(requestContext); - } catch (ErrorResponseException e) { - return e.getResponse(); - } + final String rawPrivateTransaction = requestContext.getRequiredParameter(0, String.class); + final Object id = requestContext.getRequest().getId(); - final SendTransactionResponse sendTransactionResponse; try { - sendTransactionResponse = - privacyController.sendTransaction( - privateTransaction, enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser())); + final PrivateTransaction privateTransaction = + PrivateTransaction.readFrom(RLP.input(Bytes.fromHexString(rawPrivateTransaction))); + + final String enclavePublicKey = + enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()); + final ValidationResult validationResult = + privacyController.validatePrivateTransaction(privateTransaction, enclavePublicKey); + if (!validationResult.isValid()) { + return new JsonRpcErrorResponse( + id, convertTransactionInvalidReason(validationResult.getInvalidReason())); + } + + final String enclaveKey = + privacyController.sendTransaction(privateTransaction, enclavePublicKey); + return new JsonRpcSuccessResponse(id, hexEncodeEnclaveKey(enclaveKey)); } catch (final MultiTenancyValidationException e) { LOG.error("Unauthorized privacy multi-tenancy rpc request. {}", e.getMessage()); - return new JsonRpcErrorResponse(requestContext.getRequest().getId(), ENCLAVE_ERROR); + return new JsonRpcErrorResponse(id, ENCLAVE_ERROR); + } catch (final IllegalArgumentException | RLPException e) { + return new JsonRpcErrorResponse(id, DECODE_ERROR); } catch (final Exception e) { - return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), - JsonRpcEnclaveErrorConverter.convertEnclaveInvalidReason(e.getMessage())); + return new JsonRpcErrorResponse(id, convertEnclaveInvalidReason(e.getMessage())); } + } - return privacySendTransaction.validateAndExecute( - requestContext, - privateTransaction, - sendTransactionResponse.getPrivacyGroupId(), - () -> - new JsonRpcSuccessResponse( - requestContext.getRequest().getId(), - Bytes.wrap(Base64.getDecoder().decode(sendTransactionResponse.getEnclaveKey())) - .toHexString())); + private String hexEncodeEnclaveKey(final String enclaveKey) { + return Bytes.wrap(Base64.getDecoder().decode(enclaveKey)).toHexString(); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcErrorResponse.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcErrorResponse.java index 3da3fcca1c5..9c50d7da4c0 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcErrorResponse.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcErrorResponse.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import com.google.common.base.MoreObjects; @JsonPropertyOrder({"jsonrpc", "id", "error"}) public class JsonRpcErrorResponse implements JsonRpcResponse { @@ -63,4 +64,9 @@ public boolean equals(final Object o) { public int hashCode() { return Objects.hash(id, error); } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this).add("id", id).add("error", error).toString(); + } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java index da85ca3a965..3e5b1a65e87 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/eea/EeaSendRawTransactionTest.java @@ -15,16 +15,20 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.eea; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.PRIVATE_TRANSACTION_FAILED; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import org.hyperledger.besu.crypto.SECP256K1; -import org.hyperledger.besu.enclave.EnclaveClientException; 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.privacy.methods.EnclavePublicKeyProvider; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; @@ -39,9 +43,11 @@ import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException; import org.hyperledger.besu.ethereum.privacy.PrivacyController; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; -import org.hyperledger.besu.ethereum.privacy.SendTransactionResponse; +import org.hyperledger.besu.ethereum.privacy.Restriction; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import java.math.BigInteger; +import java.util.List; import java.util.Optional; import io.vertx.core.json.JsonObject; @@ -57,35 +63,11 @@ @RunWith(MockitoJUnitRunner.class) public class EeaSendRawTransactionTest { - private static final String VALUE_NON_ZERO_TRANSACTION_RLP = - "0xf88b808203e8832dc6c0808203e880820fe8a08b89005561f31ce861" - + "84949bf32087a9817b337ab1d6027d58ef4e48aea88bafa041b93a" - + "e41a99fe662ad7fc1406ac90bf6bd498b5fe56fd6bfea15de15714" - + "438eac41316156744d784c4355486d425648586f5a7a7a42675062" - + "572f776a3561784470573958386c393153476f3dc08a7265737472" - + "6963746564"; - - private static final String VALID_PRIVATE_TRANSACTION_RLP = - "0xf8f3800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87" - + "808025a048b55bfa915ac795c431978d8a6a992b628d557da5ff" - + "759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56" - + "c0b28ad43601b4ab949f53faa07bd2c804ac41316156744d784c" - + "4355486d425648586f5a7a7a42675062572f776a356178447057" - + "3958386c393153476f3df85aac41316156744d784c4355486d42" - + "5648586f5a7a7a42675062572f776a3561784470573958386c39" - + "3153476f3dac4b6f32625671442b6e4e6c4e594c354545377933" - + "49644f6e766966746a69697a706a52742b4854754642733d8a72" - + "657374726963746564"; - + private static final String VALID_PRIVATE_TRANSACTION_RLP = validPrivateTransactionRlp(); private static final String VALID_PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP = - "0xf8ac800182520894095e7baea6a6c7c4c2dfeb977efac326af552d87" - + "80801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff" - + "759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56" - + "c0b28ad43601b4ab949f53faa07bd2c804a0035695b4cc4b0941" - + "e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486aa00f" - + "200e885ff29e973e2576b6600181d1b0a2b5294e30d9be4a1981" - + "ffb33a0b8c8a72657374726963746564"; + validPrivateTransactionRlpPrivacyGroup(); + // RLP encode fails creating a transaction without privateFrom so must be manually encoded private static final String PRIVATE_TRANSACTION_RLP_PRIVACY_GROUP_NO_PRIVATE_FROM = "0xf88b800182520894095e7baea6a6c7c4c2dfeb977efac326af55" + "2d8780801ba048b55bfa915ac795c431978d8a6a992b628d55" @@ -114,15 +96,12 @@ public class EeaSendRawTransactionTest { private static final String ENCLAVE_PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; private final String MOCK_ORION_KEY = ""; - private final String MOCK_PRIVACY_GROUP = ""; private final User user = new JWTUser(new JsonObject().put("privacyPublicKey", ENCLAVE_PUBLIC_KEY), ""); private final EnclavePublicKeyProvider enclavePublicKeyProvider = (user) -> ENCLAVE_PUBLIC_KEY; @Mock private TransactionPool transactionPool; - @Mock private EeaSendRawTransaction method; - @Mock private PrivacyController privacyController; @Before @@ -137,12 +116,9 @@ public void requestIsMissingParameter() { new JsonRpcRequestContext( new JsonRpcRequest("2.0", "eea_sendRawTransaction", new String[] {})); - final JsonRpcResponse expectedResponse = - new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS); - - final JsonRpcResponse actualResponse = method.response(request); - - assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); + assertThatThrownBy(() -> method.response(request)) + .isInstanceOf(InvalidJsonRpcParameters.class) + .hasMessage("Missing required json rpc parameter at index 0"); } @Test @@ -150,12 +126,9 @@ public void requestHasNullObjectParameter() { final JsonRpcRequestContext request = new JsonRpcRequestContext(new JsonRpcRequest("2.0", "eea_sendRawTransaction", null)); - final JsonRpcResponse expectedResponse = - new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS); - - final JsonRpcResponse actualResponse = method.response(request); - - assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); + assertThatThrownBy(() -> method.response(request)) + .isInstanceOf(InvalidJsonRpcParameters.class) + .hasMessage("Missing required json rpc parameter at index 0"); } @Test @@ -164,12 +137,9 @@ public void requestHasNullArrayParameter() { new JsonRpcRequestContext( new JsonRpcRequest("2.0", "eea_sendRawTransaction", new String[] {null})); - final JsonRpcResponse expectedResponse = - new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS); - - final JsonRpcResponse actualResponse = method.response(request); - - assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); + assertThatThrownBy(() -> method.response(request)) + .isInstanceOf(InvalidJsonRpcParameters.class) + .hasMessage("Missing required json rpc parameter at index 0"); } @Test @@ -188,27 +158,12 @@ public void invalidTransactionRlpDecoding() { assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); } - @Test - public void valueNonZeroTransaction() { - final JsonRpcRequestContext request = - new JsonRpcRequestContext( - new JsonRpcRequest( - "2.0", "eea_sendRawTransaction", new String[] {VALUE_NON_ZERO_TRANSACTION_RLP})); - - final JsonRpcResponse expectedResponse = - new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.VALUE_NOT_ZERO); - - final JsonRpcResponse actualResponse = method.response(request); - - assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); - } - @Test public void validTransactionIsSentToTransactionPool() { when(privacyController.sendTransaction(any(PrivateTransaction.class), any())) - .thenReturn(new SendTransactionResponse(MOCK_ORION_KEY, MOCK_PRIVACY_GROUP)); + .thenReturn(MOCK_ORION_KEY); when(privacyController.validatePrivateTransaction( - any(PrivateTransaction.class), any(String.class), any())) + any(PrivateTransaction.class), any(String.class))) .thenReturn(ValidationResult.valid()); when(privacyController.createPrivacyMarkerTransaction( any(String.class), any(PrivateTransaction.class))) @@ -232,8 +187,7 @@ public void validTransactionIsSentToTransactionPool() { verify(privacyController) .sendTransaction(any(PrivateTransaction.class), eq(ENCLAVE_PUBLIC_KEY)); verify(privacyController) - .validatePrivateTransaction( - any(PrivateTransaction.class), any(String.class), eq(ENCLAVE_PUBLIC_KEY)); + .validatePrivateTransaction(any(PrivateTransaction.class), eq(ENCLAVE_PUBLIC_KEY)); verify(privacyController) .createPrivacyMarkerTransaction(any(String.class), any(PrivateTransaction.class)); verify(transactionPool).addLocalTransaction(any(Transaction.class)); @@ -242,9 +196,8 @@ public void validTransactionIsSentToTransactionPool() { @Test public void validTransactionPrivacyGroupIsSentToTransactionPool() { when(privacyController.sendTransaction(any(PrivateTransaction.class), any())) - .thenReturn(new SendTransactionResponse(MOCK_ORION_KEY, MOCK_PRIVACY_GROUP)); - when(privacyController.validatePrivateTransaction( - any(PrivateTransaction.class), any(String.class), any())) + .thenReturn(MOCK_ORION_KEY); + when(privacyController.validatePrivateTransaction(any(PrivateTransaction.class), anyString())) .thenReturn(ValidationResult.valid()); when(privacyController.createPrivacyMarkerTransaction( any(String.class), any(PrivateTransaction.class))) @@ -269,7 +222,7 @@ public void validTransactionPrivacyGroupIsSentToTransactionPool() { assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); verify(privacyController).sendTransaction(any(PrivateTransaction.class), any()); verify(privacyController) - .validatePrivateTransaction(any(PrivateTransaction.class), any(String.class), any()); + .validatePrivateTransaction(any(PrivateTransaction.class), anyString()); verify(privacyController) .createPrivacyMarkerTransaction(any(String.class), any(PrivateTransaction.class)); verify(transactionPool).addLocalTransaction(any(Transaction.class)); @@ -294,9 +247,9 @@ public void invalidTransactionWithoutPrivateFromFieldFailsWithDecodeError() { } @Test - public void invalidTransactionIsNotSentToTransactionPool() { - when(privacyController.sendTransaction(any(PrivateTransaction.class), any())) - .thenThrow(new EnclaveClientException(400, "enclave failed to execute")); + public void invalidTransactionIsNotSentToEnclaveAndIsNotAddedToTransactionPool() { + when(privacyController.validatePrivateTransaction(any(PrivateTransaction.class), anyString())) + .thenReturn(ValidationResult.invalid(PRIVATE_TRANSACTION_FAILED)); final JsonRpcRequestContext request = new JsonRpcRequestContext( @@ -304,16 +257,19 @@ public void invalidTransactionIsNotSentToTransactionPool() { "2.0", "eea_sendRawTransaction", new String[] {VALID_PRIVATE_TRANSACTION_RLP})); final JsonRpcResponse expectedResponse = - new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.ENCLAVE_ERROR); + new JsonRpcErrorResponse(request.getRequest().getId(), JsonRpcError.INVALID_PARAMS); final JsonRpcResponse actualResponse = method.response(request); assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); + verify(privacyController, never()).sendTransaction(any(), any()); verifyNoInteractions(transactionPool); } @Test public void invalidTransactionFailingWithMultiTenancyValidationErrorReturnsUnauthorizedError() { + when(privacyController.validatePrivateTransaction(any(PrivateTransaction.class), anyString())) + .thenReturn(ValidationResult.valid()); when(privacyController.sendTransaction(any(PrivateTransaction.class), any())) .thenThrow(new MultiTenancyValidationException("validation failed")); @@ -379,9 +335,8 @@ private void verifyErrorForInvalidTransaction( final TransactionInvalidReason transactionInvalidReason, final JsonRpcError expectedError) { when(privacyController.sendTransaction(any(PrivateTransaction.class), any())) - .thenReturn(new SendTransactionResponse(MOCK_ORION_KEY, MOCK_PRIVACY_GROUP)); - when(privacyController.validatePrivateTransaction( - any(PrivateTransaction.class), any(String.class), any())) + .thenReturn(MOCK_ORION_KEY); + when(privacyController.validatePrivateTransaction(any(PrivateTransaction.class), anyString())) .thenReturn(ValidationResult.valid()); when(privacyController.createPrivacyMarkerTransaction( any(String.class), any(PrivateTransaction.class))) @@ -401,7 +356,7 @@ private void verifyErrorForInvalidTransaction( assertThat(actualResponse).isEqualToComparingFieldByField(expectedResponse); verify(privacyController).sendTransaction(any(PrivateTransaction.class), any()); verify(privacyController) - .validatePrivateTransaction(any(PrivateTransaction.class), any(String.class), any()); + .validatePrivateTransaction(any(PrivateTransaction.class), anyString()); verify(privacyController) .createPrivacyMarkerTransaction(any(String.class), any(PrivateTransaction.class)); verify(transactionPool).addLocalTransaction(any(Transaction.class)); @@ -411,4 +366,53 @@ private void verifyErrorForInvalidTransaction( public void getMethodReturnsExpectedName() { assertThat(method.getName()).matches("eea_sendRawTransaction"); } + + private static String validPrivateTransactionRlp() { + final PrivateTransaction.Builder privateTransactionBuilder = + PrivateTransaction.builder() + .nonce(0) + .gasPrice(Wei.of(1)) + .gasLimit(21000) + .value(Wei.ZERO) + .payload(Bytes.EMPTY) + .to(Address.fromHexString("0x095e7baea6a6c7c4c2dfeb977efac326af552d87")) + .chainId(BigInteger.ONE) + .privateFrom(Bytes.fromBase64String("S28yYlZxRCtuTmxOWUw1RUU3eTNJZE9udmlmdGppaXp=")) + .privateFor( + List.of( + Bytes.fromBase64String("S28yYlZxRCtuTmxOWUw1RUU3eTNJZE9udmlmdGppaXp="), + Bytes.fromBase64String("QTFhVnRNeExDVUhtQlZIWG9aenpCZ1BiVy93ajVheER="))) + .restriction(Restriction.RESTRICTED); + return rlpEncodeTransaction(privateTransactionBuilder); + } + + private static String validPrivateTransactionRlpPrivacyGroup() { + final PrivateTransaction.Builder privateTransactionBuilder = + PrivateTransaction.builder() + .nonce(0) + .gasPrice(Wei.of(1)) + .gasLimit(21000) + .value(Wei.ZERO) + .payload(Bytes.EMPTY) + .to(Address.fromHexString("0x095e7baea6a6c7c4c2dfeb977efac326af552d87")) + .chainId(BigInteger.ONE) + .privateFrom(Bytes.fromBase64String("S28yYlZxRCtuTmxOWUw1RUU3eTNJZE9udmlmdGppaXp=")) + .privacyGroupId(Bytes.fromBase64String("DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=")) + .restriction(Restriction.RESTRICTED); + return rlpEncodeTransaction(privateTransactionBuilder); + } + + private static String rlpEncodeTransaction( + final PrivateTransaction.Builder privateTransactionBuilder) { + final SECP256K1.KeyPair keyPair = + SECP256K1.KeyPair.create( + SECP256K1.PrivateKey.create( + new BigInteger( + "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); + + final PrivateTransaction privateTransaction = privateTransactionBuilder.signAndBuild(keyPair); + final BytesValueRLPOutput bvrlp = new BytesValueRLPOutput(); + privateTransaction.writeTo(bvrlp); + return bvrlp.encoded().toHexString(); + } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransactionTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransactionTest.java index 2c8be2c71d5..02100f8a065 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransactionTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivDistributeRawTransactionTest.java @@ -16,6 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,7 +32,6 @@ import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException; import org.hyperledger.besu.ethereum.privacy.PrivacyController; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; -import org.hyperledger.besu.ethereum.privacy.SendTransactionResponse; import java.util.Base64; @@ -74,9 +74,8 @@ public void before() { public void validTransactionHashReturnedAfterDistribute() { final String enclavePublicKey = "93Ky7lXwFkMc7+ckoFgUMku5bpr9tz4zhmWmk9RlNng="; when(privacyController.sendTransaction(any(PrivateTransaction.class), any())) - .thenReturn(new SendTransactionResponse(enclavePublicKey, "")); - when(privacyController.validatePrivateTransaction( - any(PrivateTransaction.class), any(String.class), any())) + .thenReturn(enclavePublicKey); + when(privacyController.validatePrivateTransaction(any(PrivateTransaction.class), anyString())) .thenReturn(ValidationResult.valid()); final JsonRpcRequestContext request = @@ -98,14 +97,15 @@ public void validTransactionHashReturnedAfterDistribute() { verify(privacyController) .sendTransaction(any(PrivateTransaction.class), eq(ENCLAVE_PUBLIC_KEY)); verify(privacyController) - .validatePrivateTransaction( - any(PrivateTransaction.class), any(String.class), eq(ENCLAVE_PUBLIC_KEY)); + .validatePrivateTransaction(any(PrivateTransaction.class), eq(ENCLAVE_PUBLIC_KEY)); } @Test public void invalidTransactionFailingWithMultiTenancyValidationErrorReturnsUnauthorizedError() { when(privacyController.sendTransaction(any(PrivateTransaction.class), any())) .thenThrow(new MultiTenancyValidationException("validation failed")); + when(privacyController.validatePrivateTransaction(any(PrivateTransaction.class), anyString())) + .thenReturn(ValidationResult.valid()); final JsonRpcRequestContext request = new JsonRpcRequestContext( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java index b235f81b146..2902cb2f069 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionValidator.java @@ -75,6 +75,8 @@ enum TransactionInvalidReason { PRIVATE_NONCE_TOO_LOW, PRIVACY_GROUP_DOES_NOT_EXIST, INCORRECT_PRIVATE_NONCE, - GAS_PRICE_TOO_LOW; + GAS_PRICE_TOO_LOW, + PRIVATE_VALUE_NOT_ZERO, + PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE; } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java index 77a369e5d51..09a402aacc7 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java @@ -83,21 +83,13 @@ public DefaultPrivacyController( } @Override - public SendTransactionResponse sendTransaction( + public String sendTransaction( final PrivateTransaction privateTransaction, final String enclavePublicKey) { try { LOG.trace("Storing private transaction in enclave"); final SendResponse sendResponse = sendRequest(privateTransaction, enclavePublicKey); - final String enclaveKey = sendResponse.getKey(); - if (privateTransaction.getPrivacyGroupId().isPresent()) { - final String privacyGroupId = privateTransaction.getPrivacyGroupId().get().toBase64String(); - return new SendTransactionResponse(enclaveKey, privacyGroupId); - } else { - final String privateFrom = privateTransaction.getPrivateFrom().toBase64String(); - final String privacyGroupId = getPrivacyGroupId(enclaveKey, privateFrom); - return new SendTransactionResponse(enclaveKey, privacyGroupId); - } - } catch (final Exception e) { + return sendResponse.getKey(); + } catch (Exception e) { LOG.error("Failed to store private transaction in enclave", e); throw e; } @@ -137,9 +129,8 @@ public Transaction createPrivacyMarkerTransaction( @Override public ValidationResult validatePrivateTransaction( - final PrivateTransaction privateTransaction, - final String privacyGroupId, - final String enclavePublicKey) { + final PrivateTransaction privateTransaction, final String enclavePublicKey) { + final String privacyGroupId = privateTransaction.determinePrivacyGroupId(); return privateTransactionValidator.validate( privateTransaction, determineBesuNonce(privateTransaction.getSender(), privacyGroupId, enclavePublicKey)); @@ -234,14 +225,4 @@ private SendResponse sendRequest( payload, privateTransaction.getPrivateFrom().toBase64String(), privateFor); } } - - private String getPrivacyGroupId(final String key, final String privateFrom) { - LOG.debug("Getting privacy group for key {} and privateFrom {}", key, privateFrom); - try { - return enclave.receive(key, privateFrom).getPrivacyGroupId(); - } catch (final RuntimeException e) { - LOG.error("Failed to retrieve private transaction in enclave", e); - throw e; - } - } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyController.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyController.java index c0c3df14ffa..78badbfe64f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyController.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyController.java @@ -38,7 +38,7 @@ public MultiTenancyPrivacyController( } @Override - public SendTransactionResponse sendTransaction( + public String sendTransaction( final PrivateTransaction privateTransaction, final String enclavePublicKey) { verifyPrivateFromMatchesEnclavePublicKey( privateTransaction.getPrivateFrom().toBase64String(), enclavePublicKey); @@ -92,11 +92,8 @@ public Transaction createPrivacyMarkerTransaction( @Override public ValidationResult validatePrivateTransaction( - final PrivateTransaction privateTransaction, - final String privacyGroupId, - final String enclavePublicKey) { - return privacyController.validatePrivateTransaction( - privateTransaction, privacyGroupId, enclavePublicKey); + final PrivateTransaction privateTransaction, final String enclavePublicKey) { + return privacyController.validatePrivateTransaction(privateTransaction, enclavePublicKey); } @Override diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyController.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyController.java index 09577da143f..1c92609acfb 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyController.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyController.java @@ -27,8 +27,7 @@ public interface PrivacyController { - SendTransactionResponse sendTransaction( - PrivateTransaction privateTransaction, String enclavePublicKey); + String sendTransaction(PrivateTransaction privateTransaction, String enclavePublicKey); ReceiveResponse retrieveTransaction(String enclaveKey, String enclavePublicKey); @@ -43,7 +42,7 @@ Transaction createPrivacyMarkerTransaction( String transactionEnclaveKey, PrivateTransaction privateTransaction); ValidationResult validatePrivateTransaction( - PrivateTransaction privateTransaction, String privacyGroupId, String enclavePublicKey); + PrivateTransaction privateTransaction, String enclavePublicKey); long determineEeaNonce( String privateFrom, String[] privateFor, Address address, String enclavePublicKey); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyGroupUtil.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyGroupUtil.java new file mode 100644 index 00000000000..c693986e424 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivacyGroupUtil.java @@ -0,0 +1,50 @@ +/* + * 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.privacy; + +import org.hyperledger.besu.crypto.Hash; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.tuweni.bytes.Bytes; + +public class PrivacyGroupUtil { + + public static String calculateEeaPrivacyGroupId( + final Bytes privateFrom, final List privateFor) { + final List privacyGroupIds = new ArrayList<>(); + privacyGroupIds.add(privateFrom); + privacyGroupIds.addAll(privateFor); + + final List sortedPrivacyGroupIds = + privacyGroupIds.stream() + .distinct() + .map(Bytes::toArray) + .sorted(Comparator.comparing(Arrays::hashCode)) + .collect(Collectors.toList()); + + final BytesValueRLPOutput bytesValueRLPOutput = new BytesValueRLPOutput(); + bytesValueRLPOutput.writeList( + sortedPrivacyGroupIds, + (privacyGroupId, rlpOutput) -> rlpOutput.writeBytes(Bytes.of(privacyGroupId))); + + return Hash.keccak256(bytesValueRLPOutput.encoded()).toBase64String(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransaction.java index a07bcc94224..e488bdcea82 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransaction.java @@ -32,6 +32,7 @@ import java.util.Objects; import java.util.Optional; +import com.google.common.collect.Lists; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; @@ -454,6 +455,21 @@ public Wei getUpfrontCost() { return getUpfrontGasCost().add(getValue()); } + /** + * Determines the privacy group id. Either returning the value of privacyGroupId field if it + * exists or calculating the EEA privacyGroupId from the privateFrom and privateFor fields. + * + * @return the privacyGroupId + */ + public String determinePrivacyGroupId() { + if (getPrivacyGroupId().isPresent()) { + return getPrivacyGroupId().get().toBase64String(); + } else { + final List privateFor = getPrivateFor().orElse(Lists.newArrayList()); + return PrivacyGroupUtil.calculateEeaPrivacyGroupId(getPrivateFrom(), privateFor); + } + } + private static Bytes32 computeSenderRecoveryHash( final long nonce, final Wei gasPrice, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidator.java index 4b92ce74211..c1a42e911a8 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidator.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.privacy; import org.hyperledger.besu.ethereum.mainnet.TransactionValidator; +import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.math.BigInteger; @@ -35,6 +36,16 @@ public PrivateTransactionValidator(final Optional chainId) { public ValidationResult validate( final PrivateTransaction transaction, final Long accountNonce) { + LOG.debug("Validating private transaction fields of {}", transaction.hash()); + final ValidationResult privateFieldsValidationResult = + validatePrivateTransactionFields(transaction); + if (!privateFieldsValidationResult.isValid()) { + LOG.debug( + "Private Transaction fields are invalid {}, {}", + transaction.hash(), + privateFieldsValidationResult.getErrorMessage()); + return privateFieldsValidationResult; + } LOG.debug("Validating the signature of Private Transaction {} ", transaction.hash()); @@ -76,6 +87,20 @@ public ValidationResult validate( return ValidationResult.valid(); } + private ValidationResult + validatePrivateTransactionFields(final PrivateTransaction privateTransaction) { + if (!privateTransaction.getValue().isZero()) { + return ValidationResult.invalid( + TransactionValidator.TransactionInvalidReason.PRIVATE_VALUE_NOT_ZERO); + } + if (!privateTransaction.getRestriction().equals(Restriction.RESTRICTED)) { + return ValidationResult.invalid( + TransactionValidator.TransactionInvalidReason.PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE); + } + + return ValidationResult.valid(); + } + private ValidationResult validateTransactionSignature(final PrivateTransaction transaction) { if (chainId.isPresent() @@ -87,7 +112,7 @@ public ValidationResult validate( transaction.getChainId().get(), chainId.get())); } - if (!chainId.isPresent() && transaction.getChainId().isPresent()) { + if (chainId.isEmpty() && transaction.getChainId().isPresent()) { return ValidationResult.invalid( TransactionValidator.TransactionInvalidReason.REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED, "Replay protection (chainId) is not supported"); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/SendTransactionResponse.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/SendTransactionResponse.java deleted file mode 100644 index cb78955996e..00000000000 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/SendTransactionResponse.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * 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.privacy; - -public class SendTransactionResponse { - private final String enclaveKey; - private final String privacyGroupId; - - public SendTransactionResponse(final String enclaveKey, final String privacyGroupId) { - this.enclaveKey = enclaveKey; - this.privacyGroupId = privacyGroupId; - } - - public String getEnclaveKey() { - return enclaveKey; - } - - public String getPrivacyGroupId() { - return privacyGroupId; - } -} diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/PrivateTransactionTestFixture.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/PrivateTransactionTestFixture.java index 3ab3f288fa9..66fa2c522f4 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/PrivateTransactionTestFixture.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/PrivateTransactionTestFixture.java @@ -36,7 +36,7 @@ public class PrivateTransactionTestFixture { private Optional
to = Optional.empty(); private Address sender = Address.fromHexString(String.format("%020x", 1)); - private Wei value = Wei.of(4); + private Wei value = Wei.of(0); private Bytes payload = Bytes.EMPTY; @@ -52,7 +52,7 @@ public class PrivateTransactionTestFixture { private Optional privacyGroupId = Optional.empty(); - private final Restriction restriction = Restriction.RESTRICTED; + private Restriction restriction = Restriction.RESTRICTED; public PrivateTransaction createTransaction(final KeyPair keys) { final PrivateTransaction.Builder builder = PrivateTransaction.builder(); @@ -137,4 +137,9 @@ public PrivateTransactionTestFixture privacyGroupId(final Bytes privacyGroupId) this.privacyGroupId = Optional.of(privacyGroupId); return this; } + + public PrivateTransactionTestFixture restriction(final Restriction restriction) { + this.restriction = restriction; + return this; + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyControllerTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyControllerTest.java index e7049ddeaf2..6c3a7e54aae 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyControllerTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyControllerTest.java @@ -86,7 +86,6 @@ public class DefaultPrivacyControllerTest { private PrivacyController privacyController; private PrivacyController brokenPrivacyController; private PrivateTransactionValidator privateTransactionValidator; - private PrivateTransactionSimulator privateTransactionSimulator; private Enclave enclave; private Account account; private String enclavePublicKey; @@ -154,7 +153,8 @@ public void setUp() throws Exception { enclavePublicKey = OrionKeyUtils.loadKey("orion_key_0.pub"); privateTransactionValidator = mockPrivateTransactionValidator(); enclave = mockEnclave(); - privateTransactionSimulator = mockPrivateTransactionSimulator(); + final PrivateTransactionSimulator privateTransactionSimulator = + mockPrivateTransactionSimulator(); privacyController = new DefaultPrivacyController( @@ -181,16 +181,13 @@ public void sendsValidLegacyTransaction() { final PrivateTransaction transaction = buildLegacyPrivateTransaction(1); - final SendTransactionResponse sendTransactionResponse = - privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY); + final String enclaveKey = privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY); final ValidationResult validationResult = - privacyController.validatePrivateTransaction( - transaction, sendTransactionResponse.getPrivacyGroupId(), ENCLAVE_PUBLIC_KEY); + privacyController.validatePrivateTransaction(transaction, ENCLAVE_PUBLIC_KEY); final Transaction markerTransaction = - privacyController.createPrivacyMarkerTransaction( - sendTransactionResponse.getEnclaveKey(), transaction); + privacyController.createPrivacyMarkerTransaction(enclaveKey, transaction); assertThat(validationResult).isEqualTo(ValidationResult.valid()); assertThat(markerTransaction.contractAddress()).isEqualTo(PUBLIC_TRANSACTION.contractAddress()); @@ -207,16 +204,13 @@ public void sendValidBesuTransaction() { final PrivateTransaction transaction = buildBesuPrivateTransaction(1); - final SendTransactionResponse sendTransactionResponse = - privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY); + final String enclaveKey = privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY); final ValidationResult validationResult = - privacyController.validatePrivateTransaction( - transaction, transaction.getPrivacyGroupId().get().toString(), ENCLAVE_PUBLIC_KEY); + privacyController.validatePrivateTransaction(transaction, ENCLAVE_PUBLIC_KEY); final Transaction markerTransaction = - privacyController.createPrivacyMarkerTransaction( - sendTransactionResponse.getEnclaveKey(), transaction); + privacyController.createPrivacyMarkerTransaction(enclaveKey, transaction); assertThat(validationResult).isEqualTo(ValidationResult.valid()); assertThat(markerTransaction.contractAddress()).isEqualTo(PUBLIC_TRANSACTION.contractAddress()); @@ -242,11 +236,8 @@ public void validateTransactionWithTooLowNonceReturnsError() { .thenReturn(ValidationResult.invalid(PRIVATE_NONCE_TOO_LOW)); final PrivateTransaction transaction = buildLegacyPrivateTransaction(0); - final SendTransactionResponse sendTransactionResponse = - privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY); final ValidationResult validationResult = - privacyController.validatePrivateTransaction( - transaction, sendTransactionResponse.getPrivacyGroupId(), ENCLAVE_PUBLIC_KEY); + privacyController.validatePrivateTransaction(transaction, ENCLAVE_PUBLIC_KEY); assertThat(validationResult).isEqualTo(ValidationResult.invalid(PRIVATE_NONCE_TOO_LOW)); } @@ -257,11 +248,8 @@ public void validateTransactionWithIncorrectNonceReturnsError() { final PrivateTransaction transaction = buildLegacyPrivateTransaction(2); - final SendTransactionResponse sendTransactionResponse = - privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY); final ValidationResult validationResult = - privacyController.validatePrivateTransaction( - transaction, sendTransactionResponse.getPrivacyGroupId(), ENCLAVE_PUBLIC_KEY); + privacyController.validatePrivateTransaction(transaction, ENCLAVE_PUBLIC_KEY); assertThat(validationResult).isEqualTo(ValidationResult.invalid(INCORRECT_PRIVATE_NONCE)); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyControllerTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyControllerTest.java index 2e797beee2c..e5469eb6883 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyControllerTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/MultiTenancyPrivacyControllerTest.java @@ -70,12 +70,11 @@ public void setup() { .build(); when(privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1)) - .thenReturn(new SendTransactionResponse(ENCLAVE_KEY, PRIVACY_GROUP_ID)); + .thenReturn(ENCLAVE_KEY); - final SendTransactionResponse response = + final String enclaveKey = multiTenancyPrivacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); - assertThat(response.getEnclaveKey()).isEqualTo(ENCLAVE_KEY); - assertThat(response.getPrivacyGroupId()).isEqualTo(PRIVACY_GROUP_ID); + assertThat(enclaveKey).isEqualTo(ENCLAVE_KEY); verify(privacyController).sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); } @@ -89,7 +88,7 @@ public void setup() { .build(); when(privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1)) - .thenReturn(new SendTransactionResponse(ENCLAVE_KEY, PRIVACY_GROUP_ID)); + .thenReturn(ENCLAVE_KEY); final PrivacyGroup privacyGroupWithEnclavePublicKey = new PrivacyGroup( PRIVACY_GROUP_ID, @@ -100,10 +99,9 @@ public void setup() { when(enclave.retrievePrivacyGroup(PRIVACY_GROUP_ID)) .thenReturn(privacyGroupWithEnclavePublicKey); - final SendTransactionResponse response = + final String response = multiTenancyPrivacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); - assertThat(response.getEnclaveKey()).isEqualTo(ENCLAVE_KEY); - assertThat(response.getPrivacyGroupId()).isEqualTo(PRIVACY_GROUP_ID); + assertThat(response).isEqualTo(ENCLAVE_KEY); verify(privacyController).sendTransaction(transaction, ENCLAVE_PUBLIC_KEY1); verify(enclave).retrievePrivacyGroup(PRIVACY_GROUP_ID); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivacyGroupUtilTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivacyGroupUtilTest.java new file mode 100644 index 00000000000..9d1fad56fbf --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivacyGroupUtilTest.java @@ -0,0 +1,84 @@ +/* + * 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.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.Test; + +public class PrivacyGroupUtilTest { + + private static final String ENCLAVE_PUBLIC_KEY_1 = "negmDcN2P4ODpqn/6WkJ02zT/0w0bjhGpkZ8UP6vARk="; + private static final String ENCLAVE_PUBLIC_KEY_2 = "g59BmTeJIn7HIcnq8VQWgyh/pDbvbt2eyP0Ii60aDDw="; + private static final String ENCLAVE_PUBLIC_KEY_3 = "6fg8q5rWMBoAT2oIiU3tYJbk4b7oAr7dxaaVY7TeM3U="; + + // This is the expected generated legacy privacy group id for a privacy group containing the + // enclave public keys enclave_public_key_1, enclave_public_key_2 and enclave_public_key_3 + private static final String EXPECTED_LEGACY_PRIVACY_GROUP_ID = + "/xzRjCLioUBkm5LYuzll61GXyrD5x7bvXzQk/ovJA/4="; + + @Test + public void calculatesPrivacyGroupIdWithPrivateFromAndEmptyPrivateFor() { + final String expected = "kAbelwaVW7okoEn1+okO+AbA4Hhz/7DaCOWVQz9nx5M="; + assertThat(privacyGroupId(ENCLAVE_PUBLIC_KEY_1)).isEqualTo(expected); + } + + @Test + public void calculatesSamePrivacyGroupIdForDuplicateValues() { + assertThat( + privacyGroupId( + ENCLAVE_PUBLIC_KEY_2, + ENCLAVE_PUBLIC_KEY_1, + ENCLAVE_PUBLIC_KEY_3, + ENCLAVE_PUBLIC_KEY_1)) + .isEqualTo(EXPECTED_LEGACY_PRIVACY_GROUP_ID); + assertThat( + privacyGroupId( + ENCLAVE_PUBLIC_KEY_2, + ENCLAVE_PUBLIC_KEY_1, + ENCLAVE_PUBLIC_KEY_1, + ENCLAVE_PUBLIC_KEY_3)) + .isEqualTo(EXPECTED_LEGACY_PRIVACY_GROUP_ID); + assertThat(privacyGroupId(ENCLAVE_PUBLIC_KEY_2, ENCLAVE_PUBLIC_KEY_1, ENCLAVE_PUBLIC_KEY_3)) + .isEqualTo(EXPECTED_LEGACY_PRIVACY_GROUP_ID); + } + + @Test + public void calculatesSamePrivacyGroupIdForPrivateForInDifferentOrders() { + final String expectedPrivacyGroupId = "/xzRjCLioUBkm5LYuzll61GXyrD5x7bvXzQk/ovJA/4="; + assertThat(privacyGroupId(ENCLAVE_PUBLIC_KEY_1, ENCLAVE_PUBLIC_KEY_2, ENCLAVE_PUBLIC_KEY_3)) + .isEqualTo(expectedPrivacyGroupId); + assertThat(privacyGroupId(ENCLAVE_PUBLIC_KEY_1, ENCLAVE_PUBLIC_KEY_3, ENCLAVE_PUBLIC_KEY_2)) + .isEqualTo(expectedPrivacyGroupId); + assertThat(privacyGroupId(ENCLAVE_PUBLIC_KEY_2, ENCLAVE_PUBLIC_KEY_1, ENCLAVE_PUBLIC_KEY_3)) + .isEqualTo(expectedPrivacyGroupId); + assertThat(privacyGroupId(ENCLAVE_PUBLIC_KEY_2, ENCLAVE_PUBLIC_KEY_3, ENCLAVE_PUBLIC_KEY_1)) + .isEqualTo(expectedPrivacyGroupId); + assertThat(privacyGroupId(ENCLAVE_PUBLIC_KEY_3, ENCLAVE_PUBLIC_KEY_1, ENCLAVE_PUBLIC_KEY_2)) + .isEqualTo(expectedPrivacyGroupId); + assertThat(privacyGroupId(ENCLAVE_PUBLIC_KEY_3, ENCLAVE_PUBLIC_KEY_2, ENCLAVE_PUBLIC_KEY_1)) + .isEqualTo(expectedPrivacyGroupId); + } + + private String privacyGroupId(final String privateFrom, final String... privateFor) { + return PrivacyGroupUtil.calculateEeaPrivacyGroupId( + Bytes.fromBase64String(privateFrom), + Arrays.stream(privateFor).map(Bytes::fromBase64String).collect(Collectors.toList())); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidatorTest.java index 92244c70c1c..a12e0a5861b 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidatorTest.java @@ -18,12 +18,15 @@ import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.INCORRECT_PRIVATE_NONCE; import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.INVALID_SIGNATURE; import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.PRIVATE_NONCE_TOO_LOW; +import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE; +import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.PRIVATE_VALUE_NOT_ZERO; import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.REPLAY_PROTECTED_SIGNATURES_NOT_SUPPORTED; import static org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason.WRONG_CHAIN_ID; import static org.mockito.Mockito.when; import org.hyperledger.besu.crypto.SECP256K1.KeyPair; import org.hyperledger.besu.ethereum.core.PrivateTransactionTestFixture; +import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; @@ -103,18 +106,60 @@ public void transactionWithInvalidSignatureShouldReturnInvalidSignature() { assertThat(validationResult).isEqualTo(ValidationResult.invalid(INVALID_SIGNATURE)); } + @Test + public void transactionWithNonZeroValueShouldReturnValueNotZeroError() { + validator = new PrivateTransactionValidator(Optional.of(BigInteger.ONE)); + + ValidationResult validationResult = + validator.validate(privateTransactionWithValue(1L), 0L); + + assertThat(validationResult).isEqualTo(ValidationResult.invalid(PRIVATE_VALUE_NOT_ZERO)); + } + + @Test + public void + transactionWithUnrestrictedTransactionTypeShouldReturnUnimplementedTransactionTypeError() { + validator = new PrivateTransactionValidator(Optional.of(BigInteger.ONE)); + + ValidationResult validationResult = + validator.validate(privateTransactionWithRestriction(Restriction.UNRESTRICTED), 0L); + + assertThat(validationResult) + .isEqualTo(ValidationResult.invalid(PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE)); + } + + @Test + public void + transactionWithUnsupportedTransactionTypeShouldReturnUnimplementedTransactionTypeError() { + validator = new PrivateTransactionValidator(Optional.of(BigInteger.ONE)); + + ValidationResult validationResult = + validator.validate(privateTransactionWithRestriction(Restriction.UNSUPPORTED), 0L); + + assertThat(validationResult) + .isEqualTo(ValidationResult.invalid(PRIVATE_UNIMPLEMENTED_TRANSACTION_TYPE)); + } + private PrivateTransaction privateTransactionWithNonce(final long nonce) { - PrivateTransactionTestFixture privateTransactionTestFixture = - new PrivateTransactionTestFixture(); - privateTransactionTestFixture.nonce(nonce); - privateTransactionTestFixture.chainId(Optional.empty()); - return privateTransactionTestFixture.createTransaction(senderKeys); + return new PrivateTransactionTestFixture() + .nonce(nonce) + .chainId(Optional.empty()) + .createTransaction(senderKeys); } private PrivateTransaction privateTransactionWithChainId(final int chainId) { - PrivateTransactionTestFixture privateTransactionTestFixture = - new PrivateTransactionTestFixture(); - privateTransactionTestFixture.chainId(Optional.of(BigInteger.valueOf(chainId))); - return privateTransactionTestFixture.createTransaction(senderKeys); + return new PrivateTransactionTestFixture() + .chainId(Optional.of(BigInteger.valueOf(chainId))) + .createTransaction(senderKeys); + } + + private PrivateTransaction privateTransactionWithValue(final long value) { + return new PrivateTransactionTestFixture().value(Wei.of(value)).createTransaction(senderKeys); + } + + private PrivateTransaction privateTransactionWithRestriction(final Restriction restriction) { + return new PrivateTransactionTestFixture() + .restriction(restriction) + .createTransaction(senderKeys); } } From 358ab092b46acc6469a9553e90f84946fefe876f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 10 Feb 2020 12:15:24 -0700 Subject: [PATCH 04/38] Trace API fixes (#377) - Correct Reporting of reverts in nested call - correct reporting and handling of value transfer in nested calls - correct handling of precompiles via DELEGATECALL & CALLCODE - Addition of precompiled contract gas costs - Re-work handling of storage writes - Initial handling of gas refunds - fix bug in DELEGATECALL tests, we don't need gas in the stack * this has a cascading effect on balances in diff tests - rework depth detection in flat trace - two new tests blocks Signed-off-by: Danno Ferrin --- .../internal/processor/TransactionTrace.java | 4 + .../tracing/flat/FlatTraceGenerator.java | 24 +- .../results/tracing/vm/VmOperation.java | 15 +- .../results/tracing/vm/VmTraceGenerator.java | 158 +- .../methods/DebugTraceBlockByHashTest.java | 6 +- .../methods/DebugTraceBlockByNumberTest.java | 6 +- .../internal/methods/DebugTraceBlockTest.java | 6 +- .../methods/DebugTraceTransactionTest.java | 11 +- .../api/jsonrpc/trace/chain-data/blocks.json | 76 +- .../api/jsonrpc/trace/chain-data/genesis.json | 4 +- ...race_replayBlockTransactions_all_0x10.json | 24 +- ...race_replayBlockTransactions_all_0x11.json | 8 +- ...race_replayBlockTransactions_all_0x12.json | 16 +- ...race_replayBlockTransactions_all_0x13.json | 24 +- ...race_replayBlockTransactions_all_0x14.json | 16 +- ...race_replayBlockTransactions_all_0x15.json | 1870 +++++++++++++++++ ...race_replayBlockTransactions_all_0x16.json | 1818 ++++++++++++++++ ...trace_replayBlockTransactions_all_0xB.json | 92 +- ...trace_replayBlockTransactions_all_0xC.json | 8 +- ...trace_replayBlockTransactions_all_0xD.json | 8 +- ...trace_replayBlockTransactions_all_0xE.json | 24 +- ...trace_replayBlockTransactions_all_0xF.json | 16 +- ...replayBlockTransactions_traceOnly_0xB.json | 12 +- ..._replayBlockTransactions_diffOnly_0xB.json | 6 +- ..._replayBlockTransactions_diffOnly_0xC.json | 8 +- ..._replayBlockTransactions_diffOnly_0xD.json | 8 +- ..._replayBlockTransactions_diffOnly_0xE.json | 24 +- .../trace_replayBlockTransactions_0xB.json | 78 +- .../besu/ethereum/debug/TraceFrame.java | 65 +- .../mainnet/AbstractMessageProcessor.java | 6 +- .../MainnetContractCreationProcessor.java | 3 +- .../mainnet/MainnetMessageCallProcessor.java | 11 +- .../ethereum/vm/DebugOperationTracer.java | 19 +- .../besu/ethereum/vm/MessageFrame.java | 19 +- .../besu/ethereum/vm/OperationTracer.java | 5 + .../vm/operations/SStoreOperation.java | 1 + 36 files changed, 4132 insertions(+), 367 deletions(-) create mode 100644 ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x15.json create mode 100644 ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x16.json diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java index c52ad659f35..94c1eb13fc0 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTrace.java @@ -41,6 +41,10 @@ public long getGas() { return transaction.getGasLimit() - result.getGasRemaining(); } + public long getGasLimit() { + return transaction.getGasLimit(); + } + public Result getResult() { return result; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java index 30d2b577d41..257fca9411d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java @@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.Trace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TracingUtils; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace.Context; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.Transaction; @@ -100,7 +101,9 @@ public static Stream generateFromTransactionTrace( int traceFrameIndex = 0; final List traceFrames = transactionTrace.getTraceFrames(); for (final TraceFrame traceFrame : traceFrames) { - cumulativeGasCost += traceFrame.getGasCost().orElse(Gas.ZERO).toLong(); + cumulativeGasCost += + traceFrame.getGasCost().orElse(Gas.ZERO).toLong() + + traceFrame.getPrecompiledGasCost().orElse(Gas.ZERO).toLong(); final String opcodeString = traceFrame.getOpcode(); if ("CALL".equals(opcodeString) || "CALLCODE".equals(opcodeString) @@ -129,7 +132,6 @@ public static Stream generateFromTransactionTrace( } else if ("CREATE".equals(opcodeString) || "CREATE2".equals(opcodeString)) { currentContext = handleCreateOperation( - transactionTrace, smartContractAddress, flatTraces, tracesContexts, @@ -137,7 +139,7 @@ public static Stream generateFromTransactionTrace( traceFrameIndex, traceFrames); } else if ("REVERT".equals(opcodeString)) { - currentContext.getBuilder().error(Optional.of("Reverted")); + currentContext = handleRevert(tracesContexts, currentContext); } else if (!traceFrame.getExceptionalHaltReasons().isEmpty()) { currentContext .getBuilder() @@ -148,6 +150,7 @@ public static Stream generateFromTransactionTrace( } traceFrameIndex++; } + return flatTraces.stream().map(FlatTrace.Builder::build); } @@ -207,6 +210,7 @@ private static FlatTrace.Context handleReturn( final FlatTrace.Builder traceFrameBuilder = currentContext.getBuilder(); final Result.Builder resultBuilder = traceFrameBuilder.getResultBuilder(); final Action.Builder actionBuilder = traceFrameBuilder.getActionBuilder(); + actionBuilder.value(Quantity.create(traceFrame.getValue())); final Bytes outputData = traceFrame.getOutputData(); if (resultBuilder.getCode() == null) { resultBuilder.output(outputData.toHexString()); @@ -267,7 +271,6 @@ private static FlatTrace.Context handleSelfDestruct( } private static FlatTrace.Context handleCreateOperation( - final TransactionTrace transactionTrace, final Optional smartContractAddress, final List flatTraces, final Deque tracesContexts, @@ -287,7 +290,7 @@ private static FlatTrace.Context handleCreateOperation( Action.builder() .from(smartContractAddress.orElse(callingAddress)) .gas(nextTraceFrame.getGasRemaining().toHexString()) - .value(Quantity.create(transactionTrace.getTransaction().getValue())); + .value(Quantity.create(nextTraceFrame.getValue())); final FlatTrace.Context currentContext = new FlatTrace.Context(subTraceBuilder.actionBuilder(subTraceActionBuilder)); @@ -298,6 +301,17 @@ private static FlatTrace.Context handleCreateOperation( return currentContext; } + private static Context handleRevert( + final Deque tracesContexts, final FlatTrace.Context currentContext) { + currentContext.getBuilder().error(Optional.of("Reverted")); + tracesContexts.removeLast(); + final FlatTrace.Context nextContext = tracesContexts.peekLast(); + if (nextContext != null) { + nextContext.getBuilder().incSubTraces(); + } + return nextContext; + } + private static String calculateCallingAddress(final FlatTrace.Context lastContext) { if (lastContext.getBuilder().getActionBuilder().getCallType() == null) { return ZERO_ADDRESS_STRING; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmOperation.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmOperation.java index b0702a535b1..7c6ff24629d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmOperation.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmOperation.java @@ -14,14 +14,18 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.vm; +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; + import java.util.Objects; import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonPropertyOrder; -@JsonPropertyOrder({"cost", "ex", "pc", "sub"}) +@JsonPropertyOrder({"cost", "operation", "ex", "pc", "sub"}) public class VmOperation { private long cost; + private String operation; // Information concerning the execution of the operation. private VmOperationExecutionReport vmOperationExecutionReport; private long pc; @@ -62,6 +66,15 @@ public void setSub(final VmTrace sub) { this.sub = sub; } + @JsonInclude(NON_NULL) + public String getOperation() { + return operation; + } + + public void setOperation(final String operation) { + this.operation = operation; + } + @Override public int hashCode() { return Objects.hash(cost, vmOperationExecutionReport, pc, sub); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java index 9209265c8c2..6c8a2db6e80 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java @@ -24,14 +24,10 @@ import java.util.ArrayDeque; import java.util.Deque; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; import java.util.stream.IntStream; import java.util.stream.Stream; -import com.google.common.collect.MapDifference; -import com.google.common.collect.Maps; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -44,6 +40,7 @@ public class VmTraceGenerator { private final TransactionTrace transactionTrace; private final VmTrace rootVmTrace = new VmTrace(); private final Deque parentTraces = new ArrayDeque<>(); + int lastDepth = 0; public VmTraceGenerator(final TransactionTrace transactionTrace) { this.transactionTrace = transactionTrace; @@ -82,18 +79,18 @@ private Trace generateTrace() { * @param frame the current trace frame */ private void addFrame(final TraceFrame frame) { - if (mustIgnore(frame)) { - return; + handleDepthDecreased(frame); + if (!mustIgnore(frame)) { + initStep(frame); + final VmOperation op = buildVmOperation(); + final VmOperationExecutionReport report = generateExecutionReport(); + generateTracingMemory(report); + generateTracingPush(report); + generateTracingStorage(report); + handleDepthIncreased(op, report); + completeStep(op, report); + lastDepth = frame.getDepth(); } - initStep(frame); - final VmOperation op = buildVmOperation(); - final VmOperationExecutionReport report = generateExecutionReport(); - generateTracingMemory(report); - generateTracingPush(report); - generateTracingStorage(report); - handleDepthIncreased(op, report); - handleDepthDecreased(); - completeStep(op, report); } private boolean mustIgnore(final TraceFrame frame) { @@ -112,53 +109,60 @@ private boolean mustIgnore(final TraceFrame frame) { private void completeStep(final VmOperation op, final VmOperationExecutionReport report) { // add the operation representation to the list of traces op.setVmOperationExecutionReport(report); - currentTrace.add(op); + if (currentTrace != null) { + currentTrace.add(op); + } currentIndex++; } private void handleDepthIncreased(final VmOperation op, final VmOperationExecutionReport report) { // check if next frame depth has increased i.e the current operation is a call - if (currentTraceFrame.depthHasIncreased() - || "STATICCALL".equals(currentOperation) - || "CALL".equals(currentOperation)) { - findLastFrameInCall(currentTraceFrame, currentIndex) - .ifPresent( - lastFrameInCall -> { - report.setUsed(lastFrameInCall.getGasRemaining().toLong()); - lastFrameInCall - .getStack() - .filter(stack -> stack.length > 0) - .map(stack -> stack[stack.length - 1]) - .map(last -> Quantity.create(UInt256.fromHexString(last.toHexString()))) - .ifPresent(report::singlePush); - switch (currentTraceFrame.getOpcode()) { - case "DELEGATECALL": - case "CREATE": - case "CREATE2": - break; - default: + switch (currentOperation) { + case "STATICCALL": + case "DELEGATECALL": + case "CALLCODE": + case "CALL": + case "CREATE": + case "CREATE2": + findLastFrameInCall(currentTraceFrame, currentIndex) + .ifPresent( + lastFrameInCall -> { + report.setUsed(lastFrameInCall.getGasRemaining().toLong()); + lastFrameInCall + .getStack() + .filter(stack -> stack.length > 0) + .map(stack -> stack[stack.length - 1]) + .map(last -> Quantity.create(UInt256.fromHexString(last.toHexString()))) + .ifPresent(report::singlePush); + if (!currentOperation.startsWith("CREATE")) { lastFrameInCall .getMaybeUpdatedMemory() .map( mem -> new Mem(mem.getValue().toHexString(), mem.getOffset().intValue())) .ifPresent(report::setMem); - } - }); - if (currentTraceFrame.depthHasIncreased()) { - op.setCost(currentTraceFrame.getGasRemainingPostExecution().toLong() + op.getCost()); - final VmTrace newSubTrace = new VmTrace(); - parentTraces.addLast(newSubTrace); - op.setSub(newSubTrace); - } else { - op.setSub(new VmTrace()); - } + } + }); + if (currentTraceFrame.getMaybeCode().map(Code::getSize).orElse(0) > 0) { + op.setCost(currentTraceFrame.getGasRemainingPostExecution().toLong() + op.getCost()); + final VmTrace newSubTrace = new VmTrace(); + parentTraces.addLast(newSubTrace); + op.setSub(newSubTrace); + } else { + if (currentTraceFrame.getPrecompiledGasCost().isPresent()) { + op.setCost(op.getCost() + currentTraceFrame.getPrecompiledGasCost().get().toLong()); + } + op.setSub(new VmTrace()); + } + break; + default: + break; } } - private void handleDepthDecreased() { + private void handleDepthDecreased(final TraceFrame frame) { // check if next frame depth has decreased i.e the current operation closes the parent trace - if (currentTraceFrame.depthHasDecreased()) { + if (currentTraceFrame != null && frame.getDepth() < lastDepth) { currentTrace = parentTraces.removeLast(); } } @@ -168,6 +172,7 @@ private VmOperation buildVmOperation() { // set gas cost and program counter op.setCost(currentTraceFrame.getGasCost().orElse(Gas.ZERO).toLong()); op.setPc(currentTraceFrame.getPc()); + // op.setOperation(currentOperation); return op; } @@ -220,12 +225,14 @@ private void generateTracingPush(final VmOperationExecutionReport report) { private void generateTracingStorage(final VmOperationExecutionReport report) { // set storage if updated - updatedStorage(currentTraceFrame.getStoragePreExecution(), currentTraceFrame.getStorage()) - .map( - storageEntry -> - new Store( - storageEntry.key.toShortHexString(), storageEntry.value.toShortHexString())) - .ifPresent(report::setStore); + currentTraceFrame + .getMaybeUpdatedStorage() + .ifPresent( + entry -> + report.setStore( + new Store( + entry.getOffset().toShortHexString(), + entry.getValue().toShortHexString()))); } /** @@ -236,39 +243,12 @@ private void generateTracingStorage(final VmOperationExecutionReport report) { private void initStep(final TraceFrame frame) { this.currentTraceFrame = frame; this.currentOperation = frame.getOpcode(); - currentTrace = parentTraces.getLast(); + currentTrace = parentTraces.peekLast(); // set smart contract code - currentTrace.setCode( - currentTraceFrame.getMaybeCode().orElse(new Code()).getBytes().toHexString()); - } - - /** - * Find updated storage from 2 storage captures. - * - * @param firstCapture The first storage capture. - * @param secondCapture The second storage capture. - * @return an {@link Optional} wrapping the diff. - */ - private Optional updatedStorage( - final Optional> firstCapture, - final Optional> secondCapture) { - final Map first = firstCapture.orElse(new HashMap<>()); - final Map second = secondCapture.orElse(new HashMap<>()); - final MapDifference diff = Maps.difference(first, second); - final Map> entriesDiffering = - diff.entriesDiffering(); - if (entriesDiffering.size() > 0) { - final UInt256 firstDiffKey = entriesDiffering.keySet().iterator().next(); - final MapDifference.ValueDifference firstDiff = entriesDiffering.get(firstDiffKey); - return Optional.of(new StorageEntry(firstDiffKey, firstDiff.rightValue())); + if (currentTrace != null && "0x".equals(currentTrace.getCode())) { + currentTrace.setCode( + currentTraceFrame.getMaybeCode().orElse(new Code()).getBytes().toHexString()); } - final Map onlyOnRight = diff.entriesOnlyOnRight(); - if (onlyOnRight.size() > 0) { - final UInt256 firstOnlyOnRightKey = onlyOnRight.keySet().iterator().next(); - return Optional.of( - new StorageEntry(firstOnlyOnRightKey, onlyOnRight.get(firstOnlyOnRightKey))); - } - return Optional.empty(); } /** @@ -290,14 +270,4 @@ private Optional findLastFrameInCall( } return Optional.empty(); } - - static class StorageEntry { - private final UInt256 key; - private final UInt256 value; - - StorageEntry(final UInt256 key, final UInt256 value) { - this.key = key; - this.value = value; - } - } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java index 3eb113720bb..ae9a9b82d97 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByHashTest.java @@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason; @@ -66,9 +67,11 @@ public void shouldReturnCorrectResponse() { "NONE", Gas.of(45), Optional.of(Gas.of(56)), + Gas.ZERO, 2, EnumSet.noneOf(ExceptionalHaltReason.class), null, + Wei.ZERO, Bytes.EMPTY, Bytes.EMPTY, Optional.empty(), @@ -80,9 +83,8 @@ public void shouldReturnCorrectResponse() { Optional.empty(), 0, Optional.empty(), - Optional.empty(), - Optional.empty(), false, + Optional.empty(), Optional.empty()); final TransactionProcessor.Result transaction1Result = mock(TransactionProcessor.Result.class); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java index 3974cec2ceb..521cf129958 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockByNumberTest.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason; @@ -71,9 +72,11 @@ public void shouldReturnCorrectResponse() { "NONE", Gas.of(45), Optional.of(Gas.of(56)), + Gas.ZERO, 2, EnumSet.noneOf(ExceptionalHaltReason.class), null, + Wei.ZERO, Bytes.EMPTY, Bytes.EMPTY, Optional.empty(), @@ -85,9 +88,8 @@ public void shouldReturnCorrectResponse() { Optional.empty(), 0, Optional.empty(), - Optional.empty(), - Optional.empty(), false, + Optional.empty(), Optional.empty()); final TransactionProcessor.Result transaction1Result = mock(TransactionProcessor.Result.class); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java index 8cd24bda824..6b0d3340a53 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceBlockTest.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.Gas; +import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.debug.TraceFrame; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; @@ -84,9 +85,11 @@ public void shouldReturnCorrectResponse() { "NONE", Gas.of(45), Optional.of(Gas.of(56)), + Gas.ZERO, 2, EnumSet.noneOf(ExceptionalHaltReason.class), null, + Wei.ZERO, Bytes.EMPTY, Bytes.EMPTY, Optional.empty(), @@ -98,9 +101,8 @@ public void shouldReturnCorrectResponse() { Optional.empty(), 0, Optional.empty(), - Optional.empty(), - Optional.empty(), false, + Optional.empty(), Optional.empty()); final TransactionProcessor.Result transaction1Result = mock(TransactionProcessor.Result.class); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java index a615a32aee3..7a3b574e1c8 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceTransactionTest.java @@ -33,6 +33,7 @@ import org.hyperledger.besu.ethereum.core.Gas; 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.debug.TraceFrame; import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor.Result; import org.hyperledger.besu.ethereum.vm.ExceptionalHaltReason; @@ -83,9 +84,11 @@ public void shouldTraceTheTransactionUsingTheTransactionTracer() { "NONE", Gas.of(45), Optional.of(Gas.of(56)), + Gas.ZERO, 2, EnumSet.noneOf(ExceptionalHaltReason.class), null, + Wei.ZERO, Bytes.EMPTY, Bytes.EMPTY, Optional.empty(), @@ -97,9 +100,8 @@ public void shouldTraceTheTransactionUsingTheTransactionTracer() { Optional.empty(), 0, Optional.empty(), - Optional.empty(), - Optional.empty(), false, + Optional.empty(), Optional.empty()); final List traceFrames = Collections.singletonList(traceFrame); final TransactionTrace transactionTrace = @@ -139,9 +141,11 @@ public void shouldNotTraceTheTransactionIfNotFound() { "NONE", Gas.of(45), Optional.of(Gas.of(56)), + Gas.ZERO, 2, EnumSet.noneOf(ExceptionalHaltReason.class), null, + Wei.ZERO, Bytes.EMPTY, Bytes.EMPTY, Optional.empty(), @@ -153,9 +157,8 @@ public void shouldNotTraceTheTransactionIfNotFound() { Optional.empty(), 0, Optional.empty(), - Optional.empty(), - Optional.empty(), false, + Optional.empty(), Optional.empty()); final List traceFrames = Collections.singletonList(traceFrame); final TransactionTrace transactionTrace = diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/blocks.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/blocks.json index d8fe87656f7..b7a025dcc4e 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/blocks.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/blocks.json @@ -341,6 +341,80 @@ "to": "0x0140000000000000000000000000000000000000" } ] + }, + { + "number": "0x15", + "transactions": [ + { + "comment": "Set contract storage (key,value)'s: (1,1),(2,2)", + "secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "gasLimit": "0xFFFFF2", + "gasPrice": "0xEF", + "to": "0x0040000000000000000000000000000000000000", + "data": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002" + }, + { + "comment": "Set contract storage (key,value)'s: (1,3),(2,4)", + "secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "gasLimit": "0xFFFFF2", + "gasPrice": "0xEF", + "to": "0x0040000000000000000000000000000000000000", + "data": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004" + }, + { + "comment": "Set contract storage (key,value)'s: (1,3),(1,0)", + "secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "gasLimit": "0xFFFFF2", + "gasPrice": "0xEF", + "to": "0x0040000000000000000000000000000000000000", + "data": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000" + }, + { + "comment": "Clear contract storage keys 1 and 2", + "secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "gasLimit": "0xFFFFF2", + "gasPrice": "0xEF", + "to": "0x0040000000000000000000000000000000000000", + "data": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000" + } + ] + }, + { + "number": "0x16", + "transactions": [ + { + "comment": "Set contract storage (key,value)'s: (1,1),(2,2)", + "secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "gasLimit": "0xFFFFF2", + "gasPrice": "0xEF", + "to": "0x0060000000000000000000000000000000000000", + "data": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002" + }, + { + "comment": "Set contract storage (key,value)'s: (1,3),(2,4)", + "secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "gasLimit": "0xFFFFF2", + "gasPrice": "0xEF", + "to": "0x0060000000000000000000000000000000000000", + "data": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004" + }, + { + "comment": "Set contract storage (key,value)'s: (1,3),(1,0)", + "secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "gasLimit": "0xFFFFF2", + "gasPrice": "0xEF", + "to": "0x0060000000000000000000000000000000000000", + "data": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000" + }, + { + "comment": "Clear contract storage keys 1 and 2", + "secretKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "gasLimit": "0xFFFFF2", + "gasPrice": "0xEF", + "to": "0x0060000000000000000000000000000000000000", + "data": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000" + } + ] } ] -} \ No newline at end of file +} diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/genesis.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/genesis.json index 74b084b4a04..f6cf2fb99e8 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/genesis.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/chain-data/genesis.json @@ -51,8 +51,8 @@ }, "0060000000000000000000000000000000000000": { "comment": "Proxy a call to the address in the first 32 bytes, sending the rest of the input data to this address. Return 32 bytes from sub-call.", - "comment": "0x outSize 6020 outOffset 6000 inputSize 60203603 inputToMem(dupSize 80)6020600037 inOffset 6000 val 34 to 600035 gas 5A DELEGATECALL F4 Return 60206000F3", - "code": "0x60206000602036038060206000376000346000355AF460206000F3", + "comment": "0x outSize 6020 outOffset 6000 inputSize 60203603 inputToMem(dupSize 80)6020600037 inOffset 6000 to 600035 gas 5A DELEGATECALL F4 Return 60206000F3", + "code": "0x602060006020360380602060003760006000355AF460206000F3", "balance": "0x0" }, "0070000000000000000000000000000000000000": { diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x10.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x10.json index bf682a79b7f..2e259453f58 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x10.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x10.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1a055690e93c2f67b", - "to": "0x1a055690f82c2e969" + "from": "0x1a055690e93c2f49d", + "to": "0x1a055690f82c2e78b" } }, "code": "=", @@ -32,8 +32,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0babcdea", - "to": "0xffffffffffffffffffffffffffffffffe1cabdafc" + "from": "0xfffffffffffffffffffffffffffffffff0babcfc8", + "to": "0xffffffffffffffffffffffffffffffffe1cabdcda" } }, "code": "=", @@ -88,8 +88,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1a055690f82c2e969", - "to": "0x1a055691071c2dc57" + "from": "0x1a055690f82c2e78b", + "to": "0x1a055691071c2da79" } }, "code": "=", @@ -99,8 +99,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffe1cabdafc", - "to": "0xffffffffffffffffffffffffffffffffd2dabe80e" + "from": "0xffffffffffffffffffffffffffffffffe1cabdcda", + "to": "0xffffffffffffffffffffffffffffffffd2dabe9ec" } }, "code": "=", @@ -155,8 +155,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1a055691071c2dc57", - "to": "0x1a055691160c2cf45" + "from": "0x1a055691071c2da79", + "to": "0x1a055691160c2cd67" } }, "code": "=", @@ -166,8 +166,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffd2dabe80e", - "to": "0xffffffffffffffffffffffffffffffffc3eabf520" + "from": "0xffffffffffffffffffffffffffffffffd2dabe9ec", + "to": "0xffffffffffffffffffffffffffffffffc3eabf6fe" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x11.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x11.json index 6848ccbdfea..4557c67336e 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x11.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x11.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1bc16d678af8acf45", - "to": "0x1bc16d678afd772be" + "from": "0x1bc16d678af8acd67", + "to": "0x1bc16d678afd770e0" } }, "code": "=", @@ -32,8 +32,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffc3eabf520", - "to": "0xffffffffffffffffffffffffffffffffc3e5f51a7" + "from": "0xffffffffffffffffffffffffffffffffc3eabf6fe", + "to": "0xffffffffffffffffffffffffffffffffc3e5f5385" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x12.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x12.json index a56819abe13..2429fd436a8 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x12.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x12.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1d7d843dffe9f72be", - "to": "0x1d7d843dffeec2438" + "from": "0x1d7d843dffe9f70e0", + "to": "0x1d7d843dffeec225a" } }, "code": "=", @@ -32,8 +32,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffc3e5f51a7", - "to": "0xffffffffffffffffffffffffffffffffc3e12a02d" + "from": "0xffffffffffffffffffffffffffffffffc3e5f5385", + "to": "0xffffffffffffffffffffffffffffffffc3e12a20b" } }, "code": "=", @@ -187,8 +187,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1d7d843dffeec2438", - "to": "0x1d7d843dfff38d5b2" + "from": "0x1d7d843dffeec225a", + "to": "0x1d7d843dfff38d3d4" } }, "code": "=", @@ -198,8 +198,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffc3e12a02d", - "to": "0xffffffffffffffffffffffffffffffffc3dc5eeb3" + "from": "0xffffffffffffffffffffffffffffffffc3e12a20b", + "to": "0xffffffffffffffffffffffffffffffffc3dc5f091" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x13.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x13.json index c1065e28b13..09c2a2a617a 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x13.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x13.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1f399b1474e00d5b2", - "to": "0x1f399b1474ec5ecd6" + "from": "0x1f399b1474e00d3d4", + "to": "0x1f399b1474ec5eaf8" } }, "code": "=", @@ -179,8 +179,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1f399b1474ec5ecd6", - "to": "0x1f399b1474f4c9d41" + "from": "0x1f399b1474ec5eaf8", + "to": "0x1f399b1474f4c9b63" } }, "code": "=", @@ -654,8 +654,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1f399b1474f4c9d41", - "to": "0x1f399b1475011b465" + "from": "0x1f399b1474f4c9b63", + "to": "0x1f399b1475011b287" } }, "code": "=", @@ -812,8 +812,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1f399b1475011b465", - "to": "0x1f399b147509864d0" + "from": "0x1f399b1475011b287", + "to": "0x1f399b147509862f2" } }, "code": "=", @@ -1263,8 +1263,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1f399b147509864d0", - "to": "0x1f399b147515d7bf4" + "from": "0x1f399b147509862f2", + "to": "0x1f399b147515d7a16" } }, "code": "=", @@ -1421,8 +1421,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1f399b147515d7bf4", - "to": "0x1f399b14751e4016d" + "from": "0x1f399b147515d7a16", + "to": "0x1f399b14751e3ff8f" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x14.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x14.json index e685a38b897..883946dac3a 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x14.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x14.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x20f5b1eaea0ac016d", - "to": "0x20f5b1eaea24cac3f" + "from": "0x20f5b1eaea0abff8f", + "to": "0x20f5b1eaea24caa61" } }, "code": "=", @@ -69,8 +69,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffc3dc5eeb3", - "to": "0xffffffffffffffffffffffffffffffffc3c2543e1" + "from": "0xffffffffffffffffffffffffffffffffc3dc5f091", + "to": "0xffffffffffffffffffffffffffffffffc3c2545bf" } }, "code": "=", @@ -437,8 +437,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x20f5b1eaea24cac3f", - "to": "0x20f5b1eaea3eca4c0" + "from": "0x20f5b1eaea24caa61", + "to": "0x20f5b1eaea3eca2e2" } }, "code": "=", @@ -485,8 +485,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffc3c2543e1", - "to": "0xffffffffffffffffffffffffffffffffc3a854b60" + "from": "0xffffffffffffffffffffffffffffffffc3c2545bf", + "to": "0xffffffffffffffffffffffffffffffffc3a854d3e" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x15.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x15.json new file mode 100644 index 00000000000..65503780515 --- /dev/null +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x15.json @@ -0,0 +1,1870 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "trace_replayBlockTransactions", + "params": [ + "0x15", + [ + "trace", + "vmTrace", + "stateDiff" + ] + ], + "id": 415 + }, + "response": { + "jsonrpc": "2.0", + "result": [ + { + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "stateDiff": { + "0x0000000000000000000000000000000000000000": { + "balance": { + "*": { + "from": "0x22b1c8c15f2b4a2e2", + "to": "0x22b1c8c15f39889f5" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0x0010000000000000000000000000000000000000": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0000000000000000000000000000000000000000000000000000000000000001" + } + }, + "0x0000000000000000000000000000000000000000000000000000000000000002": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0000000000000000000000000000000000000000000000000000000000000002" + } + } + } + }, + "0x627306090abab3a6e1400e9345bc60c78a8bef57": { + "balance": { + "*": { + "from": "0xefffffffffffffffffa660fde", + "to": "0xefffffffffffffffff98228cb" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0xa", + "to": "0xb" + } + }, + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gas": "0xffab2e", + "input": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002", + "to": "0x0040000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x9f59", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "call", + "from": "0x0040000000000000000000000000000000000000", + "gas": "0xfba993", + "input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002", + "to": "0x0010000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x9c58", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "transactionHash": "0x7ab106c58a1c4d5c5f545f94ad266a945e3ed82835528fd3adbf7973d2c0c953", + "vmTrace": { + "code": "0x60206000602036038060206000376000346000355af160206000f3", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755499 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755496 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755493 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xa0" + ], + "store": null, + "used": 16755491 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80" + ], + "store": null, + "used": 16755488 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80", + "0x80" + ], + "store": null, + "used": 16755485 + }, + "pc": 8, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755482 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755479 + }, + "pc": 11, + "sub": null + }, + { + "cost": 27, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002", + "off": 0 + }, + "push": [], + "store": null, + "used": 16755452 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755449 + }, + "pc": 14, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755447 + }, + "pc": 16, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755444 + }, + "pc": 17, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x10000000000000000000000000000000000000" + ], + "store": null, + "used": 16755441 + }, + "pc": 19, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xffaaef" + ], + "store": null, + "used": 16755439 + }, + "pc": 20, + "sub": null + }, + { + "cost": 16493647, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16714715 + }, + "pc": 21, + "sub": { + "code": "0x6020356000355560603560403555", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16492944 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492941 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492938 + }, + "pc": 3, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492935 + }, + "pc": 5, + "sub": null + }, + { + "cost": 20000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x1" + }, + "used": 16472935 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 16472932 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x2" + ], + "store": null, + "used": 16472929 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 16472926 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x2" + ], + "store": null, + "used": 16472923 + }, + "pc": 12, + "sub": null + }, + { + "cost": 20000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x2", + "val": "0x2" + }, + "used": 16452923 + }, + "pc": 13, + "sub": null + } + ] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16714712 + }, + "pc": 22, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16714709 + }, + "pc": 24, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 16714709 + }, + "pc": 26, + "sub": null + } + ] + } + }, + { + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "stateDiff": { + "0x0000000000000000000000000000000000000000": { + "balance": { + "*": { + "from": "0x22b1c8c15f39889f5", + "to": "0x22b1c8c15f40f0938" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0x0010000000000000000000000000000000000000": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000001", + "to": "0x0000000000000000000000000000000000000000000000000000000000000003" + } + }, + "0x0000000000000000000000000000000000000000000000000000000000000002": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000002", + "to": "0x0000000000000000000000000000000000000000000000000000000000000004" + } + } + } + }, + "0x627306090abab3a6e1400e9345bc60c78a8bef57": { + "balance": { + "*": { + "from": "0xefffffffffffffffff98228cb", + "to": "0xefffffffffffffffff90ba988" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0xb", + "to": "0xc" + } + }, + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gas": "0xffab2e", + "input": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004", + "to": "0x0040000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x2a29", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "call", + "from": "0x0040000000000000000000000000000000000000", + "gas": "0xfba993", + "input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004", + "to": "0x0010000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x2728", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "transactionHash": "0x2b701e0b45e80f1c179a3696786aff2a411a51b03153528d1579f20915def5e4", + "vmTrace": { + "code": "0x60206000602036038060206000376000346000355af160206000f3", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755499 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755496 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755493 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xa0" + ], + "store": null, + "used": 16755491 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80" + ], + "store": null, + "used": 16755488 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80", + "0x80" + ], + "store": null, + "used": 16755485 + }, + "pc": 8, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755482 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755479 + }, + "pc": 11, + "sub": null + }, + { + "cost": 27, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004", + "off": 0 + }, + "push": [], + "store": null, + "used": 16755452 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755449 + }, + "pc": 14, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755447 + }, + "pc": 16, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755444 + }, + "pc": 17, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x10000000000000000000000000000000000000" + ], + "store": null, + "used": 16755441 + }, + "pc": 19, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xffaaef" + ], + "store": null, + "used": 16755439 + }, + "pc": 20, + "sub": null + }, + { + "cost": 16493647, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16744715 + }, + "pc": 21, + "sub": { + "code": "0x6020356000355560603560403555", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16492944 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x3" + ], + "store": null, + "used": 16492941 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492938 + }, + "pc": 3, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492935 + }, + "pc": 5, + "sub": null + }, + { + "cost": 5000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x3" + }, + "used": 16487935 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 16487932 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x4" + ], + "store": null, + "used": 16487929 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 16487926 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x2" + ], + "store": null, + "used": 16487923 + }, + "pc": 12, + "sub": null + }, + { + "cost": 5000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x2", + "val": "0x4" + }, + "used": 16482923 + }, + "pc": 13, + "sub": null + } + ] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16744712 + }, + "pc": 22, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16744709 + }, + "pc": 24, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 16744709 + }, + "pc": 26, + "sub": null + } + ] + } + }, + { + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "stateDiff": { + "0x0000000000000000000000000000000000000000": { + "balance": { + "*": { + "from": "0x22b1c8c15f40f0938", + "to": "0x22b1c8c15f4429b2b" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0x0010000000000000000000000000000000000000": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000003", + "to": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + } + } + }, + "0x627306090abab3a6e1400e9345bc60c78a8bef57": { + "balance": { + "*": { + "from": "0xefffffffffffffffff90ba988", + "to": "0xefffffffffffffffff8d81795" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0xc", + "to": "0xd" + } + }, + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gas": "0xffab3a", + "input": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0040000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x19c1", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "call", + "from": "0x0040000000000000000000000000000000000000", + "gas": "0xfba99f", + "input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0010000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x16c0", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "transactionHash": "0xcc81ab56669a97c5debfff3e23ab5ffb366f04c8c7eaf83f259c975c1816cb41", + "vmTrace": { + "code": "0x60206000602036038060206000376000346000355af160206000f3", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755511 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755508 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755505 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xa0" + ], + "store": null, + "used": 16755503 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80" + ], + "store": null, + "used": 16755500 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80", + "0x80" + ], + "store": null, + "used": 16755497 + }, + "pc": 8, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755494 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755491 + }, + "pc": 11, + "sub": null + }, + { + "cost": 27, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "off": 0 + }, + "push": [], + "store": null, + "used": 16755464 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755461 + }, + "pc": 14, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755459 + }, + "pc": 16, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755456 + }, + "pc": 17, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x10000000000000000000000000000000000000" + ], + "store": null, + "used": 16755453 + }, + "pc": 19, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xffaafb" + ], + "store": null, + "used": 16755451 + }, + "pc": 20, + "sub": null + }, + { + "cost": 16493659, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16748927 + }, + "pc": 21, + "sub": { + "code": "0x6020356000355560603560403555", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16492956 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x3" + ], + "store": null, + "used": 16492953 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492950 + }, + "pc": 3, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492947 + }, + "pc": 5, + "sub": null + }, + { + "cost": 800, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x3" + }, + "used": 16492147 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 16492144 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492141 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 16492138 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492135 + }, + "pc": 12, + "sub": null + }, + { + "cost": 5000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x0" + }, + "used": 16487135 + }, + "pc": 13, + "sub": null + } + ] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16748924 + }, + "pc": 22, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16748921 + }, + "pc": 24, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 16748921 + }, + "pc": 26, + "sub": null + } + ] + } + }, + { + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "stateDiff": { + "0x0000000000000000000000000000000000000000": { + "balance": { + "*": { + "from": "0x22b1c8c15f4429b2b", + "to": "0x22b1c8c15f4762784" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0x0010000000000000000000000000000000000000": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000002": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000004", + "to": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + } + } + }, + "0x627306090abab3a6e1400e9345bc60c78a8bef57": { + "balance": { + "*": { + "from": "0xefffffffffffffffff8d81795", + "to": "0xefffffffffffffffff8a48b3c" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0xd", + "to": "0xe" + } + }, + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gas": "0xffab46", + "input": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0040000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x19c1", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "call", + "from": "0x0040000000000000000000000000000000000000", + "gas": "0xfba9aa", + "input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0010000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x16c0", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "transactionHash": "0x422edfe209e8b97f77a8e879db5182158f23e823ba7f59725d463d99d7ae66bd", + "vmTrace": { + "code": "0x60206000602036038060206000376000346000355af160206000f3", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755523 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755520 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755517 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xa0" + ], + "store": null, + "used": 16755515 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80" + ], + "store": null, + "used": 16755512 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80", + "0x80" + ], + "store": null, + "used": 16755509 + }, + "pc": 8, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755506 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755503 + }, + "pc": 11, + "sub": null + }, + { + "cost": 27, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000", + "off": 0 + }, + "push": [], + "store": null, + "used": 16755476 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755473 + }, + "pc": 14, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755471 + }, + "pc": 16, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755468 + }, + "pc": 17, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x10000000000000000000000000000000000000" + ], + "store": null, + "used": 16755465 + }, + "pc": 19, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xffab07" + ], + "store": null, + "used": 16755463 + }, + "pc": 20, + "sub": null + }, + { + "cost": 16493670, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16748939 + }, + "pc": 21, + "sub": { + "code": "0x6020356000355560603560403555", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16492967 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492964 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492961 + }, + "pc": 3, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492958 + }, + "pc": 5, + "sub": null + }, + { + "cost": 800, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x0" + }, + "used": 16492158 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 16492155 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492152 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 16492149 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x2" + ], + "store": null, + "used": 16492146 + }, + "pc": 12, + "sub": null + }, + { + "cost": 5000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x2", + "val": "0x0" + }, + "used": 16487146 + }, + "pc": 13, + "sub": null + } + ] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16748936 + }, + "pc": 22, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16748933 + }, + "pc": 24, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 16748933 + }, + "pc": 26, + "sub": null + } + ] + } + } + ], + "id": 415 + }, + "statusCode": 200 +} diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x16.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x16.json new file mode 100644 index 00000000000..ad20ec57a05 --- /dev/null +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0x16.json @@ -0,0 +1,1818 @@ +{ + "request": { + "jsonrpc": "2.0", + "method": "trace_replayBlockTransactions", + "params": [ + "0x16", + [ + "trace", + "vmTrace", + "stateDiff" + ] + ], + "id": 415 + }, + "response": { + "jsonrpc": "2.0", + "result": [ + { + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "stateDiff": { + "0x0000000000000000000000000000000000000000": { + "balance": { + "*": { + "from": "0x246ddf97d433e2784", + "to": "0x246ddf97d44220cb9" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0x0060000000000000000000000000000000000000": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0000000000000000000000000000000000000000000000000000000000000001" + } + }, + "0x0000000000000000000000000000000000000000000000000000000000000002": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0000000000000000000000000000000000000000000000000000000000000002" + } + } + } + }, + "0x627306090abab3a6e1400e9345bc60c78a8bef57": { + "balance": { + "*": { + "from": "0xefffffffffffffffff8a48b3c", + "to": "0xefffffffffffffffff7c0a607" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0xe", + "to": "0xf" + } + }, + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gas": "0xffab2e", + "input": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002", + "to": "0x0060000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x9f57", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "delegatecall", + "from": "0x0060000000000000000000000000000000000000", + "gas": "0xfba995", + "input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002", + "to": "0x0010000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x9c58", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "transactionHash": "0x1f74ac428df5427f3a5576869e870cfff6712e4cffb1506bb4ef8f36c5e48162", + "vmTrace": { + "code": "0x602060006020360380602060003760006000355af460206000f3", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755499 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755496 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755493 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xa0" + ], + "store": null, + "used": 16755491 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80" + ], + "store": null, + "used": 16755488 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80", + "0x80" + ], + "store": null, + "used": 16755485 + }, + "pc": 8, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755482 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755479 + }, + "pc": 11, + "sub": null + }, + { + "cost": 27, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002", + "off": 0 + }, + "push": [], + "store": null, + "used": 16755452 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755449 + }, + "pc": 14, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755446 + }, + "pc": 16, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x10000000000000000000000000000000000000" + ], + "store": null, + "used": 16755443 + }, + "pc": 18, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xffaaf1" + ], + "store": null, + "used": 16755441 + }, + "pc": 19, + "sub": null + }, + { + "cost": 16493649, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16714717 + }, + "pc": 20, + "sub": { + "code": "0x6020356000355560603560403555", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16492946 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492943 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492940 + }, + "pc": 3, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492937 + }, + "pc": 5, + "sub": null + }, + { + "cost": 20000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x1" + }, + "used": 16472937 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 16472934 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x2" + ], + "store": null, + "used": 16472931 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 16472928 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x2" + ], + "store": null, + "used": 16472925 + }, + "pc": 12, + "sub": null + }, + { + "cost": 20000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x2", + "val": "0x2" + }, + "used": 16452925 + }, + "pc": 13, + "sub": null + } + ] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16714714 + }, + "pc": 21, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16714711 + }, + "pc": 23, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 16714711 + }, + "pc": 25, + "sub": null + } + ] + } + }, + { + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "stateDiff": { + "0x0000000000000000000000000000000000000000": { + "balance": { + "*": { + "from": "0x246ddf97d44220cb9", + "to": "0x246ddf97d44988a1e" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0x0060000000000000000000000000000000000000": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000001", + "to": "0x0000000000000000000000000000000000000000000000000000000000000003" + } + }, + "0x0000000000000000000000000000000000000000000000000000000000000002": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000002", + "to": "0x0000000000000000000000000000000000000000000000000000000000000004" + } + } + } + }, + "0x627306090abab3a6e1400e9345bc60c78a8bef57": { + "balance": { + "*": { + "from": "0xefffffffffffffffff7c0a607", + "to": "0xefffffffffffffffff74a28a2" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0xf", + "to": "0x10" + } + }, + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gas": "0xffab2e", + "input": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004", + "to": "0x0060000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x2a27", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "delegatecall", + "from": "0x0060000000000000000000000000000000000000", + "gas": "0xfba995", + "input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004", + "to": "0x0010000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x2728", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "transactionHash": "0x43d6ba1caeced03b6fa8cc3549c0557eea52917f1de50f4da23c9642beca95ee", + "vmTrace": { + "code": "0x602060006020360380602060003760006000355af460206000f3", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755499 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755496 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755493 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xa0" + ], + "store": null, + "used": 16755491 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80" + ], + "store": null, + "used": 16755488 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80", + "0x80" + ], + "store": null, + "used": 16755485 + }, + "pc": 8, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755482 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755479 + }, + "pc": 11, + "sub": null + }, + { + "cost": 27, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004", + "off": 0 + }, + "push": [], + "store": null, + "used": 16755452 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755449 + }, + "pc": 14, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755446 + }, + "pc": 16, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x10000000000000000000000000000000000000" + ], + "store": null, + "used": 16755443 + }, + "pc": 18, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xffaaf1" + ], + "store": null, + "used": 16755441 + }, + "pc": 19, + "sub": null + }, + { + "cost": 16493649, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16744717 + }, + "pc": 20, + "sub": { + "code": "0x6020356000355560603560403555", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16492946 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x3" + ], + "store": null, + "used": 16492943 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492940 + }, + "pc": 3, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492937 + }, + "pc": 5, + "sub": null + }, + { + "cost": 5000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x3" + }, + "used": 16487937 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 16487934 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x4" + ], + "store": null, + "used": 16487931 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 16487928 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x2" + ], + "store": null, + "used": 16487925 + }, + "pc": 12, + "sub": null + }, + { + "cost": 5000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x2", + "val": "0x4" + }, + "used": 16482925 + }, + "pc": 13, + "sub": null + } + ] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16744714 + }, + "pc": 21, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16744711 + }, + "pc": 23, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 16744711 + }, + "pc": 25, + "sub": null + } + ] + } + }, + { + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "stateDiff": { + "0x0000000000000000000000000000000000000000": { + "balance": { + "*": { + "from": "0x246ddf97d44988a1e", + "to": "0x246ddf97d44cc1b22" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0x0060000000000000000000000000000000000000": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000001": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000003", + "to": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + } + } + }, + "0x627306090abab3a6e1400e9345bc60c78a8bef57": { + "balance": { + "*": { + "from": "0xefffffffffffffffff74a28a2", + "to": "0xefffffffffffffffff716979e" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0x10", + "to": "0x11" + } + }, + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gas": "0xffab3a", + "input": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0060000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x19bf", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "delegatecall", + "from": "0x0060000000000000000000000000000000000000", + "gas": "0xfba9a0", + "input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0010000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x16c0", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "transactionHash": "0x9eb50f31fc1d953e27331cd923f6b2f7fa11827d399c70aec00a04cf98cfd2ac", + "vmTrace": { + "code": "0x602060006020360380602060003760006000355af460206000f3", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755511 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755508 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755505 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xa0" + ], + "store": null, + "used": 16755503 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80" + ], + "store": null, + "used": 16755500 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80", + "0x80" + ], + "store": null, + "used": 16755497 + }, + "pc": 8, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755494 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755491 + }, + "pc": 11, + "sub": null + }, + { + "cost": 27, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", + "off": 0 + }, + "push": [], + "store": null, + "used": 16755464 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755461 + }, + "pc": 14, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755458 + }, + "pc": 16, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x10000000000000000000000000000000000000" + ], + "store": null, + "used": 16755455 + }, + "pc": 18, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xffaafd" + ], + "store": null, + "used": 16755453 + }, + "pc": 19, + "sub": null + }, + { + "cost": 16493660, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16748929 + }, + "pc": 20, + "sub": { + "code": "0x6020356000355560603560403555", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16492957 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x3" + ], + "store": null, + "used": 16492954 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492951 + }, + "pc": 3, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492948 + }, + "pc": 5, + "sub": null + }, + { + "cost": 800, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x3" + }, + "used": 16492148 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 16492145 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492142 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 16492139 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492136 + }, + "pc": 12, + "sub": null + }, + { + "cost": 5000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x0" + }, + "used": 16487136 + }, + "pc": 13, + "sub": null + } + ] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16748926 + }, + "pc": 21, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16748923 + }, + "pc": 23, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 16748923 + }, + "pc": 25, + "sub": null + } + ] + } + }, + { + "output": "0x0000000000000000000000000000000000000000000000000000000000000001", + "stateDiff": { + "0x0000000000000000000000000000000000000000": { + "balance": { + "*": { + "from": "0x246ddf97d44cc1b22", + "to": "0x246ddf97d44ffa68c" + } + }, + "code": "=", + "nonce": "=", + "storage": {} + }, + "0x0060000000000000000000000000000000000000": { + "balance": "=", + "code": "=", + "nonce": "=", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000002": { + "*": { + "from": "0x0000000000000000000000000000000000000000000000000000000000000004", + "to": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + } + } + }, + "0x627306090abab3a6e1400e9345bc60c78a8bef57": { + "balance": { + "*": { + "from": "0xefffffffffffffffff716979e", + "to": "0xefffffffffffffffff6e30c34" + } + }, + "code": "=", + "nonce": { + "*": { + "from": "0x11", + "to": "0x12" + } + }, + "storage": {} + } + }, + "trace": [ + { + "action": { + "callType": "call", + "from": "0x627306090abab3a6e1400e9345bc60c78a8bef57", + "gas": "0xffab46", + "input": "0x00000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0060000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x19bf", + "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + }, + "subtraces": 1, + "traceAddress": [], + "type": "call" + }, + { + "action": { + "callType": "delegatecall", + "from": "0x0060000000000000000000000000000000000000", + "gas": "0xfba9ac", + "input": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000", + "to": "0x0010000000000000000000000000000000000000", + "value": "0x0" + }, + "result": { + "gasUsed": "0x16c0", + "output": "0x" + }, + "subtraces": 0, + "traceAddress": [ + 0 + ], + "type": "call" + } + ], + "transactionHash": "0xe2ef09a0b71f50947bd0bd1cacac77903d2a5fce80f34862bd4559727e0f608c", + "vmTrace": { + "code": "0x602060006020360380602060003760006000355af460206000f3", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755523 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755520 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755517 + }, + "pc": 4, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xa0" + ], + "store": null, + "used": 16755515 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80" + ], + "store": null, + "used": 16755512 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x80", + "0x80" + ], + "store": null, + "used": 16755509 + }, + "pc": 8, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16755506 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755503 + }, + "pc": 11, + "sub": null + }, + { + "cost": 27, + "ex": { + "mem": { + "data": "0x0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000", + "off": 0 + }, + "push": [], + "store": null, + "used": 16755476 + }, + "pc": 13, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755473 + }, + "pc": 14, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16755470 + }, + "pc": 16, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x10000000000000000000000000000000000000" + ], + "store": null, + "used": 16755467 + }, + "pc": 18, + "sub": null + }, + { + "cost": 2, + "ex": { + "mem": null, + "push": [ + "0xffab09" + ], + "store": null, + "used": 16755465 + }, + "pc": 19, + "sub": null + }, + { + "cost": 16493672, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16748941 + }, + "pc": 20, + "sub": { + "code": "0x6020356000355560603560403555", + "ops": [ + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16492969 + }, + "pc": 0, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492966 + }, + "pc": 2, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492963 + }, + "pc": 3, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x1" + ], + "store": null, + "used": 16492960 + }, + "pc": 5, + "sub": null + }, + { + "cost": 800, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x1", + "val": "0x0" + }, + "used": 16492160 + }, + "pc": 6, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x60" + ], + "store": null, + "used": 16492157 + }, + "pc": 7, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16492154 + }, + "pc": 9, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x40" + ], + "store": null, + "used": 16492151 + }, + "pc": 10, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x2" + ], + "store": null, + "used": 16492148 + }, + "pc": 12, + "sub": null + }, + { + "cost": 5000, + "ex": { + "mem": null, + "push": [], + "store": { + "key": "0x2", + "val": "0x0" + }, + "used": 16487148 + }, + "pc": 13, + "sub": null + } + ] + } + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x20" + ], + "store": null, + "used": 16748938 + }, + "pc": 21, + "sub": null + }, + { + "cost": 3, + "ex": { + "mem": null, + "push": [ + "0x0" + ], + "store": null, + "used": 16748935 + }, + "pc": 23, + "sub": null + }, + { + "cost": 0, + "ex": { + "mem": null, + "push": [], + "store": null, + "used": 16748935 + }, + "pc": 25, + "sub": null + } + ] + } + } + ], + "id": 415 + }, + "statusCode": 200 +} diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xB.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xB.json index b29e7b46b92..4a54813786e 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xB.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xB.json @@ -16,13 +16,13 @@ "jsonrpc": "2.0", "result": [ { - "output": "0xf000000000000000000000000000000000000000000000000000000000000001", + "output": "0xf000000000000000000000000000000000000000000000000000000000000002", "stateDiff": { "0x0000000000000000000000000000000000000000": { "balance": { "*": { "from": "0x1158e460918415e6b", - "to": "0x1158e46091891dad5" + "to": "0x1158e46091891d8f7" } }, "code": "=", @@ -33,7 +33,7 @@ "balance": { "*": { "from": "0xffffffffffffffffffffffffffffffffffd4565fa", - "to": "0xffffffffffffffffffffffffffffffffffcf4e990" + "to": "0xffffffffffffffffffffffffffffffffffcf4eb6e" } }, "code": "=", @@ -57,8 +57,8 @@ "value": "0x0" }, "result": { - "gasUsed": "0x30a", - "output": "0xf000000000000000000000000000000000000000000000000000000000000001" + "gasUsed": "0x308", + "output": "0xf000000000000000000000000000000000000000000000000000000000000002" }, "subtraces": 1, "traceAddress": [], @@ -68,14 +68,14 @@ "action": { "callType": "delegatecall", "from": "0x0060000000000000000000000000000000000000", - "gas": "0xfbab36", - "input": "0x", + "gas": "0xfbab38", + "input": "0xf000000000000000000000000000000000000000000000000000000000000001", "to": "0x0030000000000000000000000000000000000000", "value": "0x0" }, "result": { "gasUsed": "0x1b", - "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + "output": "0xf000000000000000000000000000000000000000000000000000000000000002" }, "subtraces": 0, "traceAddress": [ @@ -86,7 +86,7 @@ ], "transactionHash": "0x6f77512ee9d43474a884c0703c86712fb98dca772fa6e12252786e3e23f196c1", "vmTrace": { - "code": "0x60206000602036038060206000376000346000355af460206000f3", + "code": "0x602060006020360380602060003760006000355af460206000f3", "ops": [ { "cost": 3, @@ -220,19 +220,6 @@ "pc": 14, "sub": null }, - { - "cost": 2, - "ex": { - "mem": null, - "push": [ - "0x0" - ], - "store": null, - "used": 16755873 - }, - "pc": 16, - "sub": null - }, { "cost": 3, "ex": { @@ -241,9 +228,9 @@ "0x0" ], "store": null, - "used": 16755870 + "used": 16755872 }, - "pc": 17, + "pc": 16, "sub": null }, { @@ -254,9 +241,9 @@ "0x30000000000000000000000000000000000000" ], "store": null, - "used": 16755867 + "used": 16755869 }, - "pc": 19, + "pc": 18, "sub": null }, { @@ -264,25 +251,28 @@ "ex": { "mem": null, "push": [ - "0xffac99" + "0xffac9b" ], "store": null, - "used": 16755865 + "used": 16755867 }, - "pc": 20, + "pc": 19, "sub": null }, { - "cost": 16494066, + "cost": 16494068, "ex": { - "mem": null, + "mem": { + "data": "0xf000000000000000000000000000000000000000000000000000000000000002", + "off": 0 + }, "push": [ "0x1" ], "store": null, - "used": 16755138 + "used": 16755140 }, - "pc": 21, + "pc": 20, "sub": { "code": "0x60003560010160005260206000f3", "ops": [ @@ -294,7 +284,7 @@ "0x0" ], "store": null, - "used": 16493363 + "used": 16493365 }, "pc": 0, "sub": null @@ -304,10 +294,10 @@ "ex": { "mem": null, "push": [ - "0x0" + "0xf000000000000000000000000000000000000000000000000000000000000001" ], "store": null, - "used": 16493360 + "used": 16493362 }, "pc": 2, "sub": null @@ -320,7 +310,7 @@ "0x1" ], "store": null, - "used": 16493357 + "used": 16493359 }, "pc": 3, "sub": null @@ -330,10 +320,10 @@ "ex": { "mem": null, "push": [ - "0x1" + "0xf000000000000000000000000000000000000000000000000000000000000002" ], "store": null, - "used": 16493354 + "used": 16493356 }, "pc": 5, "sub": null @@ -346,7 +336,7 @@ "0x0" ], "store": null, - "used": 16493351 + "used": 16493353 }, "pc": 6, "sub": null @@ -355,12 +345,12 @@ "cost": 6, "ex": { "mem": { - "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "data": "0xf000000000000000000000000000000000000000000000000000000000000002", "off": 0 }, "push": [], "store": null, - "used": 16493345 + "used": 16493347 }, "pc": 8, "sub": null @@ -373,7 +363,7 @@ "0x20" ], "store": null, - "used": 16493342 + "used": 16493344 }, "pc": 9, "sub": null @@ -386,7 +376,7 @@ "0x0" ], "store": null, - "used": 16493339 + "used": 16493341 }, "pc": 11, "sub": null @@ -397,7 +387,7 @@ "mem": null, "push": [], "store": null, - "used": 16493339 + "used": 16493341 }, "pc": 13, "sub": null @@ -413,9 +403,9 @@ "0x20" ], "store": null, - "used": 16755135 + "used": 16755137 }, - "pc": 22, + "pc": 21, "sub": null }, { @@ -426,9 +416,9 @@ "0x0" ], "store": null, - "used": 16755132 + "used": 16755134 }, - "pc": 24, + "pc": 23, "sub": null }, { @@ -437,9 +427,9 @@ "mem": null, "push": [], "store": null, - "used": 16755132 + "used": 16755134 }, - "pc": 26, + "pc": 25, "sub": null } ] diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xC.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xC.json index 6b3631a799b..b4ca52c3bcc 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xC.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xC.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1314fb3706759dad5", - "to": "0x1314fb37067a68c4f" + "from": "0x1314fb3706759d8f7", + "to": "0x1314fb37067a68a71" } }, "code": "=", @@ -32,8 +32,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffffcf4e990", - "to": "0xffffffffffffffffffffffffffffffffffca83816" + "from": "0xffffffffffffffffffffffffffffffffffcf4eb6e", + "to": "0xffffffffffffffffffffffffffffffffffca839f4" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xD.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xD.json index 282e1627bae..c5ef972bd0d 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xD.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xD.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x14d1120d7b66e8c4f", - "to": "0x14d1120d8a56e7f3d" + "from": "0x14d1120d7b66e8a71", + "to": "0x14d1120d8a56e7d5f" } }, "code": "=", @@ -32,8 +32,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffffca83816", - "to": "0xfffffffffffffffffffffffffffffffff0da84528" + "from": "0xffffffffffffffffffffffffffffffffffca839f4", + "to": "0xfffffffffffffffffffffffffffffffff0da84706" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xE.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xE.json index c426eae37a9..d3d700c80c7 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xE.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xE.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x168d28e3ff4367f3d", - "to": "0x168d28e3ff4cf0b77" + "from": "0x168d28e3ff4367d5f", + "to": "0x168d28e3ff4cf0999" } }, "code": "=", @@ -45,8 +45,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0da84528", - "to": "0xfffffffffffffffffffffffffffffffff0d0fb8ee" + "from": "0xfffffffffffffffffffffffffffffffff0da84706", + "to": "0xfffffffffffffffffffffffffffffffff0d0fbacc" } }, "code": "=", @@ -248,8 +248,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x168d28e3ff4cf0b77", - "to": "0x168d28e3ff530e3c9" + "from": "0x168d28e3ff4cf0999", + "to": "0x168d28e3ff530e1eb" } }, "code": "=", @@ -272,8 +272,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0d0fb8ee", - "to": "0xfffffffffffffffffffffffffffffffff0cade09c" + "from": "0xfffffffffffffffffffffffffffffffff0d0fbacc", + "to": "0xfffffffffffffffffffffffffffffffff0cade27a" } }, "code": "=", @@ -475,8 +475,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x168d28e3ff530e3c9", - "to": "0x168d28e3ff592bc1b" + "from": "0x168d28e3ff530e1eb", + "to": "0x168d28e3ff592ba3d" } }, "code": "=", @@ -499,8 +499,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0cade09c", - "to": "0xfffffffffffffffffffffffffffffffff0c4c084a" + "from": "0xfffffffffffffffffffffffffffffffff0cade27a", + "to": "0xfffffffffffffffffffffffffffffffff0c4c0a28" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xF.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xF.json index b84c7871ae0..78a95548e82 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xF.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xF.json @@ -21,8 +21,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x18493fba7445abc1b", - "to": "0x18493fba744aa28d8" + "from": "0x18493fba7445aba3d", + "to": "0x18493fba744aa26fa" } }, "code": "=", @@ -32,8 +32,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0c4c084a", - "to": "0xfffffffffffffffffffffffffffffffff0bfc9b8d" + "from": "0xfffffffffffffffffffffffffffffffff0c4c0a28", + "to": "0xfffffffffffffffffffffffffffffffff0bfc9d6b" } }, "code": "=", @@ -168,8 +168,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x18493fba744aa28d8", - "to": "0x18493fba744faf67b" + "from": "0x18493fba744aa26fa", + "to": "0x18493fba744faf49d" } }, "code": "=", @@ -179,8 +179,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0bfc9b8d", - "to": "0xfffffffffffffffffffffffffffffffff0babcdea" + "from": "0xfffffffffffffffffffffffffffffffff0bfc9d6b", + "to": "0xfffffffffffffffffffffffffffffffff0babcfc8" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/flat/trace_replayBlockTransactions_traceOnly_0xB.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/flat/trace_replayBlockTransactions_traceOnly_0xB.json index 2d2fa4ba218..df936cecb1c 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/flat/trace_replayBlockTransactions_traceOnly_0xB.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/flat/trace_replayBlockTransactions_traceOnly_0xB.json @@ -14,7 +14,7 @@ "jsonrpc": "2.0", "result": [ { - "output": "0xf000000000000000000000000000000000000000000000000000000000000001", + "output": "0xf000000000000000000000000000000000000000000000000000000000000002", "stateDiff": null, "trace": [ { @@ -27,8 +27,8 @@ "value": "0x0" }, "result": { - "gasUsed": "0x30a", - "output": "0xf000000000000000000000000000000000000000000000000000000000000001" + "gasUsed": "0x308", + "output": "0xf000000000000000000000000000000000000000000000000000000000000002" }, "subtraces": 1, "traceAddress": [], @@ -38,14 +38,14 @@ "action": { "callType": "delegatecall", "from": "0x0060000000000000000000000000000000000000", - "gas": "0xfbab36", - "input": "0x", + "gas": "0xfbab38", + "input": "0xf000000000000000000000000000000000000000000000000000000000000001", "to": "0x0030000000000000000000000000000000000000", "value": "0x0" }, "result": { "gasUsed": "0x1b", - "output": "0x0000000000000000000000000000000000000000000000000000000000000001" + "output": "0xf000000000000000000000000000000000000000000000000000000000000002" }, "subtraces": 0, "traceAddress": [ diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xB.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xB.json index 58a2f8724bd..862269a7543 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xB.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xB.json @@ -14,13 +14,13 @@ "jsonrpc": "2.0", "result": [ { - "output": "0xf000000000000000000000000000000000000000000000000000000000000001", + "output": "0xf000000000000000000000000000000000000000000000000000000000000002", "stateDiff": { "0x0000000000000000000000000000000000000000": { "balance": { "*": { "from": "0x1158e460918415e6b", - "to": "0x1158e46091891dad5" + "to": "0x1158e46091891d8f7" } }, "code": "=", @@ -31,7 +31,7 @@ "balance": { "*": { "from": "0xffffffffffffffffffffffffffffffffffd4565fa", - "to": "0xffffffffffffffffffffffffffffffffffcf4e990" + "to": "0xffffffffffffffffffffffffffffffffffcf4eb6e" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xC.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xC.json index cd39358dded..c81db19c5b0 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xC.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xC.json @@ -19,8 +19,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x1314fb3706759dad5", - "to": "0x1314fb37067a68c4f" + "from": "0x1314fb3706759d8f7", + "to": "0x1314fb37067a68a71" } }, "code": "=", @@ -30,8 +30,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffffcf4e990", - "to": "0xffffffffffffffffffffffffffffffffffca83816" + "from": "0xffffffffffffffffffffffffffffffffffcf4eb6e", + "to": "0xffffffffffffffffffffffffffffffffffca839f4" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xD.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xD.json index b12755265ef..fc619f11e66 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xD.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xD.json @@ -19,8 +19,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x14d1120d7b66e8c4f", - "to": "0x14d1120d8a56e7f3d" + "from": "0x14d1120d7b66e8a71", + "to": "0x14d1120d8a56e7d5f" } }, "code": "=", @@ -30,8 +30,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xffffffffffffffffffffffffffffffffffca83816", - "to": "0xfffffffffffffffffffffffffffffffff0da84528" + "from": "0xffffffffffffffffffffffffffffffffffca839f4", + "to": "0xfffffffffffffffffffffffffffffffff0da84706" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xE.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xE.json index 8e4d803f21f..ee6862e7f53 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xE.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/statediff/trace_replayBlockTransactions_diffOnly_0xE.json @@ -19,8 +19,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x168d28e3ff4367f3d", - "to": "0x168d28e3ff4cf0b77" + "from": "0x168d28e3ff4367d5f", + "to": "0x168d28e3ff4cf0999" } }, "code": "=", @@ -43,8 +43,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0da84528", - "to": "0xfffffffffffffffffffffffffffffffff0d0fb8ee" + "from": "0xfffffffffffffffffffffffffffffffff0da84706", + "to": "0xfffffffffffffffffffffffffffffffff0d0fbacc" } }, "code": "=", @@ -67,8 +67,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x168d28e3ff4cf0b77", - "to": "0x168d28e3ff530e3c9" + "from": "0x168d28e3ff4cf0999", + "to": "0x168d28e3ff530e1eb" } }, "code": "=", @@ -91,8 +91,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0d0fb8ee", - "to": "0xfffffffffffffffffffffffffffffffff0cade09c" + "from": "0xfffffffffffffffffffffffffffffffff0d0fbacc", + "to": "0xfffffffffffffffffffffffffffffffff0cade27a" } }, "code": "=", @@ -115,8 +115,8 @@ "0x0000000000000000000000000000000000000000": { "balance": { "*": { - "from": "0x168d28e3ff530e3c9", - "to": "0x168d28e3ff592bc1b" + "from": "0x168d28e3ff530e1eb", + "to": "0x168d28e3ff592ba3d" } }, "code": "=", @@ -139,8 +139,8 @@ "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": { "balance": { "*": { - "from": "0xfffffffffffffffffffffffffffffffff0cade09c", - "to": "0xfffffffffffffffffffffffffffffffff0c4c084a" + "from": "0xfffffffffffffffffffffffffffffffff0cade27a", + "to": "0xfffffffffffffffffffffffffffffffff0c4c0a28" } }, "code": "=", diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_replayBlockTransactions_0xB.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_replayBlockTransactions_0xB.json index d3858d5a9db..ce46a14e1c2 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_replayBlockTransactions_0xB.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_replayBlockTransactions_0xB.json @@ -14,12 +14,12 @@ "jsonrpc": "2.0", "result": [ { - "output": "0xf000000000000000000000000000000000000000000000000000000000000001", + "output": "0xf000000000000000000000000000000000000000000000000000000000000002", "stateDiff": null, "trace": [], "transactionHash": "0x6f77512ee9d43474a884c0703c86712fb98dca772fa6e12252786e3e23f196c1", "vmTrace": { - "code": "0x60206000602036038060206000376000346000355af460206000f3", + "code": "0x602060006020360380602060003760006000355af460206000f3", "ops": [ { "cost": 3, @@ -153,19 +153,6 @@ "pc": 14, "sub": null }, - { - "cost": 2, - "ex": { - "mem": null, - "push": [ - "0x0" - ], - "store": null, - "used": 16755873 - }, - "pc": 16, - "sub": null - }, { "cost": 3, "ex": { @@ -174,9 +161,9 @@ "0x0" ], "store": null, - "used": 16755870 + "used": 16755872 }, - "pc": 17, + "pc": 16, "sub": null }, { @@ -187,9 +174,9 @@ "0x30000000000000000000000000000000000000" ], "store": null, - "used": 16755867 + "used": 16755869 }, - "pc": 19, + "pc": 18, "sub": null }, { @@ -197,25 +184,28 @@ "ex": { "mem": null, "push": [ - "0xffac99" + "0xffac9b" ], "store": null, - "used": 16755865 + "used": 16755867 }, - "pc": 20, + "pc": 19, "sub": null }, { - "cost": 16494066, + "cost": 16494068, "ex": { - "mem": null, + "mem": { + "data": "0xf000000000000000000000000000000000000000000000000000000000000002", + "off": 0 + }, "push": [ "0x1" ], "store": null, - "used": 16755138 + "used": 16755140 }, - "pc": 21, + "pc": 20, "sub": { "code": "0x60003560010160005260206000f3", "ops": [ @@ -227,7 +217,7 @@ "0x0" ], "store": null, - "used": 16493363 + "used": 16493365 }, "pc": 0, "sub": null @@ -237,10 +227,10 @@ "ex": { "mem": null, "push": [ - "0x0" + "0xf000000000000000000000000000000000000000000000000000000000000001" ], "store": null, - "used": 16493360 + "used": 16493362 }, "pc": 2, "sub": null @@ -253,7 +243,7 @@ "0x1" ], "store": null, - "used": 16493357 + "used": 16493359 }, "pc": 3, "sub": null @@ -263,10 +253,10 @@ "ex": { "mem": null, "push": [ - "0x1" + "0xf000000000000000000000000000000000000000000000000000000000000002" ], "store": null, - "used": 16493354 + "used": 16493356 }, "pc": 5, "sub": null @@ -279,7 +269,7 @@ "0x0" ], "store": null, - "used": 16493351 + "used": 16493353 }, "pc": 6, "sub": null @@ -288,12 +278,12 @@ "cost": 6, "ex": { "mem": { - "data": "0x0000000000000000000000000000000000000000000000000000000000000001", + "data": "0xf000000000000000000000000000000000000000000000000000000000000002", "off": 0 }, "push": [], "store": null, - "used": 16493345 + "used": 16493347 }, "pc": 8, "sub": null @@ -306,7 +296,7 @@ "0x20" ], "store": null, - "used": 16493342 + "used": 16493344 }, "pc": 9, "sub": null @@ -319,7 +309,7 @@ "0x0" ], "store": null, - "used": 16493339 + "used": 16493341 }, "pc": 11, "sub": null @@ -330,7 +320,7 @@ "mem": null, "push": [], "store": null, - "used": 16493339 + "used": 16493341 }, "pc": 13, "sub": null @@ -346,9 +336,9 @@ "0x20" ], "store": null, - "used": 16755135 + "used": 16755137 }, - "pc": 22, + "pc": 21, "sub": null }, { @@ -359,9 +349,9 @@ "0x0" ], "store": null, - "used": 16755132 + "used": 16755134 }, - "pc": 24, + "pc": 23, "sub": null }, { @@ -370,9 +360,9 @@ "mem": null, "push": [], "store": null, - "used": 16755132 + "used": 16755134 }, - "pc": 26, + "pc": 25, "sub": null } ] diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/debug/TraceFrame.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/debug/TraceFrame.java index 96714e72581..a992e70d13e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/debug/TraceFrame.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/debug/TraceFrame.java @@ -37,9 +37,11 @@ public class TraceFrame { private final String opcode; private final Gas gasRemaining; private final Optional gasCost; + private final Gas gasRefund; private final int depth; private final EnumSet exceptionalHaltReasons; private final Address recipient; + private final Wei value; private final Bytes inputData; private final Bytes outputData; private final Optional stack; @@ -51,22 +53,23 @@ public class TraceFrame { private final Optional maybeCode; private final int stackItemsProduced; private final Optional stackPostExecution; - private final Optional memoryPostExecution; - private Optional maybeNextDepth; private Gas gasRemainingPostExecution; - private final Optional> storagePreExecution; private final boolean virtualOperation; private final Optional maybeUpdatedMemory; + private final Optional maybeUpdatedStorage; + private Optional precompiledGasCost; public TraceFrame( final int pc, final String opcode, final Gas gasRemaining, final Optional gasCost, + final Gas gasRefund, final int depth, final EnumSet exceptionalHaltReasons, final Address recipient, + final Wei value, final Bytes inputData, final Bytes outputData, final Optional stack, @@ -78,17 +81,18 @@ public TraceFrame( final Optional maybeCode, final int stackItemsProduced, final Optional stackPostExecution, - final Optional memoryPostExecution, - final Optional> storagePreExecution, final boolean virtualOperation, - final Optional maybeUpdatedMemory) { + final Optional maybeUpdatedMemory, + final Optional maybeUpdatedStorage) { this.pc = pc; this.opcode = opcode; this.gasRemaining = gasRemaining; this.gasCost = gasCost; + this.gasRefund = gasRefund; this.depth = depth; this.exceptionalHaltReasons = exceptionalHaltReasons; this.recipient = recipient; + this.value = value; this.inputData = inputData; this.outputData = outputData; this.stack = stack; @@ -100,11 +104,10 @@ public TraceFrame( this.maybeCode = maybeCode; this.stackItemsProduced = stackItemsProduced; this.stackPostExecution = stackPostExecution; - this.memoryPostExecution = memoryPostExecution; - this.maybeNextDepth = Optional.empty(); - this.storagePreExecution = storagePreExecution; this.virtualOperation = virtualOperation; this.maybeUpdatedMemory = maybeUpdatedMemory; + this.maybeUpdatedStorage = maybeUpdatedStorage; + precompiledGasCost = Optional.empty(); } public int getPc() { @@ -123,6 +126,10 @@ public Optional getGasCost() { return gasCost; } + public Gas getGasRefund() { + return gasRefund; + } + public int getDepth() { return depth; } @@ -135,6 +142,10 @@ public Address getRecipient() { return recipient; } + public Wei getValue() { + return value; + } + public Bytes getInputData() { return inputData; } @@ -194,34 +205,10 @@ public Optional getStackPostExecution() { return stackPostExecution; } - public Optional getMemoryPostExecution() { - return memoryPostExecution; - } - - public boolean depthHasIncreased() { - return maybeNextDepth.map(next -> next > depth).orElse(false); - } - - public boolean depthHasDecreased() { - return maybeNextDepth.map(next -> next < depth).orElse(false); - } - - public Optional getMaybeNextDepth() { - return maybeNextDepth; - } - - public void setMaybeNextDepth(final Optional maybeNextDepth) { - this.maybeNextDepth = maybeNextDepth; - } - public Gas getGasRemainingPostExecution() { return gasRemainingPostExecution; } - public Optional> getStoragePreExecution() { - return storagePreExecution; - } - public void setGasRemainingPostExecution(final Gas gasRemainingPostExecution) { this.gasRemainingPostExecution = gasRemainingPostExecution; } @@ -233,4 +220,16 @@ public boolean isVirtualOperation() { public Optional getMaybeUpdatedMemory() { return maybeUpdatedMemory; } + + public Optional getMaybeUpdatedStorage() { + return maybeUpdatedStorage; + } + + public Optional getPrecompiledGasCost() { + return precompiledGasCost; + } + + public void setPrecompiledGasCost(final Optional precompiledGasCost) { + this.precompiledGasCost = precompiledGasCost; + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractMessageProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractMessageProcessor.java index e52a1322238..3b0ccb8dcd6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractMessageProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractMessageProcessor.java @@ -42,7 +42,7 @@ * * * {@link MessageFrame.State#NOT_STARTED} - * {@link AbstractMessageProcessor#start(MessageFrame)} + * {@link AbstractMessageProcessor#start(MessageFrame, OperationTracer)} * * * {@link MessageFrame.State#CODE_EXECUTING} @@ -74,7 +74,7 @@ protected AbstractMessageProcessor( this.forceDeleteAccountsWhenEmpty = forceDeleteAccountsWhenEmpty; } - protected abstract void start(MessageFrame frame); + protected abstract void start(MessageFrame frame, final OperationTracer operationTracer); /** * Gets called when the message frame code executes successfully. @@ -163,7 +163,7 @@ private void codeExecute(final MessageFrame frame, final OperationTracer operati public void process(final MessageFrame frame, final OperationTracer operationTracer) { if (frame.getState() == MessageFrame.State.NOT_STARTED) { - start(frame); + start(frame, operationTracer); } if (frame.getState() == MessageFrame.State.CODE_EXECUTING) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetContractCreationProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetContractCreationProcessor.java index 712950082e2..b7dd4814c65 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetContractCreationProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetContractCreationProcessor.java @@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.vm.EVM; import org.hyperledger.besu.ethereum.vm.GasCalculator; import org.hyperledger.besu.ethereum.vm.MessageFrame; +import org.hyperledger.besu.ethereum.vm.OperationTracer; import java.util.Collection; import java.util.List; @@ -102,7 +103,7 @@ private static boolean accountExists(final Account account) { } @Override - public void start(final MessageFrame frame) { + public void start(final MessageFrame frame, final OperationTracer operationTracer) { if (LOG.isTraceEnabled()) { LOG.trace("Executing contract-creation"); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetMessageCallProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetMessageCallProcessor.java index d88b176d50c..c21ee2ebe09 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetMessageCallProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetMessageCallProcessor.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.vm.EVM; import org.hyperledger.besu.ethereum.vm.MessageFrame; +import org.hyperledger.besu.ethereum.vm.OperationTracer; import java.util.Collection; @@ -48,7 +49,7 @@ public MainnetMessageCallProcessor(final EVM evm, final PrecompileContractRegist } @Override - public void start(final MessageFrame frame) { + public void start(final MessageFrame frame, final OperationTracer operationTracer) { LOG.trace("Executing message-call"); try { transferValue(frame); @@ -57,7 +58,7 @@ public void start(final MessageFrame frame) { final PrecompiledContract precompile = precompiles.get(frame.getContractAddress(), frame.getContractAccountVersion()); if (precompile != null) { - executePrecompile(precompile, frame); + executePrecompile(precompile, frame, operationTracer); } else { frame.setState(MessageFrame.State.CODE_EXECUTING); } @@ -114,7 +115,10 @@ private void transferValue(final MessageFrame frame) { * * @param contract The contract this is a message call to. */ - private void executePrecompile(final PrecompiledContract contract, final MessageFrame frame) { + private void executePrecompile( + final PrecompiledContract contract, + final MessageFrame frame, + final OperationTracer operationTracer) { final Gas gasRequirement = contract.gasRequirement(frame.getInputData()); if (frame.getRemainingGas().compareTo(gasRequirement) < 0) { LOG.trace( @@ -127,6 +131,7 @@ private void executePrecompile(final PrecompiledContract contract, final Message } else { frame.decrementRemainingGas(gasRequirement); final Bytes output = contract.compute(frame.getInputData(), frame); + operationTracer.tracePrecompileCall(frame, gasRequirement, output); if (output != null) { if (contract.getName().equals("Privacy")) { // do not decrement the gas requirement for a privacy pre-compile contract call -> leads diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java index 047e417297c..f65a36a1aed 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/DebugOperationTracer.java @@ -61,19 +61,15 @@ public void traceExecution( EnumSet.copyOf(frame.getExceptionalHaltReasons()); final Bytes inputData = frame.getInputData(); final Optional stack = captureStack(frame); - final Optional> storagePreExecution = captureStorage(frame); final WorldUpdater worldUpdater = frame.getWorldState(); final Optional stackPostExecution; - final Optional memoryPostExecution; try { executeOperation.execute(); } finally { final Bytes outputData = frame.getOutputData(); final Optional memory = captureMemory(frame); stackPostExecution = captureStack(frame); - memoryPostExecution = captureMemory(frame); if (lastFrame != null) { - lastFrame.setMaybeNextDepth(Optional.of(depth)); lastFrame.setGasRemainingPostExecution(gasRemaining); } final Optional> storage = captureStorage(frame); @@ -85,9 +81,11 @@ public void traceExecution( opcode, gasRemaining, currentGasCost, + frame.getGasRefund(), depth, exceptionalHaltReasons, frame.getRecipientAddress(), + frame.getApparentValue(), inputData, outputData, stack, @@ -96,18 +94,23 @@ public void traceExecution( worldUpdater, frame.getRevertReason(), maybeRefunds, - Optional.ofNullable(frame.getCode()), + Optional.ofNullable(frame.getMessageFrameStack().peek()).map(MessageFrame::getCode), frame.getCurrentOperation().getStackItemsProduced(), stackPostExecution, - memoryPostExecution, - storagePreExecution, currentOperation.isVirtualOperation(), - frame.getMaybeUpdatedMemory()); + frame.getMaybeUpdatedMemory(), + frame.getMaybeUpdatedStorage()); traceFrames.add(lastFrame); } frame.reset(); } + @Override + public void tracePrecompileCall( + final MessageFrame frame, final Gas gasRequirement, final Bytes output) { + traceFrames.get(traceFrames.size() - 1).setPrecompiledGasCost(Optional.of(gasRequirement)); + } + private Optional> captureStorage(final MessageFrame frame) { if (!options.isStorageEnabled()) { return Optional.empty(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java index 3d8e1f9bd8c..411f7294b79 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java @@ -235,6 +235,7 @@ public enum Type { private Operation currentOperation; private final Consumer completer; private Optional maybeUpdatedMemory = Optional.empty(); + private Optional maybeUpdatedStorage = Optional.empty(); public static Builder builder() { return new Builder(); @@ -556,16 +557,6 @@ public Bytes readMemory( return value; } - /** - * Write byte to memory - * - * @param offset The offset in memory - * @param value The value to set in memory - */ - public void writeMemory(final UInt256 offset, final byte value) { - writeMemory(offset, value, false); - } - /** * Write byte to memory * @@ -668,6 +659,9 @@ private void setUpdatedMemory(final UInt256 offset, final Bytes value) { maybeUpdatedMemory = Optional.of(new MemoryEntry(offset, value)); } + public void storageWasUpdated(final UInt256 storageAddress, final Bytes value) { + maybeUpdatedStorage = Optional.of(new MemoryEntry(storageAddress, value)); + } /** * Accumulate a log. * @@ -978,8 +972,13 @@ Optional getMaybeUpdatedMemory() { return maybeUpdatedMemory; } + public Optional getMaybeUpdatedStorage() { + return maybeUpdatedStorage; + } + public void reset() { maybeUpdatedMemory = Optional.empty(); + maybeUpdatedStorage = Optional.empty(); } public static class Builder { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/OperationTracer.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/OperationTracer.java index e5c3c96cb83..05be408bdde 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/OperationTracer.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/OperationTracer.java @@ -19,6 +19,8 @@ import java.util.Optional; +import org.apache.tuweni.bytes.Bytes; + public interface OperationTracer { OperationTracer NO_TRACING = @@ -28,6 +30,9 @@ void traceExecution( MessageFrame frame, Optional currentGasCost, ExecuteOperation executeOperation) throws ExceptionalHaltException; + default void tracePrecompileCall( + final MessageFrame frame, final Gas gasRequirement, final Bytes output) {}; + interface ExecuteOperation { void execute() throws ExceptionalHaltException; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java index deb92d1c65f..04144cb748a 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/operations/SStoreOperation.java @@ -62,6 +62,7 @@ public void execute(final MessageFrame frame) { frame.incrementGasRefund(gasCalculator().calculateStorageRefundAmount(account, key, value)); account.setStorageValue(key, value); + frame.storageWasUpdated(key, value.toBytes()); } @Override From 39826b14231ef8cff7ca88589ed21972120b3e40 Mon Sep 17 00:00:00 2001 From: Karim T Date: Mon, 10 Feb 2020 21:13:21 +0100 Subject: [PATCH 05/38] [BOUNTY-2] Add NAT Docker Support (#368) * add docker detection Signed-off-by: Karim TAAM * add port mapping detection Signed-off-by: Karim TAAM * add tests and refactor ip detection Signed-off-by: Karim TAAM * clean RunnerBuilder Signed-off-by: Karim TAAM * clean useless modification Signed-off-by: Karim TAAM * spotless Signed-off-by: Karim TAAM * resolve tests issues Signed-off-by: Karim TAAM * streamline auto detection Signed-off-by: Ratan Rai Sur Co-authored-by: Abdelhamid Bakhta <45264458+abdelhamidbakhta@users.noreply.github.com> Co-authored-by: Ratan Rai Sur --- .../org/hyperledger/besu/RunnerBuilder.java | 8 +- .../hyperledger/besu/cli/BesuCommandTest.java | 5 +- .../org/hyperledger/besu/nat/NatMethod.java | 1 + .../org/hyperledger/besu/nat/NatService.java | 26 ++-- ...oDetection.java => NatMethodDetector.java} | 8 +- .../besu/nat/docker/DockerDetector.java | 40 ++++++ .../besu/nat/docker/DockerNatManager.java | 129 +++++++++++++++++ .../HostBasedIpDetector.java} | 29 ++-- .../besu/nat/docker/IpDetector.java | 23 ++++ .../hyperledger/besu/nat/NatServiceTest.java | 15 +- .../besu/nat/docker/DockerNatManagerTest.java | 130 ++++++++++++++++++ 11 files changed, 364 insertions(+), 50 deletions(-) rename nat/src/main/java/org/hyperledger/besu/nat/core/{NatMethodAutoDetection.java => NatMethodDetector.java} (82%) create mode 100644 nat/src/main/java/org/hyperledger/besu/nat/docker/DockerDetector.java create mode 100644 nat/src/main/java/org/hyperledger/besu/nat/docker/DockerNatManager.java rename nat/src/main/java/org/hyperledger/besu/nat/{core/AutoDetectionResult.java => docker/HostBasedIpDetector.java} (55%) create mode 100644 nat/src/main/java/org/hyperledger/besu/nat/docker/IpDetector.java create mode 100644 nat/src/test/java/org/hyperledger/besu/nat/docker/DockerNatManagerTest.java diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index a6c832bbe1c..ff65476fde9 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -91,6 +91,8 @@ import org.hyperledger.besu.nat.NatMethod; import org.hyperledger.besu.nat.NatService; import org.hyperledger.besu.nat.core.NatManager; +import org.hyperledger.besu.nat.docker.DockerDetector; +import org.hyperledger.besu.nat.docker.DockerNatManager; import org.hyperledger.besu.nat.manual.ManualNatManager; import org.hyperledger.besu.nat.upnp.UpnpNatManager; import org.hyperledger.besu.plugin.BesuPlugin; @@ -339,7 +341,6 @@ public Runner build() { .orElse(bannedNodes); final NatService natService = new NatService(buildNatManager(natMethod)); - final NetworkBuilder inactiveNetwork = (caps) -> new NoopP2PNetwork(); final NetworkBuilder activeNetwork = (caps) -> @@ -581,13 +582,16 @@ private Optional buildNatManager(final NatMethod natMethod) { final NatMethod detectedNatMethod = Optional.of(natMethod) .filter(not(isEqual(NatMethod.AUTO))) - .orElse(NatService.autoDetectNatMethod()); + .orElse(NatService.autoDetectNatMethod(new DockerDetector())); switch (detectedNatMethod) { case UPNP: return Optional.of(new UpnpNatManager()); case MANUAL: return Optional.of( new ManualNatManager(p2pAdvertisedHost, p2pListenPort, jsonRpcConfiguration.getPort())); + case DOCKER: + return Optional.of( + new DockerNatManager(p2pAdvertisedHost, p2pListenPort, jsonRpcConfiguration.getPort())); case NONE: default: return Optional.empty(); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 45128ef9751..5883dbc42e3 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -1392,6 +1392,9 @@ public void natMethodOptionIsParsedCorrectly() { parseCommand("--nat-method", "AUTO"); verify(mockRunnerBuilder).natMethod(eq(NatMethod.AUTO)); + parseCommand("--nat-method", "DOCKER"); + verify(mockRunnerBuilder).natMethod(eq(NatMethod.DOCKER)); + assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); } @@ -1404,7 +1407,7 @@ public void parsesInvalidNatMethodOptionsShouldFail() { assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()) .contains( - "Invalid value for option '--nat-method': expected one of [UPNP, MANUAL, AUTO, NONE] (case-insensitive) but was 'invalid'"); + "Invalid value for option '--nat-method': expected one of [UPNP, MANUAL, DOCKER, AUTO, NONE] (case-insensitive) but was 'invalid'"); } @Test diff --git a/nat/src/main/java/org/hyperledger/besu/nat/NatMethod.java b/nat/src/main/java/org/hyperledger/besu/nat/NatMethod.java index 5ef21c3daf2..2b72c68feb3 100644 --- a/nat/src/main/java/org/hyperledger/besu/nat/NatMethod.java +++ b/nat/src/main/java/org/hyperledger/besu/nat/NatMethod.java @@ -17,6 +17,7 @@ public enum NatMethod { UPNP, MANUAL, + DOCKER, AUTO, NONE; diff --git a/nat/src/main/java/org/hyperledger/besu/nat/NatService.java b/nat/src/main/java/org/hyperledger/besu/nat/NatService.java index 6f6f469f4f0..8fbbb10a3e9 100644 --- a/nat/src/main/java/org/hyperledger/besu/nat/NatService.java +++ b/nat/src/main/java/org/hyperledger/besu/nat/NatService.java @@ -14,15 +14,13 @@ */ package org.hyperledger.besu.nat; -import static com.google.common.base.Preconditions.checkNotNull; - -import org.hyperledger.besu.nat.core.AutoDetectionResult; import org.hyperledger.besu.nat.core.NatManager; -import org.hyperledger.besu.nat.core.NatMethodAutoDetection; +import org.hyperledger.besu.nat.core.NatMethodDetector; import org.hyperledger.besu.nat.core.domain.NatPortMapping; import org.hyperledger.besu.nat.core.domain.NatServiceType; import org.hyperledger.besu.nat.core.domain.NetworkProtocol; +import java.util.Arrays; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -185,20 +183,16 @@ private NatMethod retrieveNatMethod(final Optional natManager) { } /** - * Attempts to automatically detect the Nat method being used by the node. + * Attempts to automatically detect the Nat method by applying nat method detectors. Will return + * the first one that succeeds in its detection. * - * @param natMethodAutoDetections list of nat method auto detections + * @param natMethodDetectors list of nat method auto detections * @return a {@link NatMethod} equal to NONE if no Nat method has been detected automatically. */ - public static NatMethod autoDetectNatMethod( - final NatMethodAutoDetection... natMethodAutoDetections) { - checkNotNull(natMethodAutoDetections); - for (NatMethodAutoDetection autoDetection : natMethodAutoDetections) { - final AutoDetectionResult result = autoDetection.shouldBeThisNatMethod(); - if (result.isDetectedNatMethod()) { - return result.getNatMethod(); - } - } - return NatMethod.NONE; + public static NatMethod autoDetectNatMethod(final NatMethodDetector... natMethodDetectors) { + return Arrays.stream(natMethodDetectors) + .flatMap(natMethodDetector -> natMethodDetector.detect().stream()) + .findFirst() + .orElse(NatMethod.NONE); } } diff --git a/nat/src/main/java/org/hyperledger/besu/nat/core/NatMethodAutoDetection.java b/nat/src/main/java/org/hyperledger/besu/nat/core/NatMethodDetector.java similarity index 82% rename from nat/src/main/java/org/hyperledger/besu/nat/core/NatMethodAutoDetection.java rename to nat/src/main/java/org/hyperledger/besu/nat/core/NatMethodDetector.java index f3052a2c981..d39d648d3e0 100644 --- a/nat/src/main/java/org/hyperledger/besu/nat/core/NatMethodAutoDetection.java +++ b/nat/src/main/java/org/hyperledger/besu/nat/core/NatMethodDetector.java @@ -15,8 +15,12 @@ package org.hyperledger.besu.nat.core; +import org.hyperledger.besu.nat.NatMethod; + +import java.util.Optional; + @FunctionalInterface -public interface NatMethodAutoDetection { +public interface NatMethodDetector { - AutoDetectionResult shouldBeThisNatMethod(); + Optional detect(); } diff --git a/nat/src/main/java/org/hyperledger/besu/nat/docker/DockerDetector.java b/nat/src/main/java/org/hyperledger/besu/nat/docker/DockerDetector.java new file mode 100644 index 00000000000..b8ff5ab5f66 --- /dev/null +++ b/nat/src/main/java/org/hyperledger/besu/nat/docker/DockerDetector.java @@ -0,0 +1,40 @@ +/* + * 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.nat.docker; + +import org.hyperledger.besu.nat.NatMethod; +import org.hyperledger.besu.nat.core.NatMethodDetector; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; +import java.util.stream.Stream; + +public class DockerDetector implements NatMethodDetector { + + @Override + public Optional detect() { + try (Stream stream = Files.lines(Paths.get("/proc/1/cgroup"))) { + return stream + .filter(line -> line.contains("/docker")) + .findFirst() + .map(__ -> NatMethod.DOCKER); + } catch (IOException e) { + return Optional.empty(); + } + } +} diff --git a/nat/src/main/java/org/hyperledger/besu/nat/docker/DockerNatManager.java b/nat/src/main/java/org/hyperledger/besu/nat/docker/DockerNatManager.java new file mode 100644 index 00000000000..0c9a97df6a3 --- /dev/null +++ b/nat/src/main/java/org/hyperledger/besu/nat/docker/DockerNatManager.java @@ -0,0 +1,129 @@ +/* + * 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.nat.docker; + +import org.hyperledger.besu.nat.NatMethod; +import org.hyperledger.besu.nat.core.AbstractNatManager; +import org.hyperledger.besu.nat.core.domain.NatPortMapping; +import org.hyperledger.besu.nat.core.domain.NatServiceType; +import org.hyperledger.besu.nat.core.domain.NetworkProtocol; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * This class describes the behaviour of the Docker NAT manager. Docker Nat manager add support for + * Docker’s NAT implementation when Besu is being run from a Docker container + */ +public class DockerNatManager extends AbstractNatManager { + protected static final Logger LOG = LogManager.getLogger(); + + private static final String PORT_MAPPING_TAG = "HOST_PORT_"; + + private final IpDetector ipDetector; + + private final String internalAdvertisedHost; + private final int internalP2pPort; + private final int internalRpcHttpPort; + + private final List forwardedPorts; + + public DockerNatManager(final String advertisedHost, final int p2pPort, final int rpcHttpPort) { + this(new HostBasedIpDetector(), advertisedHost, p2pPort, rpcHttpPort); + } + + public DockerNatManager( + final IpDetector ipDetector, + final String advertisedHost, + final int p2pPort, + final int rpcHttpPort) { + super(NatMethod.DOCKER); + this.ipDetector = ipDetector; + this.internalAdvertisedHost = advertisedHost; + this.internalP2pPort = p2pPort; + this.internalRpcHttpPort = rpcHttpPort; + this.forwardedPorts = buildForwardedPorts(); + } + + private List buildForwardedPorts() { + try { + final String internalHost = queryLocalIPAddress().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + final String advertisedHost = + retrieveExternalIPAddress().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + return Arrays.asList( + new NatPortMapping( + NatServiceType.DISCOVERY, + NetworkProtocol.UDP, + internalHost, + advertisedHost, + internalP2pPort, + getExternalPort(internalP2pPort)), + new NatPortMapping( + NatServiceType.RLPX, + NetworkProtocol.TCP, + internalHost, + advertisedHost, + internalP2pPort, + getExternalPort(internalP2pPort)), + new NatPortMapping( + NatServiceType.JSON_RPC, + NetworkProtocol.TCP, + internalHost, + advertisedHost, + internalRpcHttpPort, + getExternalPort(internalRpcHttpPort))); + } catch (Exception e) { + LOG.warn("Failed to create forwarded port list", e); + } + return Collections.emptyList(); + } + + @Override + protected void doStart() { + LOG.info("Starting docker NAT manager."); + } + + @Override + protected void doStop() { + LOG.info("Stopping docker NAT manager."); + } + + @Override + protected CompletableFuture retrieveExternalIPAddress() { + return ipDetector + .detectExternalIp() + .map(CompletableFuture::completedFuture) + .orElse(CompletableFuture.completedFuture(internalAdvertisedHost)); + } + + @Override + public CompletableFuture> getPortMappings() { + return CompletableFuture.completedFuture(forwardedPorts); + } + + private int getExternalPort(final int defaultValue) { + return Optional.ofNullable(System.getenv(PORT_MAPPING_TAG + defaultValue)) + .map(Integer::valueOf) + .orElse(defaultValue); + } +} diff --git a/nat/src/main/java/org/hyperledger/besu/nat/core/AutoDetectionResult.java b/nat/src/main/java/org/hyperledger/besu/nat/docker/HostBasedIpDetector.java similarity index 55% rename from nat/src/main/java/org/hyperledger/besu/nat/core/AutoDetectionResult.java rename to nat/src/main/java/org/hyperledger/besu/nat/docker/HostBasedIpDetector.java index 22d76384b4b..b84a3d832b3 100644 --- a/nat/src/main/java/org/hyperledger/besu/nat/core/AutoDetectionResult.java +++ b/nat/src/main/java/org/hyperledger/besu/nat/docker/HostBasedIpDetector.java @@ -13,25 +13,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -package org.hyperledger.besu.nat.core; +package org.hyperledger.besu.nat.docker; -import org.hyperledger.besu.nat.NatMethod; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Optional; -public class AutoDetectionResult { +public class HostBasedIpDetector implements IpDetector { - private final NatMethod natMethod; - private final boolean isDetectedNatMethod; + private static final String HOSTNAME = "HOST_IP"; - public AutoDetectionResult(final NatMethod natMethod, final boolean isDetectedNatMethod) { - this.natMethod = natMethod; - this.isDetectedNatMethod = isDetectedNatMethod; - } - - public NatMethod getNatMethod() { - return natMethod; - } - - public boolean isDetectedNatMethod() { - return isDetectedNatMethod; + @Override + public Optional detectExternalIp() { + try { + return Optional.of(InetAddress.getByName(HOSTNAME).getHostAddress()); + } catch (final UnknownHostException e) { + return Optional.empty(); + } } } diff --git a/nat/src/main/java/org/hyperledger/besu/nat/docker/IpDetector.java b/nat/src/main/java/org/hyperledger/besu/nat/docker/IpDetector.java new file mode 100644 index 00000000000..373e1e67e12 --- /dev/null +++ b/nat/src/main/java/org/hyperledger/besu/nat/docker/IpDetector.java @@ -0,0 +1,23 @@ +/* + * 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.nat.docker; + +import java.util.Optional; + +public interface IpDetector { + + Optional detectExternalIp(); +} diff --git a/nat/src/test/java/org/hyperledger/besu/nat/NatServiceTest.java b/nat/src/test/java/org/hyperledger/besu/nat/NatServiceTest.java index efd80d3e688..46735471469 100644 --- a/nat/src/test/java/org/hyperledger/besu/nat/NatServiceTest.java +++ b/nat/src/test/java/org/hyperledger/besu/nat/NatServiceTest.java @@ -20,7 +20,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.hyperledger.besu.nat.core.AutoDetectionResult; import org.hyperledger.besu.nat.core.NatManager; import org.hyperledger.besu.nat.core.domain.NatPortMapping; import org.hyperledger.besu.nat.core.domain.NatServiceType; @@ -150,23 +149,13 @@ public void assertThatQueryLocalIPAddressWorksProperlyWithoutNat() { @Test public void givenOneAutoDetectionWorksWhenAutoDetectThenReturnCorrectNatMethod() { - final NatMethod natMethod = - NatService.autoDetectNatMethod(NatServiceTest::alwaysTrueShouldBeUpnpMethod); + final NatMethod natMethod = NatService.autoDetectNatMethod(() -> Optional.of(NatMethod.UPNP)); assertThat(natMethod).isEqualTo(NatMethod.UPNP); } @Test public void givenNoAutoDetectionWorksWhenAutoDetectThenReturnEmptyNatMethod() { - final NatMethod natMethod = - NatService.autoDetectNatMethod(NatServiceTest::alwaysFalseShouldBeUpnpMethod); + final NatMethod natMethod = NatService.autoDetectNatMethod(Optional::empty); assertThat(natMethod).isEqualTo(NatMethod.NONE); } - - private static AutoDetectionResult alwaysTrueShouldBeUpnpMethod() { - return new AutoDetectionResult(NatMethod.UPNP, true); - } - - private static AutoDetectionResult alwaysFalseShouldBeUpnpMethod() { - return new AutoDetectionResult(NatMethod.UPNP, false); - } } diff --git a/nat/src/test/java/org/hyperledger/besu/nat/docker/DockerNatManagerTest.java b/nat/src/test/java/org/hyperledger/besu/nat/docker/DockerNatManagerTest.java new file mode 100644 index 00000000000..9a99b8c2b40 --- /dev/null +++ b/nat/src/test/java/org/hyperledger/besu/nat/docker/DockerNatManagerTest.java @@ -0,0 +1,130 @@ +/* + * 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.nat.docker; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.nat.core.domain.NatPortMapping; +import org.hyperledger.besu.nat.core.domain.NatServiceType; +import org.hyperledger.besu.nat.core.domain.NetworkProtocol; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; + +public final class DockerNatManagerTest { + + private final String advertisedHost = "99.45.69.12"; + private final String detectedAdvertisedHost = "199.45.69.12"; + + private final int p2pPort = 1; + private final int rpcHttpPort = 2; + + @Mock private HostBasedIpDetector hostBasedIpDetector; + + private DockerNatManager natManager; + + @Before + public void initialize() { + hostBasedIpDetector = mock(HostBasedIpDetector.class); + when(hostBasedIpDetector.detectExternalIp()).thenReturn(Optional.of(detectedAdvertisedHost)); + natManager = new DockerNatManager(hostBasedIpDetector, advertisedHost, p2pPort, rpcHttpPort); + natManager.start(); + } + + @Test + public void assertThatExternalIPIsEqualToRemoteHost() + throws ExecutionException, InterruptedException { + assertThat(natManager.queryExternalIPAddress().get()).isEqualTo(detectedAdvertisedHost); + } + + @Test + public void assertThatExternalIPIsEqualToDefaultHostIfIpDetectorCannotRetrieveIP() + throws ExecutionException, InterruptedException { + when(hostBasedIpDetector.detectExternalIp()).thenReturn(Optional.empty()); + assertThat(natManager.queryExternalIPAddress().get()).isEqualTo(advertisedHost); + } + + @Test + public void assertThatLocalIPIsEqualToLocalHost() + throws ExecutionException, InterruptedException, UnknownHostException { + final String internalHost = InetAddress.getLocalHost().getHostAddress(); + assertThat(natManager.queryLocalIPAddress().get()).isEqualTo(internalHost); + } + + @Test + public void assertThatMappingForDiscoveryWorks() throws UnknownHostException { + final String internalHost = InetAddress.getLocalHost().getHostAddress(); + + final NatPortMapping mapping = + natManager.getPortMapping(NatServiceType.DISCOVERY, NetworkProtocol.UDP); + + final NatPortMapping expectedMapping = + new NatPortMapping( + NatServiceType.DISCOVERY, + NetworkProtocol.UDP, + internalHost, + detectedAdvertisedHost, + p2pPort, + p2pPort); + + assertThat(mapping).isEqualToComparingFieldByField(expectedMapping); + } + + @Test + public void assertThatMappingForJsonRpcWorks() throws UnknownHostException { + final String internalHost = InetAddress.getLocalHost().getHostAddress(); + + final NatPortMapping mapping = + natManager.getPortMapping(NatServiceType.JSON_RPC, NetworkProtocol.TCP); + + final NatPortMapping expectedMapping = + new NatPortMapping( + NatServiceType.JSON_RPC, + NetworkProtocol.TCP, + internalHost, + detectedAdvertisedHost, + rpcHttpPort, + rpcHttpPort); + + assertThat(mapping).isEqualToComparingFieldByField(expectedMapping); + } + + @Test + public void assertThatMappingForRlpxWorks() throws UnknownHostException { + final String internalHost = InetAddress.getLocalHost().getHostAddress(); + + final NatPortMapping mapping = + natManager.getPortMapping(NatServiceType.RLPX, NetworkProtocol.TCP); + + final NatPortMapping expectedMapping = + new NatPortMapping( + NatServiceType.RLPX, + NetworkProtocol.TCP, + internalHost, + detectedAdvertisedHost, + p2pPort, + p2pPort); + + assertThat(mapping).isEqualToComparingFieldByField(expectedMapping); + } +} From d25297443e7327139acf9634ef4ab0a4544ee59e Mon Sep 17 00:00:00 2001 From: mark-terry <36909937+mark-terry@users.noreply.github.com> Date: Tue, 11 Feb 2020 12:19:44 +1000 Subject: [PATCH 06/38] [PIE-1798] Priv RPC acceptance tests with stub enclave. (#330) * [PIE-1798] Added some Privacy RPC ATs with a stub enclave. Signed-off-by: Mark Terry --- .../acceptance/dsl/AcceptanceTestBase.java | 6 + .../priv/EeaSendRawTransactionSuccess.java | 38 +++ .../dsl/condition/priv/PrivConditions.java | 93 ++++++ .../priv/PrivCreatePrivacyGroupSuccess.java | 39 +++ .../priv/PrivDeletePrivacyGroupSuccess.java | 39 +++ .../PrivDistributeRawTransactionSuccess.java | 42 +++ .../priv/PrivFindPrivacyGroupSuccess.java | 40 +++ .../PrivGetEeaTransactionCountSuccess.java | 42 +++ ...rivGetPrivacyPrecompileAddressSuccess.java | 42 +++ .../PrivGetPrivateTransactionSuccess.java | 43 +++ .../priv/PrivGetTransactionCountSuccess.java | 42 +++ .../PrivGetTransactionReceiptSuccess.java | 38 +++ .../tests/acceptance/dsl/node/BesuNode.java | 4 +- .../dsl/node/ProcessBesuNodeRunner.java | 11 +- .../configuration/BesuNodeConfiguration.java | 10 +- .../BesuNodeConfigurationBuilder.java | 23 +- .../node/configuration/BesuNodeFactory.java | 48 ++- .../acceptance/dsl/privacy/PrivacyNode.java | 5 +- .../PrivateTransactionGroupResponse.java | 123 +++++++ .../EeaSendRawTransactionTransaction.java | 44 +++ .../PrivCreatePrivacyGroupTransaction.java | 46 +++ .../PrivDeletePrivacyGroupTransaction.java | 43 +++ ...ivDistributeRawTransactionTransaction.java | 43 +++ .../PrivFindPrivacyGroupTransaction.java | 44 +++ ...PrivGetEeaTransactionCountTransaction.java | 44 +++ ...etPrivacyPrecompileAddressTransaction.java | 38 +++ .../PrivGetPrivateTransactionTransaction.java | 46 +++ .../PrivGetTransactionCountTransaction.java | 44 +++ .../PrivGetTransactionReceiptTransaction.java | 47 +++ .../privacy/PrivacyRequestFactory.java | 132 +++++++- .../privacy/PrivacyTransactions.java | 65 ++++ acceptance-tests/tests/build.gradle | 4 + .../HttpServiceLoginAcceptanceTest.java | 4 +- .../WebsocketServiceLoginAcceptanceTest.java | 3 +- .../MultiTenancyAcceptanceTest.java | 312 ++++++++++++++++++ .../resources/authentication/auth_priv.toml | 4 + .../resources/authentication/auth_priv_key | 1 + .../resources/authentication/auth_pub_key | 1 + 38 files changed, 1669 insertions(+), 24 deletions(-) create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/EeaSendRawTransactionSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivCreatePrivacyGroupSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivDeletePrivacyGroupSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivDistributeRawTransactionSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivFindPrivacyGroupSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetEeaTransactionCountSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetPrivacyPrecompileAddressSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetPrivateTransactionSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetTransactionCountSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetTransactionReceiptSuccess.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivateTransactionGroupResponse.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/EeaSendRawTransactionTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivCreatePrivacyGroupTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivDeletePrivacyGroupTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivDistributeRawTransactionTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivFindPrivacyGroupTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetEeaTransactionCountTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetPrivacyPrecompileAddressTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetPrivateTransactionTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetTransactionCountTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetTransactionReceiptTransaction.java create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyTransactions.java create mode 100644 acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java create mode 100644 acceptance-tests/tests/src/test/resources/authentication/auth_priv.toml create mode 100644 acceptance-tests/tests/src/test/resources/authentication/auth_priv_key create mode 100644 acceptance-tests/tests/src/test/resources/authentication/auth_pub_key diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java index 02b30759ec4..e11349023c2 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.condition.login.LoginConditions; import org.hyperledger.besu.tests.acceptance.dsl.condition.net.NetConditions; import org.hyperledger.besu.tests.acceptance.dsl.condition.perm.PermissioningConditions; +import org.hyperledger.besu.tests.acceptance.dsl.condition.priv.PrivConditions; import org.hyperledger.besu.tests.acceptance.dsl.condition.web3.Web3Conditions; import org.hyperledger.besu.tests.acceptance.dsl.contract.ContractVerifier; import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster; @@ -37,6 +38,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerTransactions; import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.NetTransactions; import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningTransactions; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyTransactions; import org.hyperledger.besu.tests.acceptance.dsl.transaction.web3.Web3Transactions; import org.junit.After; @@ -65,6 +67,8 @@ public class AcceptanceTestBase { protected final PermissioningTransactions permissioningTransactions; protected final MinerTransactions minerTransactions; protected final Web3Conditions web3; + protected final PrivConditions priv; + protected final PrivacyTransactions privacyTransactions; protected AcceptanceTestBase() { ethTransactions = new EthTransactions(); @@ -74,6 +78,7 @@ protected AcceptanceTestBase() { ibftTwoTransactions = new Ibft2Transactions(); accountTransactions = new AccountTransactions(accounts); permissioningTransactions = new PermissioningTransactions(); + privacyTransactions = new PrivacyTransactions(); contractTransactions = new ContractTransactions(); minerTransactions = new MinerTransactions(); @@ -85,6 +90,7 @@ protected AcceptanceTestBase() { net = new NetConditions(new NetTransactions()); cluster = new Cluster(net); perm = new PermissioningConditions(permissioningTransactions); + priv = new PrivConditions(privacyTransactions); admin = new AdminConditions(adminTransactions); web3 = new Web3Conditions(new Web3Transactions()); besu = new BesuNodeFactory(); diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/EeaSendRawTransactionSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/EeaSendRawTransactionSuccess.java new file mode 100644 index 00000000000..a80cbb0007b --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/EeaSendRawTransactionSuccess.java @@ -0,0 +1,38 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.EeaSendRawTransactionTransaction; + +public class EeaSendRawTransactionSuccess implements Condition { + + private final EeaSendRawTransactionTransaction sendRawTransactionTransaction; + + public EeaSendRawTransactionSuccess( + final EeaSendRawTransactionTransaction sendRawTransactionTransaction) { + this.sendRawTransactionTransaction = sendRawTransactionTransaction; + } + + @Override + public void verify(final Node node) { + final Hash transactionHash = node.execute(sendRawTransactionTransaction); + assertThat(transactionHash).isNotNull(); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java new file mode 100644 index 00000000000..a763eab6957 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java @@ -0,0 +1,93 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyTransactions; + +import java.util.List; + +public class PrivConditions { + + private final PrivacyTransactions transactions; + + public PrivConditions(final PrivacyTransactions transactions) { + this.transactions = transactions; + } + + public Condition getPrivacyPrecompileAddress(final Address precompileAddress) { + return new PrivGetPrivacyPrecompileAddressSuccess( + transactions.getPrivacyPrecompileAddress(), precompileAddress); + } + + public Condition getPrivateTransaction( + final Hash transactionHash, final PrivateTransaction privateTransaction) { + return new PrivGetPrivateTransactionSuccess( + transactions.getPrivateTransaction(transactionHash), privateTransaction); + } + + public Condition createPrivacyGroup( + final List addresses, + final String groupName, + final String groupDescription, + final String groupId) { + return new PrivCreatePrivacyGroupSuccess( + transactions.createPrivacyGroup(addresses, groupName, groupDescription), groupId); + } + + public Condition deletePrivacyGroup(final String groupId) { + return new PrivDeletePrivacyGroupSuccess(transactions.deletePrivacyGroup(groupId), groupId); + } + + public Condition findPrivacyGroup(final int numGroups, final String... groupMembers) { + return new PrivFindPrivacyGroupSuccess(transactions.findPrivacyGroup(groupMembers), numGroups); + } + + public Condition eeaSendRawTransaction(final String transaction) { + return new EeaSendRawTransactionSuccess(transactions.sendRawTransaction(transaction)); + } + + public Condition distributeRawTransaction( + final String transactionRLP, final String enclaveResponseKey) { + return new PrivDistributeRawTransactionSuccess( + transactions.distributeRawTransaction(transactionRLP), enclaveResponseKey); + } + + public Condition getTransactionCount( + final String accountAddress, + final String privacyGroupId, + final int expectedTransactionCount) { + return new PrivGetTransactionCountSuccess( + transactions.getTransactionCount(accountAddress, privacyGroupId), expectedTransactionCount); + } + + public Condition getEeaTransactionCount( + final String accountAddress, + final String privateFrom, + final String[] privateFor, + final int expectedTransactionCount) { + return new PrivGetEeaTransactionCountSuccess( + transactions.getEeaTransactionCount(accountAddress, privateFrom, privateFor), + expectedTransactionCount); + } + + public Condition getTransactionReceipt(final Hash transactionHash) { + return new PrivGetTransactionReceiptSuccess( + transactions.getTransactionReceipt(transactionHash)); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivCreatePrivacyGroupSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivCreatePrivacyGroupSuccess.java new file mode 100644 index 00000000000..c507ca4b50f --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivCreatePrivacyGroupSuccess.java @@ -0,0 +1,39 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivCreatePrivacyGroupTransaction; + +public class PrivCreatePrivacyGroupSuccess implements Condition { + + private final PrivCreatePrivacyGroupTransaction transaction; + private final String groupId; + + public PrivCreatePrivacyGroupSuccess( + final PrivCreatePrivacyGroupTransaction transaction, final String groupId) { + this.transaction = transaction; + this.groupId = groupId; + } + + @Override + public void verify(final Node node) { + final String result = node.execute(transaction); + assertThat(result).isEqualTo(groupId); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivDeletePrivacyGroupSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivDeletePrivacyGroupSuccess.java new file mode 100644 index 00000000000..b2cd4a40ce5 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivDeletePrivacyGroupSuccess.java @@ -0,0 +1,39 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivDeletePrivacyGroupTransaction; + +public class PrivDeletePrivacyGroupSuccess implements Condition { + + private final PrivDeletePrivacyGroupTransaction transaction; + private final String groupId; + + public PrivDeletePrivacyGroupSuccess( + final PrivDeletePrivacyGroupTransaction transaction, final String groupId) { + this.transaction = transaction; + this.groupId = groupId; + } + + @Override + public void verify(final Node node) { + final String result = node.execute(transaction); + assertThat(result).isEqualTo(groupId); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivDistributeRawTransactionSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivDistributeRawTransactionSuccess.java new file mode 100644 index 00000000000..4184102cf5a --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivDistributeRawTransactionSuccess.java @@ -0,0 +1,42 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivDistributeRawTransactionTransaction; + +public class PrivDistributeRawTransactionSuccess implements Condition { + + private final PrivDistributeRawTransactionTransaction sendRawTransactionTransaction; + private final String enclaveResponseKey; + + public PrivDistributeRawTransactionSuccess( + final PrivDistributeRawTransactionTransaction sendRawTransactionTransaction, + final String enclaveResponseKey) { + this.sendRawTransactionTransaction = sendRawTransactionTransaction; + this.enclaveResponseKey = enclaveResponseKey; + } + + @Override + public void verify(final Node node) { + final String result = node.execute(sendRawTransactionTransaction); + assertThat(result).isNotNull(); + assertThat(result).isInstanceOf(String.class); + assertThat(result).isEqualTo(enclaveResponseKey); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivFindPrivacyGroupSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivFindPrivacyGroupSuccess.java new file mode 100644 index 00000000000..f4a069a243f --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivFindPrivacyGroupSuccess.java @@ -0,0 +1,40 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import org.hyperledger.besu.enclave.types.PrivacyGroup; +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivFindPrivacyGroupTransaction; + +public class PrivFindPrivacyGroupSuccess implements Condition { + + private final PrivFindPrivacyGroupTransaction transaction; + private final int numGroups; + + public PrivFindPrivacyGroupSuccess( + final PrivFindPrivacyGroupTransaction transaction, final int numGroups) { + this.transaction = transaction; + this.numGroups = numGroups; + } + + @Override + public void verify(final Node node) { + final PrivacyGroup[] privacyGroups = node.execute(transaction); + assertThat(privacyGroups).hasSize(numGroups); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetEeaTransactionCountSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetEeaTransactionCountSuccess.java new file mode 100644 index 00000000000..def5502d98c --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetEeaTransactionCountSuccess.java @@ -0,0 +1,42 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivGetEeaTransactionCountTransaction; + +public class PrivGetEeaTransactionCountSuccess implements Condition { + + private final PrivGetEeaTransactionCountTransaction privGetEeaTransactionCountTransaction; + private final int expectedTransactionCount; + + public PrivGetEeaTransactionCountSuccess( + final PrivGetEeaTransactionCountTransaction privGetEeaTransactionCountTransaction, + final int expectedTransactionCount) { + this.privGetEeaTransactionCountTransaction = privGetEeaTransactionCountTransaction; + this.expectedTransactionCount = expectedTransactionCount; + } + + @Override + public void verify(final Node node) { + final int result = node.execute(privGetEeaTransactionCountTransaction); + assertThat(result).isNotNull(); + assertThat(result).isInstanceOf(Integer.class); + assertThat(result).isEqualTo(expectedTransactionCount); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetPrivacyPrecompileAddressSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetPrivacyPrecompileAddressSuccess.java new file mode 100644 index 00000000000..8b904965810 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetPrivacyPrecompileAddressSuccess.java @@ -0,0 +1,42 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivGetPrivacyPrecompileAddressTransaction; + +public class PrivGetPrivacyPrecompileAddressSuccess implements Condition { + + private final PrivGetPrivacyPrecompileAddressTransaction transaction; + private final Address precompileAddress; + + public PrivGetPrivacyPrecompileAddressSuccess( + final PrivGetPrivacyPrecompileAddressTransaction transaction, + final Address precompileAddress) { + this.transaction = transaction; + this.precompileAddress = precompileAddress; + } + + @Override + public void verify(final Node node) { + final Address result = node.execute(transaction); + assertThat(result).isInstanceOf(Address.class); + assertThat(result).isEqualTo(precompileAddress); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetPrivateTransactionSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetPrivateTransactionSuccess.java new file mode 100644 index 00000000000..33383a054cf --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetPrivateTransactionSuccess.java @@ -0,0 +1,43 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.privacy.PrivateTransactionGroupResult; +import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivateTransactionGroupResponse; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivGetPrivateTransactionTransaction; + +public class PrivGetPrivateTransactionSuccess implements Condition { + + private final PrivGetPrivateTransactionTransaction transaction; + private final PrivateTransactionGroupResult privateTransaction; + + public PrivGetPrivateTransactionSuccess( + final PrivGetPrivateTransactionTransaction transaction, + final PrivateTransaction privateTransaction) { + this.transaction = transaction; + this.privateTransaction = new PrivateTransactionGroupResult(privateTransaction); + } + + @Override + public void verify(final Node node) { + final PrivateTransactionGroupResponse result = node.execute(transaction); + assertThat(result).isEqualToComparingFieldByField(privateTransaction); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetTransactionCountSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetTransactionCountSuccess.java new file mode 100644 index 00000000000..823548effaf --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetTransactionCountSuccess.java @@ -0,0 +1,42 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivGetTransactionCountTransaction; + +public class PrivGetTransactionCountSuccess implements Condition { + + private final PrivGetTransactionCountTransaction privGetTransactionCountTransaction; + private final int expectedTransactionCount; + + public PrivGetTransactionCountSuccess( + final PrivGetTransactionCountTransaction privGetTransactionCountTransaction, + final int expectedTransactionCount) { + this.privGetTransactionCountTransaction = privGetTransactionCountTransaction; + this.expectedTransactionCount = expectedTransactionCount; + } + + @Override + public void verify(final Node node) { + final Integer result = node.execute(privGetTransactionCountTransaction); + assertThat(result).isNotNull(); + assertThat(result).isInstanceOf(Integer.class); + assertThat(result).isEqualTo(expectedTransactionCount); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetTransactionReceiptSuccess.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetTransactionReceiptSuccess.java new file mode 100644 index 00000000000..aee12a8ab98 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetTransactionReceiptSuccess.java @@ -0,0 +1,38 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.WaitUtils; +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivGetTransactionReceiptTransaction; + +public class PrivGetTransactionReceiptSuccess implements Condition { + + private final PrivGetTransactionReceiptTransaction getTransactionReceiptTransaction; + + public PrivGetTransactionReceiptSuccess( + final PrivGetTransactionReceiptTransaction getTransactionReceiptTransaction) { + this.getTransactionReceiptTransaction = getTransactionReceiptTransaction; + } + + @Override + public void verify(final Node node) { + WaitUtils.waitFor(() -> assertThat(node.execute(getTransactionReceiptTransaction)).isNotNull()); + assertThat(node.execute(getTransactionReceiptTransaction).getStatus()).isEqualTo("0x1"); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java index 2840ba28620..a55e6df9924 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java @@ -123,7 +123,8 @@ public BesuNode( final boolean revertReasonEnabled, final List plugins, final List extraCLIOptions, - final List staticNodes) + final List staticNodes, + final Optional privacyParameters) throws IOException { this.bootnodeEligible = bootnodeEligible; this.revertReasonEnabled = revertReasonEnabled; @@ -161,6 +162,7 @@ public BesuNode( }); this.extraCLIOptions = extraCLIOptions; this.staticNodes = staticNodes; + privacyParameters.ifPresent(this::setPrivacyParameters); LOG.info("Created BesuNode {}", this.toString()); } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java index e46a97c9aa3..ae27c30e130 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java @@ -91,6 +91,9 @@ public void startNode(final BesuNode node) { params.add(Integer.toString(node.getMiningParameters().getStratumPort())); params.add("--miner-stratum-host"); params.add(node.getMiningParameters().getStratumNetworkInterface()); + params.add("--min-gas-price"); + params.add( + Integer.toString(node.getMiningParameters().getMinTransactionGasPrice().intValue())); } if (node.getMiningParameters().isStratumMiningEnabled()) { params.add("--miner-stratum-enabled"); @@ -100,8 +103,12 @@ public void startNode(final BesuNode node) { params.add("--privacy-enabled"); params.add("--privacy-url"); params.add(node.getPrivacyParameters().getEnclaveUri().toString()); - params.add("--privacy-public-key-file"); - params.add(node.getPrivacyParameters().getEnclavePublicKeyFile().getAbsolutePath()); + if (node.getPrivacyParameters().isMultiTenancyEnabled()) { + params.add("--privacy-multi-tenancy-enabled"); + } else { + params.add("--privacy-public-key-file"); + params.add(node.getPrivacyParameters().getEnclavePublicKeyFile().getAbsolutePath()); + } params.add("--privacy-precompiled-address"); params.add(String.valueOf(node.getPrivacyParameters().getPrivacyAddress())); params.add("--privacy-marker-transaction-signing-key-file"); diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java index 3b7e208e1ef..a2c78ca3b21 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; import org.hyperledger.besu.ethereum.core.MiningParameters; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; @@ -44,6 +45,7 @@ public class BesuNodeConfiguration { private final List plugins; private final List extraCLIOptions; private final List staticNodes; + private final Optional privacyParameters; public BesuNodeConfiguration( final String name, @@ -62,7 +64,8 @@ public BesuNodeConfiguration( final boolean revertReasonEnabled, final List plugins, final List extraCLIOptions, - final List staticNodes) { + final List staticNodes, + final Optional privacyParameters) { this.name = name; this.miningParameters = miningParameters; this.jsonRpcConfiguration = jsonRpcConfiguration; @@ -80,6 +83,7 @@ public BesuNodeConfiguration( this.plugins = plugins; this.extraCLIOptions = extraCLIOptions; this.staticNodes = staticNodes; + this.privacyParameters = privacyParameters; } public String getName() { @@ -149,4 +153,8 @@ public boolean isRevertReasonEnabled() { public List getStaticNodes() { return staticNodes; } + + public Optional getPrivacyParameters() { + return privacyParameters; + } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java index 3608b434762..a5867bd793a 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.MiningParametersTestBuilder; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; @@ -54,6 +55,7 @@ public class BesuNodeConfigurationBuilder { private final List plugins = new ArrayList<>(); private final List extraCLIOptions = new ArrayList<>(); private List staticNodes = new ArrayList<>(); + private Optional privacyParameters = Optional.empty(); public BesuNodeConfigurationBuilder() { // Check connections more frequently during acceptance tests to cut down on @@ -72,6 +74,12 @@ public BesuNodeConfigurationBuilder miningEnabled() { return this; } + public BesuNodeConfigurationBuilder miningConfiguration(final MiningParameters miningParameters) { + this.miningParameters = miningParameters; + this.jsonRpcConfiguration.addRpcApi(RpcApis.MINER); + return this; + } + public BesuNodeConfigurationBuilder jsonRpcConfiguration( final JsonRpcConfiguration jsonRpcConfiguration) { this.jsonRpcConfiguration = jsonRpcConfiguration; @@ -103,11 +111,10 @@ public BesuNodeConfigurationBuilder enablePrivateTransactions() { return this; } - public BesuNodeConfigurationBuilder jsonRpcAuthenticationEnabled() throws URISyntaxException { + public BesuNodeConfigurationBuilder jsonRpcAuthenticationConfiguration(final String authFile) + throws URISyntaxException { final String authTomlPath = - Paths.get(ClassLoader.getSystemResource("authentication/auth.toml").toURI()) - .toAbsolutePath() - .toString(); + Paths.get(ClassLoader.getSystemResource(authFile).toURI()).toAbsolutePath().toString(); this.jsonRpcConfiguration.setAuthenticationEnabled(true); this.jsonRpcConfiguration.setAuthenticationCredentialsFile(authTomlPath); @@ -234,6 +241,11 @@ public BesuNodeConfigurationBuilder staticNodes(final List staticNodes) return this; } + public BesuNodeConfigurationBuilder privacyParameters(final PrivacyParameters privacyParameters) { + this.privacyParameters = Optional.ofNullable(privacyParameters); + return this; + } + public BesuNodeConfiguration build() { return new BesuNodeConfiguration( name, @@ -252,6 +264,7 @@ public BesuNodeConfiguration build() { revertReasonEnabled, plugins, extraCLIOptions, - staticNodes); + staticNodes, + privacyParameters); } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java index 1707e047a35..820a25aad02 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java @@ -17,9 +17,15 @@ import static java.util.Arrays.asList; import static java.util.stream.Collectors.toList; +import org.hyperledger.besu.enclave.EnclaveFactory; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration; +import org.hyperledger.besu.ethereum.core.InMemoryPrivacyStorageProvider; +import org.hyperledger.besu.ethereum.core.MiningParameters; +import org.hyperledger.besu.ethereum.core.MiningParametersTestBuilder; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; +import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; import org.hyperledger.besu.tests.acceptance.dsl.node.Node; import org.hyperledger.besu.tests.acceptance.dsl.node.RunnableNode; @@ -28,9 +34,12 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Paths; import java.util.List; import java.util.Optional; +import io.vertx.core.Vertx; + public class BesuNodeFactory { private final GenesisConfigurationFactory genesis = new GenesisConfigurationFactory(); @@ -54,7 +63,8 @@ public BesuNode create(final BesuNodeConfiguration config) throws IOException { config.isRevertReasonEnabled(), config.getPlugins(), config.getExtraCLIOptions(), - config.getStaticNodes()); + config.getStaticNodes(), + config.getPrivacyParameters()); } public BesuNode createMinerNode(final String name) throws IOException { @@ -128,13 +138,13 @@ public BesuNode createArchiveNodeNetServicesDisabled(final String name) throws I .build()); } - public BesuNode createNodeWithAuthentication(final String name) + public BesuNode createNodeWithAuthentication(final String name, final String authFile) throws IOException, URISyntaxException { return create( new BesuNodeConfigurationBuilder() .name(name) .jsonRpcEnabled() - .jsonRpcAuthenticationEnabled() + .jsonRpcAuthenticationConfiguration(authFile) .webSocketEnabled() .webSocketAuthenticationEnabled() .build()); @@ -161,6 +171,38 @@ public BesuNode createNodeWithP2pDisabled(final String name) throws IOException .build()); } + public BesuNode createNodeWithMultiTenantedPrivacy( + final String name, + final String enclaveUrl, + final String authFile, + final String privTransactionSigningKey) + throws IOException, URISyntaxException { + final PrivacyParameters.Builder privacyParametersBuilder = new PrivacyParameters.Builder(); + final PrivacyParameters privacyParameters = + privacyParametersBuilder + .setMultiTenancyEnabled(true) + .setEnabled(true) + .setStorageProvider(new InMemoryPrivacyStorageProvider()) + .setEnclaveFactory(new EnclaveFactory(Vertx.vertx())) + .setEnclaveUrl(URI.create(enclaveUrl)) + .setPrivateKeyPath( + Paths.get(ClassLoader.getSystemResource(privTransactionSigningKey).toURI())) + .build(); + + final MiningParameters miningParameters = + new MiningParametersTestBuilder().minTransactionGasPrice(Wei.ZERO).enabled(true).build(); + + return create( + new BesuNodeConfigurationBuilder() + .name(name) + .jsonRpcEnabled() + .jsonRpcAuthenticationConfiguration(authFile) + .enablePrivateTransactions() + .privacyParameters(privacyParameters) + .miningConfiguration(miningParameters) + .build()); + } + public BesuNode createArchiveNodeWithRpcDisabled(final String name) throws IOException { return create(new BesuNodeConfigurationBuilder().name(name).build()); } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java index ddd8746607e..3459562b1be 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java @@ -48,8 +48,8 @@ import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -96,7 +96,8 @@ public PrivacyNode(final PrivacyNodeConfiguration privacyConfiguration, final Ve besuConfig.isRevertReasonEnabled(), besuConfig.getPlugins(), besuConfig.getExtraCLIOptions(), - new ArrayList<>()); + Collections.emptyList(), + besuConfig.getPrivacyParameters()); } public void testOrionConnection(final List otherNodes) { diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivateTransactionGroupResponse.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivateTransactionGroupResponse.java new file mode 100644 index 00000000000..f3a139b0ec4 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivateTransactionGroupResponse.java @@ -0,0 +1,123 @@ +/* + * 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.tests.acceptance.dsl.privacy; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class PrivateTransactionGroupResponse { + private final String from; + private final String gas; + private final String gasPrice; + private final String hash; + private final String input; + private final String nonce; + private final String to; + private final String value; + private final String v; + private final String r; + private final String s; + private final String privateFrom; + private final String restriction; + private final String privacyGroupId; + + @JsonCreator + public PrivateTransactionGroupResponse( + @JsonProperty("from") final String from, + @JsonProperty("gas") final String gas, + @JsonProperty("gasPrice") final String gasPrice, + @JsonProperty("hash") final String hash, + @JsonProperty("input") final String input, + @JsonProperty("nonce") final String nonce, + @JsonProperty("to") final String to, + @JsonProperty("value") final String value, + @JsonProperty("v") final String v, + @JsonProperty("r") final String r, + @JsonProperty("s") final String s, + @JsonProperty("privateFrom") final String privateFrom, + @JsonProperty("restriction") final String restriction, + @JsonProperty("privacyGroupId") final String privacyGroupId) { + this.from = from; + this.gas = gas; + this.gasPrice = gasPrice; + this.hash = hash; + this.input = input; + this.nonce = nonce; + this.to = to; + this.value = value; + this.v = v; + this.r = r; + this.s = s; + this.privateFrom = privateFrom; + this.restriction = restriction; + this.privacyGroupId = privacyGroupId; + } + + public String getFrom() { + return from; + } + + public String getGas() { + return gas; + } + + public String getGasPrice() { + return gasPrice; + } + + public String getHash() { + return hash; + } + + public String getInput() { + return input; + } + + public String getNonce() { + return nonce; + } + + public String getTo() { + return to; + } + + public String getValue() { + return value; + } + + public String getV() { + return v; + } + + public String getR() { + return r; + } + + public String getS() { + return s; + } + + public String getPrivateFrom() { + return privateFrom; + } + + public String getRestriction() { + return restriction; + } + + public String getPrivacyGroupId() { + return privacyGroupId; + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/EeaSendRawTransactionTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/EeaSendRawTransactionTransaction.java new file mode 100644 index 00000000000..e5e1ef9a2f2 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/EeaSendRawTransactionTransaction.java @@ -0,0 +1,44 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +public class EeaSendRawTransactionTransaction implements Transaction { + + final String transaction; + + public EeaSendRawTransactionTransaction(final String transaction) { + this.transaction = transaction; + } + + @Override + public Hash execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.SendRawTransactionResponse result = + node.privacy().eeaSendRawTransaction(transaction).send(); + assertThat(result).isNotNull(); + return result.getResult(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivCreatePrivacyGroupTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivCreatePrivacyGroupTransaction.java new file mode 100644 index 00000000000..eaddb84fc52 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivCreatePrivacyGroupTransaction.java @@ -0,0 +1,46 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.parameters.CreatePrivacyGroupParameter; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; +import java.util.List; + +public class PrivCreatePrivacyGroupTransaction implements Transaction { + + final CreatePrivacyGroupParameter params; + + public PrivCreatePrivacyGroupTransaction( + final List addresses, final String groupName, final String groupDescription) { + this.params = new CreatePrivacyGroupParameter(addresses, groupName, groupDescription); + } + + @Override + public String execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.CreatePrivacyGroupResponse result = + node.privacy().privCreatePrivacyGroup(params).send(); + assertThat(result).isNotNull(); + return result.getResult(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivDeletePrivacyGroupTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivDeletePrivacyGroupTransaction.java new file mode 100644 index 00000000000..19adda1472f --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivDeletePrivacyGroupTransaction.java @@ -0,0 +1,43 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +public class PrivDeletePrivacyGroupTransaction implements Transaction { + + final String transactionHash; + + public PrivDeletePrivacyGroupTransaction(final String transactionHash) { + this.transactionHash = transactionHash; + } + + @Override + public String execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.DeletePrivacyGroupResponse result = + node.privacy().privDeletePrivacyGroup(transactionHash).send(); + assertThat(result).isNotNull(); + return result.getResult(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivDistributeRawTransactionTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivDistributeRawTransactionTransaction.java new file mode 100644 index 00000000000..f5245951b9f --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivDistributeRawTransactionTransaction.java @@ -0,0 +1,43 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +public class PrivDistributeRawTransactionTransaction implements Transaction { + + final String transaction; + + public PrivDistributeRawTransactionTransaction(final String transaction) { + this.transaction = transaction; + } + + @Override + public String execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.PrivDistributeTransactionResponse result = + node.privacy().privDistributeTransaction(transaction).send(); + assertThat(result).isNotNull(); + return result.getTransactionKey(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivFindPrivacyGroupTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivFindPrivacyGroupTransaction.java new file mode 100644 index 00000000000..5ee5b5e1fb4 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivFindPrivacyGroupTransaction.java @@ -0,0 +1,44 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.enclave.types.PrivacyGroup; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +public class PrivFindPrivacyGroupTransaction implements Transaction { + + final String[] groupMembers; + + public PrivFindPrivacyGroupTransaction(final String[] groupMembers) { + this.groupMembers = groupMembers; + } + + @Override + public PrivacyGroup[] execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.FindPrivacyGroupResponse result = + node.privacy().privFindPrivacyGroup(groupMembers).send(); + assertThat(result).isNotNull(); + return result.getResult(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetEeaTransactionCountTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetEeaTransactionCountTransaction.java new file mode 100644 index 00000000000..ba0bd3ba737 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetEeaTransactionCountTransaction.java @@ -0,0 +1,44 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +public class PrivGetEeaTransactionCountTransaction implements Transaction { + + private final Object[] params; + + public PrivGetEeaTransactionCountTransaction( + final String accountAddress, final String privateFrom, final String[] privateFor) { + this.params = new Object[] {accountAddress, privateFrom, privateFor}; + } + + @Override + public Integer execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.GetTransactionCountResponse result = + node.privacy().privGetEeaTransactionCount(params).send(); + assertThat(result).isNotNull(); + return result.getCount(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetPrivacyPrecompileAddressTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetPrivacyPrecompileAddressTransaction.java new file mode 100644 index 00000000000..abbba72a7c6 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetPrivacyPrecompileAddressTransaction.java @@ -0,0 +1,38 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +public class PrivGetPrivacyPrecompileAddressTransaction implements Transaction
{ + + @Override + public Address execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.GetPrivacyPrecompileAddressResponse result = + node.privacy().privGetPrivacyPrecompileAddress().send(); + assertThat(result).isNotNull(); + return result.getResult(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetPrivateTransactionTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetPrivateTransactionTransaction.java new file mode 100644 index 00000000000..5c12b6c13eb --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetPrivateTransactionTransaction.java @@ -0,0 +1,46 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivateTransactionGroupResponse; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +public class PrivGetPrivateTransactionTransaction + implements Transaction { + + private final Hash transactionHash; + + public PrivGetPrivateTransactionTransaction(final Hash transactionHash) { + this.transactionHash = transactionHash; + } + + @Override + public PrivateTransactionGroupResponse execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.GetPrivateTransactionResponse result = + node.privacy().privGetPrivateTransaction(transactionHash).send(); + assertThat(result).isNotNull(); + return result.getResult(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetTransactionCountTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetTransactionCountTransaction.java new file mode 100644 index 00000000000..aee4cab49c2 --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetTransactionCountTransaction.java @@ -0,0 +1,44 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +public class PrivGetTransactionCountTransaction implements Transaction { + + private final Object[] params; + + public PrivGetTransactionCountTransaction( + final String accountAddress, final String privacyGroupId) { + this.params = new String[] {accountAddress, privacyGroupId}; + } + + @Override + public Integer execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.GetTransactionCountResponse result = + node.privacy().privGetTransactionCount(params).send(); + assertThat(result).isNotNull(); + return result.getCount(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetTransactionReceiptTransaction.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetTransactionReceiptTransaction.java new file mode 100644 index 00000000000..2ea67b7982f --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivGetTransactionReceiptTransaction.java @@ -0,0 +1,47 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.io.IOException; + +import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt; + +public class PrivGetTransactionReceiptTransaction + implements Transaction { + + private final Hash transaction; + + public PrivGetTransactionReceiptTransaction(final Hash transaction) { + this.transaction = transaction; + } + + @Override + public PrivateTransactionReceipt execute(final NodeRequests node) { + try { + final PrivacyRequestFactory.GetTransactionReceiptResponse result = + node.privacy().privGetTransactionReceipt(transaction).send(); + assertThat(result).isNotNull(); + return result.getResult(); + } catch (final IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyRequestFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyRequestFactory.java index 2f71369f9e5..314e921bf56 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyRequestFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyRequestFactory.java @@ -16,22 +16,51 @@ import static java.util.Collections.singletonList; +import org.hyperledger.besu.enclave.types.PrivacyGroup; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.parameters.CreatePrivacyGroupParameter; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivateTransactionGroupResponse; + +import java.util.Collections; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import org.web3j.protocol.Web3jService; import org.web3j.protocol.besu.Besu; +import org.web3j.protocol.besu.response.privacy.PrivateTransactionReceipt; import org.web3j.protocol.core.Request; import org.web3j.protocol.core.Response; public class PrivacyRequestFactory { - private final Besu besuClient; - private final Web3jService web3jService; - public PrivacyRequestFactory(final Web3jService web3jService) { - this.web3jService = web3jService; - this.besuClient = Besu.build(web3jService); - } + public static class GetPrivacyPrecompileAddressResponse extends Response
{} - public Besu getBesuClient() { - return besuClient; + public static class GetPrivateTransactionResponse + extends Response {} + + public static class CreatePrivacyGroupResponse extends Response {} + + public static class DeletePrivacyGroupResponse extends Response {} + + public static class FindPrivacyGroupResponse extends Response {} + + public static class SendRawTransactionResponse extends Response {} + + public static class GetTransactionReceiptResponse extends Response {} + + public static class GetTransactionCountResponse extends Response { + final Integer count; + + @JsonCreator + public GetTransactionCountResponse(@JsonProperty("result") final String result) { + this.count = Integer.decode(result); + } + + public Integer getCount() { + return count; + } } public Request privDistributeTransaction( @@ -43,6 +72,18 @@ public Request privDistributeTransaction( PrivDistributeTransactionResponse.class); } + private final Besu besuClient; + private final Web3jService web3jService; + + public PrivacyRequestFactory(final Web3jService web3jService) { + this.web3jService = web3jService; + this.besuClient = Besu.build(web3jService); + } + + public Besu getBesuClient() { + return besuClient; + } + public static class PrivDistributeTransactionResponse extends Response { public PrivDistributeTransactionResponse() {} @@ -51,4 +92,79 @@ public String getTransactionKey() { return getResult(); } } + + public Request privGetPrivacyPrecompileAddress() { + return new Request<>( + "priv_getPrivacyPrecompileAddress", + Collections.emptyList(), + web3jService, + GetPrivacyPrecompileAddressResponse.class); + } + + public Request privGetPrivateTransaction( + final Hash transactionHash) { + return new Request<>( + "priv_getPrivateTransaction", + singletonList(transactionHash.toHexString()), + web3jService, + GetPrivateTransactionResponse.class); + } + + public Request privCreatePrivacyGroup( + final CreatePrivacyGroupParameter params) { + return new Request<>( + "priv_createPrivacyGroup", + singletonList(params), + web3jService, + CreatePrivacyGroupResponse.class); + } + + public Request privDeletePrivacyGroup(final String groupId) { + return new Request<>( + "priv_deletePrivacyGroup", + singletonList(groupId), + web3jService, + DeletePrivacyGroupResponse.class); + } + + public Request privFindPrivacyGroup(final String[] groupMembers) { + return new Request<>( + "priv_findPrivacyGroup", + singletonList(groupMembers), + web3jService, + FindPrivacyGroupResponse.class); + } + + public Request eeaSendRawTransaction(final String transaction) { + return new Request<>( + "eea_sendRawTransaction", + singletonList(transaction), + web3jService, + SendRawTransactionResponse.class); + } + + public Request privGetTransactionReceipt( + final Hash transactionHash) { + return new Request<>( + "priv_getTransactionReceipt", + singletonList(transactionHash.toHexString()), + web3jService, + GetTransactionReceiptResponse.class); + } + + public Request privGetTransactionCount(final Object[] params) { + return new Request<>( + "priv_getTransactionCount", + List.of(params), + web3jService, + GetTransactionCountResponse.class); + } + + public Request privGetEeaTransactionCount(final Object[] params) { + return new Request<>( + "priv_getEeaTransactionCount", + List.of(params), + web3jService, + GetTransactionCountResponse.class); + } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyTransactions.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyTransactions.java new file mode 100644 index 00000000000..218368a3b7a --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/privacy/PrivacyTransactions.java @@ -0,0 +1,65 @@ +/* + * 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.tests.acceptance.dsl.transaction.privacy; + +import org.hyperledger.besu.ethereum.core.Hash; + +import java.util.List; + +public class PrivacyTransactions { + public PrivGetPrivacyPrecompileAddressTransaction getPrivacyPrecompileAddress() { + return new PrivGetPrivacyPrecompileAddressTransaction(); + } + + public PrivGetPrivateTransactionTransaction getPrivateTransaction(final Hash transactionHash) { + return new PrivGetPrivateTransactionTransaction(transactionHash); + } + + public PrivCreatePrivacyGroupTransaction createPrivacyGroup( + final List addresses, final String groupName, final String groupDescription) { + return new PrivCreatePrivacyGroupTransaction(addresses, groupName, groupDescription); + } + + public PrivDeletePrivacyGroupTransaction deletePrivacyGroup(final String transactionHash) { + return new PrivDeletePrivacyGroupTransaction(transactionHash); + } + + public PrivFindPrivacyGroupTransaction findPrivacyGroup(final String[] groupMembers) { + return new PrivFindPrivacyGroupTransaction(groupMembers); + } + + public EeaSendRawTransactionTransaction sendRawTransaction(final String transaction) { + return new EeaSendRawTransactionTransaction(transaction); + } + + public PrivDistributeRawTransactionTransaction distributeRawTransaction( + final String transaction) { + return new PrivDistributeRawTransactionTransaction(transaction); + } + + public PrivGetTransactionCountTransaction getTransactionCount( + final String accountAddress, final String privacyGroupId) { + return new PrivGetTransactionCountTransaction(accountAddress, privacyGroupId); + } + + public PrivGetEeaTransactionCountTransaction getEeaTransactionCount( + final String accountAddress, final String privateFrom, final String[] privateFor) { + return new PrivGetEeaTransactionCountTransaction(accountAddress, privateFrom, privateFor); + } + + public PrivGetTransactionReceiptTransaction getTransactionReceipt(final Hash transactionHash) { + return new PrivGetTransactionReceiptTransaction(transactionHash); + } +} diff --git a/acceptance-tests/tests/build.gradle b/acceptance-tests/tests/build.gradle index 1e936e148c1..76aa1646340 100644 --- a/acceptance-tests/tests/build.gradle +++ b/acceptance-tests/tests/build.gradle @@ -15,9 +15,11 @@ dependencies { testImplementation project(':acceptance-tests:dsl') testImplementation project(':besu') testImplementation project(':consensus:clique') + implementation project(':crypto') testImplementation project(':enclave') testImplementation project(':ethereum:api') testImplementation project(':ethereum:core') + testImplementation project(':ethereum:rlp') testImplementation project(path: ':ethereum:core', configuration: 'testSupportArtifacts') testImplementation project(':ethereum:permissioning') testImplementation project(':plugin-api') @@ -33,6 +35,8 @@ dependencies { testImplementation 'org.web3j:besu' testImplementation 'tech.pegasys.ethsigner.internal:core' testImplementation 'tech.pegasys.ethsigner.internal:file-based' + + testCompile "com.github.tomakehurst:wiremock-jre8-standalone:2.25.1" } test.enabled = false diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/HttpServiceLoginAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/HttpServiceLoginAcceptanceTest.java index 5ff7dd6d3ac..605d4d2cf12 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/HttpServiceLoginAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/HttpServiceLoginAcceptanceTest.java @@ -30,6 +30,7 @@ public class HttpServiceLoginAcceptanceTest extends AcceptanceTestBase { private Cluster authenticatedCluster; private BesuNode nodeUsingAuthFile; private BesuNode nodeUsingJwtPublicKey; + private static final String AUTH_FILE = "authentication/auth.toml"; // token with payload{"iat": 1516239022,"exp": 4729363200,"permissions": ["net:peerCount"]} private static final String TOKEN_ALLOWING_NET_PEER_COUNT = @@ -42,11 +43,12 @@ public class HttpServiceLoginAcceptanceTest extends AcceptanceTestBase { @Before public void setUp() throws IOException, URISyntaxException { + final ClusterConfiguration clusterConfiguration = new ClusterConfigurationBuilder().awaitPeerDiscovery(false).build(); authenticatedCluster = new Cluster(clusterConfiguration, net); - nodeUsingAuthFile = besu.createNodeWithAuthentication("node1"); + nodeUsingAuthFile = besu.createNodeWithAuthentication("node1", AUTH_FILE); nodeUsingJwtPublicKey = besu.createNodeWithAuthenticationUsingJwtPublicKey("node2"); authenticatedCluster.start(nodeUsingAuthFile, nodeUsingJwtPublicKey); diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/WebsocketServiceLoginAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/WebsocketServiceLoginAcceptanceTest.java index e81cdad8647..588a27390bc 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/WebsocketServiceLoginAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/jsonrpc/WebsocketServiceLoginAcceptanceTest.java @@ -30,6 +30,7 @@ public class WebsocketServiceLoginAcceptanceTest extends AcceptanceTestBase { private BesuNode nodeUsingAuthFile; private BesuNode nodeUsingJwtPublicKey; private Cluster authenticatedCluster; + private static final String AUTH_FILE = "authentication/auth.toml"; // token with payload{"iat": 1516239022,"exp": 4729363200,"permissions": ["net:peerCount"]} private static final String TOKEN_ALLOWING_NET_PEER_COUNT = @@ -46,7 +47,7 @@ public void setUp() throws IOException, URISyntaxException { new ClusterConfigurationBuilder().awaitPeerDiscovery(false).build(); authenticatedCluster = new Cluster(clusterConfiguration, net); - nodeUsingAuthFile = besu.createNodeWithAuthentication("node1"); + nodeUsingAuthFile = besu.createNodeWithAuthentication("node1", AUTH_FILE); nodeUsingJwtPublicKey = besu.createNodeWithAuthenticationUsingJwtPublicKey("node2"); authenticatedCluster.start(nodeUsingAuthFile, nodeUsingJwtPublicKey); diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java new file mode 100644 index 00000000000..0c873402c2a --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java @@ -0,0 +1,312 @@ +/* + * 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.tests.acceptance.privacy.multitenancy; + +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.util.Collections.emptyList; +import static org.hyperledger.besu.ethereum.core.Address.DEFAULT_PRIVACY; + +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.enclave.types.PrivacyGroup; +import org.hyperledger.besu.enclave.types.ReceiveResponse; +import org.hyperledger.besu.enclave.types.SendResponse; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.ethereum.privacy.Restriction; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.ClusterConfiguration; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.ClusterConfigurationBuilder; + +import java.math.BigInteger; +import java.util.List; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.io.Base64; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +public class MultiTenancyAcceptanceTest extends AcceptanceTestBase { + private BesuNode node; + private final ObjectMapper mapper = new ObjectMapper(); + private Cluster multiTenancyCluster; + + private static final int ENCLAVE_PORT = 1080; + private static final String PRIVACY_GROUP_ID = "Z3JvdXBJZA=="; + private static final String ENCLAVE_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; + private static final String KEY1 = "sgFkVOyFndZe/5SAZJO5UYbrl7pezHetveriBBWWnE8="; + private static final String KEY2 = "R1kW75NQC9XX3kwNpyPjCBFflM29+XvnKKS9VLrUkzo="; + private static final String KEY3 = "QzHuACXpfhoGAgrQriWJcDJ6MrUwcCvutKMoAn9KplQ="; + private final Address senderAddress = + Address.wrap(Bytes.fromHexString(accounts.getPrimaryBenefactor().getAddress())); + + @Rule public WireMockRule wireMockRule = new WireMockRule(options().port(ENCLAVE_PORT)); + + @Before + public void setUp() throws Exception { + final ClusterConfiguration clusterConfiguration = + new ClusterConfigurationBuilder().awaitPeerDiscovery(false).build(); + multiTenancyCluster = new Cluster(clusterConfiguration, net); + node = + besu.createNodeWithMultiTenantedPrivacy( + "node1", + "http://127.0.0.1:" + ENCLAVE_PORT, + "authentication/auth_priv.toml", + "authentication/auth_priv_key"); + multiTenancyCluster.start(node); + final String token = + node.execute(permissioningTransactions.createSuccessfulLogin("user", "pegasys")); + node.useAuthenticationTokenInHeaderForJsonRpc(token); + } + + @After + public void tearDown() { + multiTenancyCluster.close(); + } + + @Test + public void privGetPrivacyPrecompileAddressShouldReturnExpectedAddress() { + node.verify(priv.getPrivacyPrecompileAddress(DEFAULT_PRIVACY)); + } + + @Test + public void privGetPrivateTransactionSuccessShouldReturnExpectedPrivateTransaction() + throws JsonProcessingException { + final PrivateTransaction validSignedPrivateTransaction = + getValidSignedPrivateTransaction(senderAddress); + + receiveEnclaveStub(getRLPOutput(validSignedPrivateTransaction)); + retrievePrivacyGroupEnclaveStub(); + sendEnclaveStub("testKey"); + + final Hash transactionHash = + node.execute( + privacyTransactions.sendRawTransaction( + getRLPOutput(validSignedPrivateTransaction).encoded().toHexString())); + node.verify(priv.getTransactionReceipt(transactionHash)); + node.verify(priv.getPrivateTransaction(transactionHash, validSignedPrivateTransaction)); + } + + @Test + public void privCreatePrivacyGroupSuccessShouldReturnNewId() throws JsonProcessingException { + createPrivacyGroupEnclaveStub(); + + node.verify( + priv.createPrivacyGroup( + List.of(KEY1, KEY2, KEY3), "GroupName", "Group description.", PRIVACY_GROUP_ID)); + } + + @Test + public void privDeletePrivacyGroupSuccessShouldReturnId() throws JsonProcessingException { + retrievePrivacyGroupEnclaveStub(); + deletePrivacyGroupEnclaveStub(); + + node.verify(priv.deletePrivacyGroup(PRIVACY_GROUP_ID)); + } + + @Test + public void privFindPrivacyGroupSuccessShouldReturnExpectedGroupMembership() + throws JsonProcessingException { + final List groupMembership = + List.of( + testPrivacyGroup(emptyList(), PrivacyGroup.Type.PANTHEON), + testPrivacyGroup(emptyList(), PrivacyGroup.Type.PANTHEON), + testPrivacyGroup(emptyList(), PrivacyGroup.Type.PANTHEON)); + + findPrivacyGroupEnclaveStub(groupMembership); + + node.verify(priv.findPrivacyGroup(groupMembership.size(), ENCLAVE_KEY)); + } + + @Test + public void eeaSendRawTransactionSuccessShouldReturnPrivateTransactionHash() + throws JsonProcessingException { + final PrivateTransaction validSignedPrivateTransaction = + getValidSignedPrivateTransaction(senderAddress); + + retrievePrivacyGroupEnclaveStub(); + sendEnclaveStub("testKey"); + receiveEnclaveStub(getRLPOutput(validSignedPrivateTransaction)); + + node.verify( + priv.eeaSendRawTransaction( + getRLPOutput(validSignedPrivateTransaction).encoded().toHexString())); + } + + @Test + public void privGetTransactionCountSuccessShouldReturnExpectedTransactionCount() + throws JsonProcessingException { + final PrivateTransaction validSignedPrivateTransaction = + getValidSignedPrivateTransaction(senderAddress); + final String accountAddress = validSignedPrivateTransaction.getSender().toHexString(); + final BytesValueRLPOutput rlpOutput = getRLPOutput(validSignedPrivateTransaction); + + retrievePrivacyGroupEnclaveStub(); + sendEnclaveStub("testKey"); + receiveEnclaveStub(rlpOutput); + + node.verify(priv.getTransactionCount(accountAddress, PRIVACY_GROUP_ID, 0)); + final Hash transactionReceipt = + node.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString())); + + node.verify(priv.getTransactionReceipt(transactionReceipt)); + node.verify(priv.getTransactionCount(accountAddress, PRIVACY_GROUP_ID, 1)); + } + + @Test + public void privDistributeRawTransactionSuccessShouldReturnEnclaveKey() + throws JsonProcessingException { + final String enclaveResponseKey = "TestKey"; + final String enclaveResponseKeyBase64 = + Base64.encode(Bytes.wrap(enclaveResponseKey.getBytes(UTF_8))); + final String enclaveResponseKeyBytes = + Bytes.wrap(Bytes.fromBase64String(enclaveResponseKeyBase64)).toString(); + + retrievePrivacyGroupEnclaveStub(); + sendEnclaveStub(enclaveResponseKeyBase64); + + node.verify( + priv.distributeRawTransaction( + getRLPOutput(getValidSignedPrivateTransaction(senderAddress)).encoded().toHexString(), + enclaveResponseKeyBytes)); + } + + @Test + public void privGetTransactionReceiptSuccessShouldReturnTransactionReceiptAfterMined() + throws JsonProcessingException { + final PrivateTransaction validSignedPrivateTransaction = + getValidSignedPrivateTransaction(senderAddress); + final BytesValueRLPOutput rlpOutput = getRLPOutput(validSignedPrivateTransaction); + + retrievePrivacyGroupEnclaveStub(); + sendEnclaveStub("testKey"); + receiveEnclaveStub(rlpOutput); + + final Hash transactionReceipt = + node.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString())); + + node.verify(priv.getTransactionReceipt(transactionReceipt)); + } + + @Test + public void privGetEeaTransactionCountSuccessShouldReturnExpectedTransactionCount() + throws JsonProcessingException { + final PrivateTransaction validSignedPrivateTransaction = + getValidSignedPrivateTransaction(senderAddress); + final String accountAddress = validSignedPrivateTransaction.getSender().toHexString(); + final String senderAddressBase64 = Base64.encode(Bytes.wrap(accountAddress.getBytes(UTF_8))); + final BytesValueRLPOutput rlpOutput = getRLPOutput(validSignedPrivateTransaction); + final List groupMembership = + List.of(testPrivacyGroup(emptyList(), PrivacyGroup.Type.LEGACY)); + + retrievePrivacyGroupEnclaveStub(); + sendEnclaveStub("testKey"); + receiveEnclaveStub(rlpOutput); + findPrivacyGroupEnclaveStub(groupMembership); + + node.verify(priv.getTransactionCount(accountAddress, PRIVACY_GROUP_ID, 0)); + final Hash transactionHash = + node.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString())); + + node.verify(priv.getTransactionReceipt(transactionHash)); + + final String privateFrom = ENCLAVE_KEY; + final String[] privateFor = {senderAddressBase64}; + node.verify(priv.getEeaTransactionCount(accountAddress, privateFrom, privateFor, 1)); + } + + private void findPrivacyGroupEnclaveStub(final List groupMembership) + throws JsonProcessingException { + final String findGroupResponse = mapper.writeValueAsString(groupMembership); + stubFor(post("/findPrivacyGroup").willReturn(ok(findGroupResponse))); + } + + private void createPrivacyGroupEnclaveStub() throws JsonProcessingException { + final String createGroupResponse = + mapper.writeValueAsString(testPrivacyGroup(emptyList(), PrivacyGroup.Type.PANTHEON)); + stubFor(post("/createPrivacyGroup").willReturn(ok(createGroupResponse))); + } + + private void deletePrivacyGroupEnclaveStub() throws JsonProcessingException { + final String deleteGroupResponse = mapper.writeValueAsString(PRIVACY_GROUP_ID); + stubFor(post("/deletePrivacyGroup").willReturn(ok(deleteGroupResponse))); + } + + private void retrievePrivacyGroupEnclaveStub() throws JsonProcessingException { + final String retrieveGroupResponse = + mapper.writeValueAsString( + testPrivacyGroup(List.of(ENCLAVE_KEY), PrivacyGroup.Type.PANTHEON)); + stubFor(post("/retrievePrivacyGroup").willReturn(ok(retrieveGroupResponse))); + } + + private void sendEnclaveStub(final String testKey) throws JsonProcessingException { + final String sendResponse = mapper.writeValueAsString(new SendResponse(testKey)); + stubFor(post("/send").willReturn(ok(sendResponse))); + } + + private void receiveEnclaveStub(final BytesValueRLPOutput rlpOutput) + throws JsonProcessingException { + final String senderKey = "QTFhVnRNeExDVUhtQlZIWG9aenpCZ1BiVy93ajVheERwVzlYOGw5MVNHbz0="; + final String receiveResponse = + mapper.writeValueAsString( + new ReceiveResponse( + rlpOutput.encoded().toBase64String().getBytes(UTF_8), PRIVACY_GROUP_ID, senderKey)); + stubFor(post("/receive").willReturn(ok(receiveResponse))); + } + + private BytesValueRLPOutput getRLPOutput(final PrivateTransaction validSignedPrivateTransaction) { + final BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput(); + validSignedPrivateTransaction.writeTo(bvrlpo); + return bvrlpo; + } + + private PrivacyGroup testPrivacyGroup( + final List groupMembers, final PrivacyGroup.Type groupType) { + return new PrivacyGroup(PRIVACY_GROUP_ID, groupType, "test", "testGroup", groupMembers); + } + + private static PrivateTransaction getValidSignedPrivateTransaction(final Address senderAddress) { + return PrivateTransaction.builder() + .nonce(0) + .gasPrice(Wei.ZERO) + .gasLimit(3000000) + .to(null) + .value(Wei.ZERO) + .payload(Bytes.wrap(new byte[] {})) + .sender(senderAddress) + .chainId(BigInteger.valueOf(2018)) + .privateFrom(Bytes.fromBase64String(ENCLAVE_KEY)) + .restriction(Restriction.RESTRICTED) + .privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID)) + .signAndBuild( + SECP256K1.KeyPair.create( + SECP256K1.PrivateKey.create( + new BigInteger( + "853d7f0010fd86d0d7811c1f9d968ea89a24484a8127b4a483ddf5d2cfec766d", 16)))); + } +} diff --git a/acceptance-tests/tests/src/test/resources/authentication/auth_priv.toml b/acceptance-tests/tests/src/test/resources/authentication/auth_priv.toml new file mode 100644 index 00000000000..2c86f165bf7 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/authentication/auth_priv.toml @@ -0,0 +1,4 @@ +[Users.user] +password = "$2a$10$l3GA7K8g6rJ/Yv.YFSygCuI9byngpEzxgWS9qEg5emYDZomQW7fGC" +permissions = ["fakePermission", "*:*"] +privacyPublicKey = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=" diff --git a/acceptance-tests/tests/src/test/resources/authentication/auth_priv_key b/acceptance-tests/tests/src/test/resources/authentication/auth_priv_key new file mode 100644 index 00000000000..85329a8cbe3 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/authentication/auth_priv_key @@ -0,0 +1 @@ +8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63 \ No newline at end of file diff --git a/acceptance-tests/tests/src/test/resources/authentication/auth_pub_key b/acceptance-tests/tests/src/test/resources/authentication/auth_pub_key new file mode 100644 index 00000000000..3c8b7f3bfb8 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/authentication/auth_pub_key @@ -0,0 +1 @@ +A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo= \ No newline at end of file From 87f4829e235015ce166e2c0fbdb456ef40809d0d Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 12 Feb 2020 09:16:59 -0700 Subject: [PATCH 07/38] More specific task metrics names (#389) A prior refactoring had accidentally removed the specific task names from the metrics labels. Signed-off-by: Danno Ferrin --- .../eth/manager/task/AbstractEthTask.java | 7 +++-- .../eth/manager/task/AbstractEthTaskTest.java | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/AbstractEthTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/AbstractEthTask.java index 2bfdadad33e..bfae75fdfe9 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/AbstractEthTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/AbstractEthTask.java @@ -42,14 +42,15 @@ public abstract class AbstractEthTask implements EthTask { private final Collection> subTaskFutures = new ConcurrentLinkedDeque<>(); protected AbstractEthTask(final MetricsSystem metricsSystem) { - this(buildOperationTimer(metricsSystem)); + this.taskTimer = buildOperationTimer(metricsSystem, getClass().getSimpleName()); } protected AbstractEthTask(final OperationTimer taskTimer) { this.taskTimer = taskTimer; } - private static OperationTimer buildOperationTimer(final MetricsSystem metricsSystem) { + private static OperationTimer buildOperationTimer( + final MetricsSystem metricsSystem, final String taskName) { final LabelledMetric ethTasksTimer = metricsSystem.createLabelledTimer( BesuMetricCategory.SYNCHRONIZER, "task", "Internal processing tasks", "taskName"); @@ -64,7 +65,7 @@ public double stopTimer() { } }; } else { - return ethTasksTimer.labels(AbstractEthTask.class.getSimpleName()); + return ethTasksTimer.labels(taskName); } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/AbstractEthTaskTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/AbstractEthTaskTest.java index 79a9a6789b6..44b9e11a0ed 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/AbstractEthTaskTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/AbstractEthTaskTest.java @@ -19,6 +19,9 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; +import org.hyperledger.besu.plugin.services.metrics.MetricCategory; import org.hyperledger.besu.plugin.services.metrics.OperationTimer; import java.util.Arrays; @@ -104,6 +107,33 @@ protected void executeTask() {} verify(mockTimingContext).stopTimer(); } + @Test + public void shouldHaveSpecificMetricsLabels() { + // seed with a failing value so that a no-op also trips the failure. + final String[] lastLabelNames = {"AbstractEthTask"}; + final MetricsSystem instrumentedLabeler = + new NoOpMetricsSystem() { + @Override + public LabelledMetric createLabelledTimer( + final MetricCategory category, + final String name, + final String help, + final String... labelNames) { + return names -> { + lastLabelNames[0] = names[0]; + return null; + }; + } + }; + new AbstractEthTask<>(instrumentedLabeler) { + @Override + protected void executeTask() { + // no-op + } + }; + assertThat(lastLabelNames[0]).isNotEqualTo("AbstractEthTask"); + } + private class EthTaskWithMultipleSubtasks extends AbstractEthTask { private final List> subtasks; From ae3bd0129e94a96d1f569af7ee78f9925872ce0d Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 12 Feb 2020 10:31:52 -0700 Subject: [PATCH 08/38] More trace fixes (#386) * pop flat trace context when handling halts * Better detection of precompiled and non-executed contracts * correct from address when calling in init code * fix some exotic nesting cases * correct from field for init code calls at depth >1 * correct cost on a non-call * changelog and notes Signed-off-by: Danno Ferrin --- CHANGELOG.md | 8 ++ docs/trace_rpc_apis.md | 39 +++++++ .../results/tracing/flat/FlatTrace.java | 4 + .../tracing/flat/FlatTraceGenerator.java | 106 +++++++++++------- .../internal/results/tracing/flat/Result.java | 4 + .../results/tracing/vm/VmTraceGenerator.java | 55 ++++++--- ...trace_replayBlockTransactions_all_0xD.json | 6 + .../trace_replayBlockTransactions_0xD.json | 6 + 8 files changed, 170 insertions(+), 58 deletions(-) create mode 100644 docs/trace_rpc_apis.md diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f8b45cde7..90283d1cff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 1.4 RC + +### Additions and Improvements + +- New`trace_replayBlockTransactions` JSON-RPC API + +This can be enabled using the `--rpc-http-api TRACE` CLI flag. There are some philosophical differences between Besu and other implementations that are outlined in the `[trace_rpc_apis.md](./docs/trace_rpc_apis.md)` documentation. + ## 1.4 Beta 3 ### Additions and Improvements diff --git a/docs/trace_rpc_apis.md b/docs/trace_rpc_apis.md new file mode 100644 index 00000000000..116c7ea226b --- /dev/null +++ b/docs/trace_rpc_apis.md @@ -0,0 +1,39 @@ +# Trace RPC API Notes + +This document outlines major differences for `trace_replayBlockTransactions` +compared to other implementations. + +## `stateDiff` + +No major differences were observed in the `stateDiff` field. + +## `trace` + +Besu reports `gasUsed` after applying the effects of gas refunds. Future +implementations of Besu might track gas refunds separately. + +## `vmTrace` + +### Returned Memory from Calls + +In the `vmTrace` `ope.ex.mem` fields Besu only reports actual data returned +from a `RETURN` opcode. Other implementations return the contents of the +reserved output space for the call operations. Note two major differences: + +1. Besu reports `null` when a call operation ends because of a `STOP`, `HALT`, + `REVERT`, running out of instructions, or any exceptional halts. +2. When a `RETURN` operation returns data of a different length than the space + reserved by the call only the data passed to the `RETURN` operation is + reported. Other implementations will include pre-existing memory data or + trim the returned data. + +### Precompiled Contracts Calls + +Besu reports only the actual cost of the precompiled contract call in the +`cost` field. + +### Out of Gas + +Besu reports the operation that causes out fo gas exceptions, including +calculated gas cost. The operation is not executed so no `ex` values are +reported. diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTrace.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTrace.java index 5a430d53d32..6964e52c2a5 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTrace.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTrace.java @@ -187,6 +187,10 @@ public Builder type(final String type) { return this; } + public String getType() { + return type; + } + public Builder error(final Optional error) { this.error = error; return this; diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java index 257fca9411d..b1ffee6f9bb 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/FlatTraceGenerator.java @@ -18,7 +18,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.Trace; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.TracingUtils; -import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.tracing.flat.FlatTrace.Context; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.Transaction; @@ -30,6 +29,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -98,9 +98,12 @@ public static Stream generateFromTransactionTrace( // declare the first transactionTrace context as the previous transactionTrace context long cumulativeGasCost = 0; - int traceFrameIndex = 0; - final List traceFrames = transactionTrace.getTraceFrames(); - for (final TraceFrame traceFrame : traceFrames) { + final Iterator iter = transactionTrace.getTraceFrames().iterator(); + Optional nextTraceFrame = + iter.hasNext() ? Optional.of(iter.next()) : Optional.empty(); + while (nextTraceFrame.isPresent()) { + final TraceFrame traceFrame = nextTraceFrame.get(); + nextTraceFrame = iter.hasNext() ? Optional.of(iter.next()) : Optional.empty(); cumulativeGasCost += traceFrame.getGasCost().orElse(Gas.ZERO).toLong() + traceFrame.getPrecompiledGasCost().orElse(Gas.ZERO).toLong(); @@ -113,12 +116,10 @@ public static Stream generateFromTransactionTrace( handleCall( transactionTrace, traceFrame, - smartContractAddress, + nextTraceFrame, flatTraces, cumulativeGasCost, tracesContexts, - traceFrameIndex, - traceFrames, opcodeString.toLowerCase(Locale.US)); } else if ("RETURN".equals(opcodeString) || "STOP".equals(opcodeString)) { if (currentContext != null) { @@ -136,19 +137,12 @@ public static Stream generateFromTransactionTrace( flatTraces, tracesContexts, cumulativeGasCost, - traceFrameIndex, - traceFrames); + nextTraceFrame); } else if ("REVERT".equals(opcodeString)) { currentContext = handleRevert(tracesContexts, currentContext); } else if (!traceFrame.getExceptionalHaltReasons().isEmpty()) { - currentContext - .getBuilder() - .error( - traceFrame.getExceptionalHaltReasons().stream() - .map(ExceptionalHaltReason::getDescription) - .reduce((a, b) -> a + ", " + b)); + currentContext = handleHalt(tracesContexts, currentContext, traceFrame); } - traceFrameIndex++; } return flatTraces.stream().map(FlatTrace.Builder::build); @@ -157,21 +151,18 @@ public static Stream generateFromTransactionTrace( private static FlatTrace.Context handleCall( final TransactionTrace transactionTrace, final TraceFrame traceFrame, - final Optional smartContractAddress, + final Optional nextTraceFrame, final List flatTraces, final long cumulativeGasCost, final Deque tracesContexts, - final int traceFrameIndex, - final List traceFrames, final String opcodeString) { - final TraceFrame nextTraceFrame = traceFrames.get(traceFrameIndex + 1); final Bytes32[] stack = traceFrame.getStack().orElseThrow(); final Address contractCallAddress = toAddress(stack[stack.length - 2]); final FlatTrace.Context lastContext = tracesContexts.peekLast(); final String callingAddress = calculateCallingAddress(lastContext); - if (contractCallAddress.numberOfLeadingZeroBytes() >= 19) { - // don't log calls to precompiles + if (traceFrame.getDepth() >= nextTraceFrame.map(TraceFrame::getDepth).orElse(0)) { + // don't log calls to calls that don't execute, such as insufficient value and precompiles return tracesContexts.peekLast(); } @@ -181,13 +172,11 @@ private static FlatTrace.Context handleCall( .resultBuilder(Result.builder()); final Action.Builder subTraceActionBuilder = Action.builder() - .from(smartContractAddress.orElse(callingAddress)) + .from(callingAddress) .to(contractCallAddress.toString()) .input( - Optional.ofNullable(nextTraceFrame.getInputData()) - .map(Bytes::toHexString) - .orElse(null)) - .gas(nextTraceFrame.getGasRemaining().toHexString()) + nextTraceFrame.map(TraceFrame::getInputData).map(Bytes::toHexString).orElse(null)) + .gas(nextTraceFrame.map(TraceFrame::getGasRemaining).orElse(Gas.ZERO).toHexString()) .callType(opcodeString.toLowerCase(Locale.US)) .value(Quantity.create(transactionTrace.getTransaction().getValue())); @@ -218,7 +207,7 @@ private static FlatTrace.Context handleReturn( // set value for contract creation TXes, CREATE, and CREATE2 if (actionBuilder.getCallType() == null && traceFrame.getMaybeCode().isPresent()) { actionBuilder.init(traceFrame.getMaybeCode().get().getBytes().toHexString()); - resultBuilder.code(outputData.toHexString()).address(traceFrame.getRecipient().toHexString()); + resultBuilder.code(outputData.toHexString()); if (currentContext.isCreateOp()) { // this is from a CREATE/CREATE2, so add code deposit cost. currentContext.incGasUsed(outputData.size() * 200L); @@ -275,9 +264,7 @@ private static FlatTrace.Context handleCreateOperation( final List flatTraces, final Deque tracesContexts, final long cumulativeGasCost, - final int traceFrameIndex, - final List traceFrames) { - final TraceFrame nextTraceFrame = traceFrames.get(traceFrameIndex + 1); + final Optional nextTraceFrame) { final FlatTrace.Context lastContext = tracesContexts.peekLast(); final String callingAddress = calculateCallingAddress(lastContext); @@ -289,11 +276,15 @@ private static FlatTrace.Context handleCreateOperation( final Action.Builder subTraceActionBuilder = Action.builder() .from(smartContractAddress.orElse(callingAddress)) - .gas(nextTraceFrame.getGasRemaining().toHexString()) - .value(Quantity.create(nextTraceFrame.getValue())); + .gas(nextTraceFrame.map(TraceFrame::getGasRemaining).orElse(Gas.ZERO).toHexString()) + .value(Quantity.create(nextTraceFrame.map(TraceFrame::getValue).orElse(Wei.ZERO))); final FlatTrace.Context currentContext = new FlatTrace.Context(subTraceBuilder.actionBuilder(subTraceActionBuilder)); + currentContext + .getBuilder() + .getResultBuilder() + .address(nextTraceFrame.map(TraceFrame::getRecipient).orElse(Address.ZERO).toHexString()); currentContext.setCreateOp(true); currentContext.decGasUsed(cumulativeGasCost); tracesContexts.addLast(currentContext); @@ -301,9 +292,33 @@ private static FlatTrace.Context handleCreateOperation( return currentContext; } - private static Context handleRevert( - final Deque tracesContexts, final FlatTrace.Context currentContext) { - currentContext.getBuilder().error(Optional.of("Reverted")); + private static FlatTrace.Context handleHalt( + final Deque tracesContexts, + final FlatTrace.Context currentContext, + final TraceFrame traceFrame) { + final FlatTrace.Builder traceFrameBuilder = currentContext.getBuilder(); + traceFrameBuilder.error( + traceFrame.getExceptionalHaltReasons().stream() + .map(ExceptionalHaltReason::getDescription) + .reduce((a, b) -> a + ", " + b)); + if (tracesContexts.size() > 1) { + traceFrameBuilder.getActionBuilder().value("0x0"); + } + tracesContexts.removeLast(); + final FlatTrace.Context nextContext = tracesContexts.peekLast(); + if (nextContext != null) { + nextContext.getBuilder().incSubTraces(); + } + return nextContext; + } + + private static FlatTrace.Context handleRevert( + final Deque tracesContexts, final FlatTrace.Context currentContext) { + final FlatTrace.Builder traceFrameBuilder = currentContext.getBuilder(); + traceFrameBuilder.error(Optional.of("Reverted")); + if (tracesContexts.size() > 1) { + traceFrameBuilder.getActionBuilder().value("0x0"); + } tracesContexts.removeLast(); final FlatTrace.Context nextContext = tracesContexts.peekLast(); if (nextContext != null) { @@ -313,16 +328,25 @@ private static Context handleRevert( } private static String calculateCallingAddress(final FlatTrace.Context lastContext) { - if (lastContext.getBuilder().getActionBuilder().getCallType() == null) { - return ZERO_ADDRESS_STRING; + final FlatTrace.Builder lastContextBuilder = lastContext.getBuilder(); + final Action.Builder lastActionBuilder = lastContextBuilder.getActionBuilder(); + if (lastActionBuilder.getCallType() == null) { + if ("create".equals(lastContextBuilder.getType())) { + return lastContextBuilder.getResultBuilder().getAddress(); + } else { + return ZERO_ADDRESS_STRING; + } } - switch (lastContext.getBuilder().getActionBuilder().getCallType()) { + switch (lastActionBuilder.getCallType()) { case "call": case "staticcall": - return lastContext.getBuilder().getActionBuilder().getTo(); + return lastActionBuilder.getTo(); case "delegatecall": case "callcode": - return lastContext.getBuilder().getActionBuilder().getFrom(); + return lastActionBuilder.getFrom(); + case "create": + case "create2": + return lastContextBuilder.getResultBuilder().getAddress(); default: return ZERO_ADDRESS_STRING; } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/Result.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/Result.java index 4f11795834d..569d3a0c97e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/Result.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/flat/Result.java @@ -89,6 +89,10 @@ public Builder address(final String address) { return this; } + public String getAddress() { + return address; + } + public static Builder of(final Result result) { final Builder builder = new Builder(); if (result != null) { diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java index 6c8a2db6e80..b9bae4cc87c 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/tracing/vm/VmTraceGenerator.java @@ -24,6 +24,8 @@ import java.util.ArrayDeque; import java.util.Deque; +import java.util.EnumSet; +import java.util.Iterator; import java.util.Optional; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -40,7 +42,7 @@ public class VmTraceGenerator { private final TransactionTrace transactionTrace; private final VmTrace rootVmTrace = new VmTrace(); private final Deque parentTraces = new ArrayDeque<>(); - int lastDepth = 0; + private int lastDepth = 0; public VmTraceGenerator(final TransactionTrace transactionTrace) { this.transactionTrace = transactionTrace; @@ -68,7 +70,14 @@ private Trace generateTrace() { .getInit() .map(Bytes::toHexString) .ifPresent(rootVmTrace::setCode); - transactionTrace.getTraceFrames().forEach(this::addFrame); + final Iterator iter = transactionTrace.getTraceFrames().iterator(); + Optional nextTraceFrame = + iter.hasNext() ? Optional.of(iter.next()) : Optional.empty(); + while (nextTraceFrame.isPresent()) { + final TraceFrame currentTraceFrame = nextTraceFrame.get(); + nextTraceFrame = iter.hasNext() ? Optional.of(iter.next()) : Optional.empty(); + addFrame(currentTraceFrame, nextTraceFrame); + } } return rootVmTrace; } @@ -78,7 +87,7 @@ private Trace generateTrace() { * * @param frame the current trace frame */ - private void addFrame(final TraceFrame frame) { + private void addFrame(final TraceFrame frame, final Optional nextTraceFrame) { handleDepthDecreased(frame); if (!mustIgnore(frame)) { initStep(frame); @@ -87,8 +96,8 @@ private void addFrame(final TraceFrame frame) { generateTracingMemory(report); generateTracingPush(report); generateTracingStorage(report); - handleDepthIncreased(op, report); - completeStep(op, report); + handleDepthIncreased(op, report, nextTraceFrame); + completeStep(frame, op, report); lastDepth = frame.getDepth(); } } @@ -96,26 +105,33 @@ private void addFrame(final TraceFrame frame) { private boolean mustIgnore(final TraceFrame frame) { if ("STOP".equals(frame.getOpcode()) && transactionTrace.getTraceFrames().size() == 1) { return true; - } else if (!frame.getExceptionalHaltReasons().isEmpty() - && !frame - .getExceptionalHaltReasons() - .contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION)) { - return true; + } else if (!frame.getExceptionalHaltReasons().isEmpty()) { + final EnumSet haltReasons = frame.getExceptionalHaltReasons(); + return !haltReasons.contains(ExceptionalHaltReason.INVALID_JUMP_DESTINATION) + && !haltReasons.contains(ExceptionalHaltReason.INSUFFICIENT_GAS); } else { return frame.isVirtualOperation(); } } - private void completeStep(final VmOperation op, final VmOperationExecutionReport report) { + private void completeStep( + final TraceFrame frame, final VmOperation op, final VmOperationExecutionReport report) { // add the operation representation to the list of traces - op.setVmOperationExecutionReport(report); + if (frame.getExceptionalHaltReasons().contains(ExceptionalHaltReason.INSUFFICIENT_GAS)) { + op.setVmOperationExecutionReport(null); + } else { + op.setVmOperationExecutionReport(report); + } if (currentTrace != null) { currentTrace.add(op); } currentIndex++; } - private void handleDepthIncreased(final VmOperation op, final VmOperationExecutionReport report) { + private void handleDepthIncreased( + final VmOperation op, + final VmOperationExecutionReport report, + final Optional nextTraceFrame) { // check if next frame depth has increased i.e the current operation is a call switch (currentOperation) { case "STATICCALL": @@ -144,10 +160,15 @@ private void handleDepthIncreased(final VmOperation op, final VmOperationExecuti } }); if (currentTraceFrame.getMaybeCode().map(Code::getSize).orElse(0) > 0) { - op.setCost(currentTraceFrame.getGasRemainingPostExecution().toLong() + op.getCost()); - final VmTrace newSubTrace = new VmTrace(); - parentTraces.addLast(newSubTrace); - op.setSub(newSubTrace); + if (nextTraceFrame.map(TraceFrame::getDepth).orElse(0) > currentTraceFrame.getDepth()) { + op.setCost(currentTraceFrame.getGasRemainingPostExecution().toLong() + op.getCost()); + final VmTrace newSubTrace = new VmTrace(); + parentTraces.addLast(newSubTrace); + op.setSub(newSubTrace); + } else { + op.setCost(op.getCost()); + op.setSub(null); + } } else { if (currentTraceFrame.getPrecompiledGasCost().isPresent()) { op.setCost(op.getCost() + currentTraceFrame.getPrecompiledGasCost().get().toLong()); diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xD.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xD.json index c5ef972bd0d..9b59f473524 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xD.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/all/trace_replayBlockTransactions_all_0xD.json @@ -91,6 +91,12 @@ }, "pc": 33, "sub": null + }, + { + "cost": 9223372036854775807, + "ex": null, + "pc": 66, + "sub": null } ] } diff --git a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_replayBlockTransactions_0xD.json b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_replayBlockTransactions_0xD.json index a2a45406058..d0d9556e157 100644 --- a/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_replayBlockTransactions_0xD.json +++ b/ethereum/api/src/test/resources/org/hyperledger/besu/ethereum/api/jsonrpc/trace/specs/vm-trace/trace_replayBlockTransactions_0xD.json @@ -46,6 +46,12 @@ }, "pc": 33, "sub": null + }, + { + "cost": 9223372036854775807, + "ex": null, + "pc": 66, + "sub": null } ] } From 21e02b12b7e6efb4f676138d98fd1026d2d63727 Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Thu, 13 Feb 2020 03:49:16 +1000 Subject: [PATCH 09/38] adding the plugin-api javadoc jar at the root level (#378) Signed-off-by: Joshua Fernandes --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index 1589d594a3a..840728d0537 100644 --- a/build.gradle +++ b/build.gradle @@ -701,6 +701,7 @@ bintray { filesSpec { from distTar.destinationDirectory from distZip.destinationDirectory + from file("plugin-api/build/libs/plugin-api-$version-javadoc.jar") into '.' } @@ -715,3 +716,4 @@ build.dependsOn verifyDistributions bintrayUpload.dependsOn verifyDistributions bintrayUpload.mustRunAfter(distTar) bintrayUpload.mustRunAfter(distZip) +bintrayUpload.mustRunAfter(javadocJar) From 358458d5a74c16feb99f7e55159463d15f8122b6 Mon Sep 17 00:00:00 2001 From: Antoine Toulme Date: Wed, 12 Feb 2020 10:05:38 -0800 Subject: [PATCH 10/38] BESU-56: remove erroneous links to errorprone checks (#385) Signed-off-by: Antoine Toulme --- .../java/org/hyperledger/errorpronechecks/BannedMethod.java | 3 ++- .../errorpronechecks/DoNotCreateSecureRandomDirectly.java | 3 ++- .../errorpronechecks/DoNotInvokeMessageDigestDirectly.java | 3 ++- .../hyperledger/errorpronechecks/DoNotReturnNullOptionals.java | 3 ++- .../errorpronechecks/MethodInputParametersMustBeFinal.java | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/BannedMethod.java b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/BannedMethod.java index 70d2684fd21..314b6fc2a84 100644 --- a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/BannedMethod.java +++ b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/BannedMethod.java @@ -35,7 +35,8 @@ @BugPattern( name = "BannedMethod", summary = "Some methods should not be used, make sure that doesn't happen.", - severity = WARNING) + severity = WARNING, + linkType = BugPattern.LinkType.NONE) public class BannedMethod extends BugChecker implements MethodInvocationTreeMatcher { private static final ImmutableMap, String> BANNED_METHOD_LIST = diff --git a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotCreateSecureRandomDirectly.java b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotCreateSecureRandomDirectly.java index 38b0afe2870..5af4645768d 100644 --- a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotCreateSecureRandomDirectly.java +++ b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotCreateSecureRandomDirectly.java @@ -32,7 +32,8 @@ @BugPattern( name = "DoNotCreateSecureRandomDirectly", summary = "Do not create SecureRandom directly.", - severity = WARNING) + severity = WARNING, + linkType = BugPattern.LinkType.NONE) public class DoNotCreateSecureRandomDirectly extends BugChecker implements MethodInvocationTreeMatcher, NewClassTreeMatcher { diff --git a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotInvokeMessageDigestDirectly.java b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotInvokeMessageDigestDirectly.java index de370e96bd5..532a82fa461 100644 --- a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotInvokeMessageDigestDirectly.java +++ b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotInvokeMessageDigestDirectly.java @@ -28,7 +28,8 @@ @BugPattern( name = "DoNotInvokeMessageDigestDirectly", summary = "Do not invoke MessageDigest.getInstance directly.", - severity = WARNING) + severity = WARNING, + linkType = BugPattern.LinkType.NONE) public class DoNotInvokeMessageDigestDirectly extends BugChecker implements MethodInvocationTreeMatcher { diff --git a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotReturnNullOptionals.java b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotReturnNullOptionals.java index e20b14315fd..78354610691 100644 --- a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotReturnNullOptionals.java +++ b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/DoNotReturnNullOptionals.java @@ -38,7 +38,8 @@ @BugPattern( name = "DoNotReturnNullOptionals", summary = "Do not return null optionals.", - severity = SUGGESTION) + severity = SUGGESTION, + linkType = BugPattern.LinkType.NONE) public class DoNotReturnNullOptionals extends BugChecker implements MethodTreeMatcher { private static class ReturnNullMatcher implements Matcher { diff --git a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/MethodInputParametersMustBeFinal.java b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/MethodInputParametersMustBeFinal.java index d3692aefd16..c2b31310c7f 100644 --- a/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/MethodInputParametersMustBeFinal.java +++ b/errorprone-checks/src/main/java/org/hyperledger/errorpronechecks/MethodInputParametersMustBeFinal.java @@ -34,7 +34,8 @@ @BugPattern( name = "MethodInputParametersMustBeFinal", summary = "Method input parameters must be final.", - severity = WARNING) + severity = WARNING, + linkType = BugPattern.LinkType.NONE) public class MethodInputParametersMustBeFinal extends BugChecker implements MethodTreeMatcher, ClassTreeMatcher { From 8b8a39c90dece4a9114a2fab8186e2c514d7205b Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Thu, 13 Feb 2020 08:51:48 +1000 Subject: [PATCH 11/38] Changelog entry for multi-tenancy feature (#394) Signed-off-by: Edward Evans Signed-off-by: Jason Frame --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90283d1cff0..eebd5c4f759 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This can be enabled using the `--rpc-http-api TRACE` CLI flag. There are some philosophical differences between Besu and other implementations that are outlined in the `[trace_rpc_apis.md](./docs/trace_rpc_apis.md)` documentation. +- Added [Multi-tenancy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Multi-Tenancy/) support which allows multiple participants to use the same Besu node for private transactions. + ## 1.4 Beta 3 ### Additions and Improvements From 4c72ab7ef32abee63fc4b327a35368f4d8306a15 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Thu, 13 Feb 2020 09:54:51 +1000 Subject: [PATCH 12/38] 1.5 RC Changelog (#395) * 1.5 RC changelog additions Signed-off-by: Sally MacFarlane --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eebd5c4f759..a4c096cd45b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,12 @@ This can be enabled using the `--rpc-http-api TRACE` CLI flag. There are some p - Added [Multi-tenancy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Multi-Tenancy/) support which allows multiple participants to use the same Besu node for private transactions. +- Added TLS support for communication with privacy enclave + +### Bug Fixes + +- Private transactions are now validated before sent to the enclave [\#356](https://github.com/hyperledger/besu/pull/356) + ## 1.4 Beta 3 ### Additions and Improvements From 80e0ee102ec1b95ff57353c301a437e994c0ac8c Mon Sep 17 00:00:00 2001 From: MadelineMurray <43356962+MadelineMurray@users.noreply.github.com> Date: Thu, 13 Feb 2020 11:20:21 +1000 Subject: [PATCH 13/38] Fixed link (#393) Signed-off-by: Madeline Co-authored-by: Edward --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4c096cd45b..aaebbd34e3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ - New`trace_replayBlockTransactions` JSON-RPC API -This can be enabled using the `--rpc-http-api TRACE` CLI flag. There are some philosophical differences between Besu and other implementations that are outlined in the `[trace_rpc_apis.md](./docs/trace_rpc_apis.md)` documentation. +This can be enabled using the `--rpc-http-api TRACE` CLI flag. There are some philosophical differences between Besu and other implementations that are outlined in [trace_rpc_apis](docs/trace_rpc_apis.md). - Added [Multi-tenancy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Multi-Tenancy/) support which allows multiple participants to use the same Besu node for private transactions. From d06c8bfefc0ff037a8d6878296b9c3a17746c4f0 Mon Sep 17 00:00:00 2001 From: Ratan Rai Sur Date: Wed, 12 Feb 2020 20:40:55 -0500 Subject: [PATCH 14/38] docker changelog (#391) * docker changelog Signed-off-by: Ratan Rai Sur * address comments Signed-off-by: Ratan Rai Sur Co-authored-by: MadelineMurray <43356962+MadelineMurray@users.noreply.github.com> --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaebbd34e3d..2f1eb618e99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ This can be enabled using the `--rpc-http-api TRACE` CLI flag. There are some philosophical differences between Besu and other implementations that are outlined in [trace_rpc_apis](docs/trace_rpc_apis.md). +- Ability to automatically detect Docker NAT settings from inside the conainter. + +The default NAT method (AUTO) can detect this so no user intervention is required to enable this. + - Added [Multi-tenancy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Multi-Tenancy/) support which allows multiple participants to use the same Besu node for private transactions. - Added TLS support for communication with privacy enclave From 3728c3ced59172f58598b5b8b7c4bf6bccd5d4db Mon Sep 17 00:00:00 2001 From: MadelineMurray <43356962+MadelineMurray@users.noreply.github.com> Date: Thu, 13 Feb 2020 12:01:33 +1000 Subject: [PATCH 15/38] Added known bug to changelog (#388) * Added known bug Signed-off-by: Madeline * Added another known bug Signed-off-by: Madeline --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f1eb618e99..6bdcfc141c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,16 @@ The default NAT method (AUTO) can detect this so no user intervention is require - Private transactions are now validated before sent to the enclave [\#356](https://github.com/hyperledger/besu/pull/356) +### Known Bugs + +- Error syncing with mainnet on Besu 1.3.7 node - MerkleTrieException [\#BESU-160](https://jira.hyperledger.org/browse/BESU-160) + +Workaround -> Don't enable pruning when syncing to mainnet. + +- Onchain permissioning nodes can't peer when using a non-validator bootnode [\#BESU-181](https://jira.hyperledger.org/browse/BESU-181) + +Workaround -> When using onchain permissioning, ensure bootnodes are also validators. + ## 1.4 Beta 3 ### Additions and Improvements From 9135622c128ba03a47b79fb9fc1a1b9cfdfb00e4 Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Thu, 13 Feb 2020 12:57:19 +1000 Subject: [PATCH 16/38] updating version to 1.4.0-rc1 (#397) Signed-off-by: Joshua Fernandes --- CHANGELOG.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bdcfc141c9..87f3384f738 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 1.4 RC +## 1.4.0 RC-1 ### Additions and Improvements diff --git a/gradle.properties b/gradle.properties index 5e58442077e..805c9fa026f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ org.gradle.jvmargs=-Xmx1g -version=1.4.0-beta4-SNAPSHOT +version=1.4.0-RC1 From d82c8191d90fa234da8326cea6b150d4ccb696a5 Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Thu, 13 Feb 2020 13:17:21 +1000 Subject: [PATCH 17/38] updating verion to 1.4.1-snapshot (#398) Signed-off-by: Joshua Fernandes --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 805c9fa026f..9ed3cc51456 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ org.gradle.jvmargs=-Xmx1g -version=1.4.0-RC1 +version=1.4.1-SNAPSHOT From 6677362598716d4a4519b9095bacb41a5e3c955b Mon Sep 17 00:00:00 2001 From: Abdelhamid Bakhta <45264458+abdelhamidbakhta@users.noreply.github.com> Date: Thu, 13 Feb 2020 16:33:10 +0100 Subject: [PATCH 18/38] [BESU-169] cache logs bloom filters automatically. (#367) * First iteration. Draft PR. Signed-off-by: Abdelhamid Bakhta * fix SPDX header Signed-off-by: Abdelhamid Bakhta * Use block broadcaster to index log bloom. Signed-off-by: Abdelhamid Bakhta * Remove useless toString method Signed-off-by: Abdelhamid Bakhta * spotless apply Signed-off-by: Abdelhamid Bakhta * cacheLogsBloomForBlockHeader Signed-off-by: Abdelhamid Bakhta * spotless apply Signed-off-by: Abdelhamid Bakhta * ensurePreviousSegmentsArePresent Signed-off-by: Abdelhamid Bakhta * Added CLI flag to enable / disable automatic logs bloom indexing. Signed-off-by: Abdelhamid Bakhta * Create cache directory and cache file if not exist. Signed-off-by: Abdelhamid Bakhta * Fix acceptance test Signed-off-by: Abdelhamid Bakhta * Write cache for block only if block is new canonical head. Signed-off-by: Abdelhamid Bakhta * Handling of chain reorg. Signed-off-by: Abdelhamid Bakhta * fix Signed-off-by: Abdelhamid Bakhta * sportless apply Signed-off-by: Abdelhamid Bakhta * Address PR comments. Signed-off-by: Abdelhamid Bakhta * Remove unused constant. Signed-off-by: Abdelhamid Bakhta * spotless apply Signed-off-by: Abdelhamid Bakhta --- .../dsl/node/ProcessBesuNodeRunner.java | 3 + .../dsl/node/ThreadBesuNodeRunner.java | 1 + .../java/org/hyperledger/besu/Runner.java | 14 +++- .../org/hyperledger/besu/RunnerBuilder.java | 10 ++- .../org/hyperledger/besu/cli/BesuCommand.java | 7 ++ .../hyperledger/besu/cli/BesuCommandTest.java | 1 + .../besu/cli/CommandTestAbstract.java | 1 + .../src/test/resources/everything_config.toml | 4 +- .../AutoTransactionLogsIndexingService.java | 75 ++++++++++++++++++ .../api/query/TransactionLogsIndexer.java | 77 ++++++++++++++++++- .../besu/ethereum/chain/Blockchain.java | 18 +++++ .../ethereum/chain/ChainReorgObserver.java | 22 ++++++ .../ethereum/chain/DefaultBlockchain.java | 18 ++++- .../besu/ethereum/vm/TestBlockchain.java | 11 +++ 14 files changed, 253 insertions(+), 9 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogsIndexingService.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainReorgObserver.java diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java index ae27c30e130..829fad59900 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java @@ -257,6 +257,9 @@ public void startNode(final BesuNode node) { params.add("--key-value-storage"); params.add("rocksdb"); + params.add("--auto-logs-bloom-indexing-enabled"); + params.add("false"); + LOG.info("Creating besu process with params {}", params); final ProcessBuilder processBuilder = new ProcessBuilder(params) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index 68e0d3cbeab..a3fce047b3e 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -195,6 +195,7 @@ public void startNode(final BesuNode node) { .map(EnodeURL::fromString) .collect(Collectors.toList())) .besuPluginContext(new BesuPluginContextImpl()) + .autoLogsBloomIndexing(false) .build(); runner.start(); diff --git a/besu/src/main/java/org/hyperledger/besu/Runner.java b/besu/src/main/java/org/hyperledger/besu/Runner.java index 08d8880bf7e..4b67f5ec61c 100644 --- a/besu/src/main/java/org/hyperledger/besu/Runner.java +++ b/besu/src/main/java/org/hyperledger/besu/Runner.java @@ -18,6 +18,9 @@ import org.hyperledger.besu.ethereum.api.graphql.GraphQLHttpService; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcHttpService; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketService; +import org.hyperledger.besu.ethereum.api.query.AutoTransactionLogsIndexingService; +import org.hyperledger.besu.ethereum.api.query.TransactionLogsIndexer; +import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.p2p.network.NetworkRunner; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL; import org.hyperledger.besu.ethereum.stratum.StratumServer; @@ -58,6 +61,7 @@ public class Runner implements AutoCloseable { private final BesuController besuController; private final Path dataDir; private final Optional stratumServer; + private final Optional autoTransactionLogsIndexingService; Runner( final Vertx vertx, @@ -69,7 +73,9 @@ public class Runner implements AutoCloseable { final Optional stratumServer, final Optional metrics, final BesuController besuController, - final Path dataDir) { + final Path dataDir, + final Optional transactionLogsIndexer, + final Blockchain blockchain) { this.vertx = vertx; this.networkRunner = networkRunner; this.natService = natService; @@ -80,6 +86,9 @@ public class Runner implements AutoCloseable { this.besuController = besuController; this.dataDir = dataDir; this.stratumServer = stratumServer; + this.autoTransactionLogsIndexingService = + transactionLogsIndexer.map( + indexer -> new AutoTransactionLogsIndexingService(blockchain, indexer)); } public void start() { @@ -103,6 +112,7 @@ public void start() { LOG.info("Ethereum main loop is up."); writeBesuPortsToFile(); writeBesuNetworksToFile(); + autoTransactionLogsIndexingService.ifPresent(AutoTransactionLogsIndexingService::start); } catch (final Exception ex) { LOG.error("Startup failed", ex); throw new IllegalStateException(ex); @@ -125,7 +135,7 @@ public void stop() { networkRunner.stop(); waitForServiceToStop("Network", networkRunner::awaitStop); - + autoTransactionLogsIndexingService.ifPresent(AutoTransactionLogsIndexingService::stop); natService.stop(); besuController.close(); vertx.close((res) -> vertxShutdownLatch.countDown()); diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index ff65476fde9..dc0e1754976 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -144,6 +144,7 @@ public class RunnerBuilder { private Collection staticNodes = Collections.emptyList(); private Optional identityString = Optional.empty(); private BesuPluginContextImpl besuPluginContext; + private boolean autoLogsBloomIndexing = true; public RunnerBuilder vertx(final Vertx vertx) { this.vertx = vertx; @@ -270,6 +271,11 @@ public RunnerBuilder besuPluginContext(final BesuPluginContextImpl besuPluginCon return this; } + public RunnerBuilder autoLogsBloomIndexing(final boolean autoLogsBloomIndexing) { + this.autoLogsBloomIndexing = autoLogsBloomIndexing; + return this; + } + public Runner build() { Preconditions.checkNotNull(besuController); @@ -527,7 +533,9 @@ public Runner build() { stratumServer, metricsService, besuController, - dataDir); + dataDir, + autoLogsBloomIndexing ? blockchainQueries.getTransactionLogsIndexer() : Optional.empty(), + context.getBlockchain()); } private Optional buildNodePermissioningController( diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index fc57772a0db..1a11521e98b 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -790,6 +790,12 @@ void setBannedNodeIds(final List values) { arity = "1") private String keyValueStorageName = DEFAULT_KEY_VALUE_STORAGE_NAME; + @Option( + names = {"--auto-logs-bloom-indexing-enabled"}, + description = "Enable Automatic logs bloom indexing (default: ${DEFAULT-VALUE})", + arity = "1") + private final Boolean autoLogsBloomIndexingEnabled = true; + @Option( names = {"--override-genesis-config"}, paramLabel = "NAME=VALUE", @@ -1695,6 +1701,7 @@ private void synchronize( .staticNodes(staticNodes) .identityString(identityString) .besuPluginContext(besuPluginContext) + .autoLogsBloomIndexing(autoLogsBloomIndexingEnabled) .build(); addShutdownHook(runner); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 5883dbc42e3..373cbd2a995 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -175,6 +175,7 @@ public void callingBesuCommandWithoutOptionsMustSyncWithDefaultValues() throws E verify(mockRunnerBuilder).webSocketConfiguration(eq(DEFAULT_WEB_SOCKET_CONFIGURATION)); verify(mockRunnerBuilder).metricsConfiguration(eq(DEFAULT_METRICS_CONFIGURATION)); verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkArg.capture()); + verify(mockRunnerBuilder).autoLogsBloomIndexing(eq(true)); verify(mockRunnerBuilder).build(); verify(mockControllerBuilderFactory).fromEthNetworkConfig(ethNetworkArg.capture(), any()); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index 9b4d6db7cb5..4d4f31ed8ee 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -220,6 +220,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.staticNodes(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.identityString(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.besuPluginContext(any())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.autoLogsBloomIndexing(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); when(storageService.getByName("rocksdb")).thenReturn(Optional.of(rocksDBStorageFactory)); diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index 56e60dc23b0..c0b19c30033 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -134,4 +134,6 @@ revert-reason-enabled=false key-value-storage="rocksdb" # Gas limit -target-gas-limit=8000000 \ No newline at end of file +target-gas-limit=8000000 + +auto-logs-bloom-indexing-enabled=true \ No newline at end of file diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogsIndexingService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogsIndexingService.java new file mode 100644 index 00000000000..b1d71cf0890 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogsIndexingService.java @@ -0,0 +1,75 @@ +/* + * 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.query; + +import org.hyperledger.besu.ethereum.chain.Blockchain; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.util.OptionalLong; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class AutoTransactionLogsIndexingService { + protected static final Logger LOG = LogManager.getLogger(); + private final Blockchain blockchain; + private final TransactionLogsIndexer transactionLogsIndexer; + private OptionalLong blockAddedSubscriptionId = OptionalLong.empty(); + private OptionalLong chainReorgSubscriptionId = OptionalLong.empty(); + + public AutoTransactionLogsIndexingService( + final Blockchain blockchain, final TransactionLogsIndexer transactionLogsIndexer) { + this.blockchain = blockchain; + this.transactionLogsIndexer = transactionLogsIndexer; + } + + public void start() { + try { + LOG.info("Starting Auto transaction logs indexing service."); + final Path cacheDir = transactionLogsIndexer.getCacheDir(); + if (!cacheDir.toFile().exists() || !cacheDir.toFile().isDirectory()) { + Files.createDirectory(cacheDir); + } + blockAddedSubscriptionId = + OptionalLong.of( + blockchain.observeBlockAdded( + (event, __) -> { + if (event.isNewCanonicalHead()) { + transactionLogsIndexer.cacheLogsBloomForBlockHeader( + event.getBlock().getHeader()); + } + })); + chainReorgSubscriptionId = + OptionalLong.of( + blockchain.observeChainReorg( + (header, __) -> transactionLogsIndexer.cacheLogsBloomForBlockHeader(header))); + + transactionLogsIndexer + .getScheduler() + .scheduleFutureTask(transactionLogsIndexer::indexAll, Duration.ofMinutes(1)); + } catch (IOException e) { + LOG.error("Unhandled indexing exception.", e); + } + } + + public void stop() { + LOG.info("Shutting down Auto transaction logs indexing service."); + blockAddedSubscriptionId.ifPresent(blockchain::removeObserver); + chainReorgSubscriptionId.ifPresent(blockchain::removeChainReorgObserver); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogsIndexer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogsIndexer.java index 11223e04c0d..ad7ed9d9da2 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogsIndexer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogsIndexer.java @@ -27,10 +27,14 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.time.Duration; +import java.util.Map; import java.util.Optional; +import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -44,11 +48,15 @@ public class TransactionLogsIndexer { private static final Logger LOG = LogManager.getLogger(); public static final int BLOCKS_PER_BLOOM_CACHE = 100_000; + private static final int BLOOM_BITS_LENGTH = 256; public static final String PENDING = "pending"; + private final Map cachedSegments; private final Lock submissionLock = new ReentrantLock(); + private final EthScheduler scheduler; private final Blockchain blockchain; + private final Path cacheDir; private final IndexingStatus indexingStatus = new IndexingStatus(); @@ -58,6 +66,11 @@ public TransactionLogsIndexer( this.blockchain = blockchain; this.cacheDir = cacheDir; this.scheduler = scheduler; + this.cachedSegments = new TreeMap<>(); + } + + public void indexAll() { + ensurePreviousSegmentsArePresent(blockchain.getChainHeadBlockNumber()); } private static File calculateCacheFileName(final String name, final Path cacheDir) { @@ -117,16 +130,64 @@ private long fillCacheFile( if (maybeHeader.isEmpty()) { break; } - final byte[] logs = maybeHeader.get().getLogsBloom().toArray(); - checkNotNull(logs); - checkState(logs.length == 256, "BloomBits are not the correct length"); - fos.write(logs); + fillCacheFileWithBlock(maybeHeader.get(), fos); indexingStatus.currentBlock = blockNum; blockNum++; } return blockNum - startBlock; } + public void cacheLogsBloomForBlockHeader(final BlockHeader blockHeader) { + try { + final long blockNumber = blockHeader.getNumber(); + LOG.debug("Caching logs bloom for block {}.", "0x" + Long.toHexString(blockNumber)); + ensurePreviousSegmentsArePresent(blockNumber); + final File cacheFile = calculateCacheFileName(blockNumber, cacheDir); + if (!cacheFile.exists()) { + Files.createFile(cacheFile.toPath()); + } + try (RandomAccessFile writer = new RandomAccessFile(cacheFile, "rw")) { + final long offset = (blockNumber / BLOCKS_PER_BLOOM_CACHE) * BLOOM_BITS_LENGTH; + writer.seek(offset); + writer.write(ensureBloomBitsAreCorrectLength(blockHeader.getLogsBloom().toArray())); + } + } catch (IOException e) { + LOG.error("Unhandled indexing exception.", e); + } + } + + private void ensurePreviousSegmentsArePresent(final long blockNumber) { + if (!indexingStatus.isIndexing()) { + scheduler.scheduleFutureTask( + () -> { + long currentSegment = (blockNumber / BLOCKS_PER_BLOOM_CACHE) - 1; + while (currentSegment > 0) { + try { + if (!cachedSegments.getOrDefault(currentSegment, false)) { + final long startBlock = currentSegment * BLOCKS_PER_BLOOM_CACHE; + generateLogBloomCache(startBlock, startBlock + BLOCKS_PER_BLOOM_CACHE); + cachedSegments.put(currentSegment, true); + } + } finally { + currentSegment--; + } + } + }, + Duration.ofSeconds(1)); + } + } + + private void fillCacheFileWithBlock(final BlockHeader blockHeader, final FileOutputStream fos) + throws IOException { + fos.write(ensureBloomBitsAreCorrectLength(blockHeader.getLogsBloom().toArray())); + } + + private byte[] ensureBloomBitsAreCorrectLength(final byte[] logs) { + checkNotNull(logs); + checkState(logs.length == BLOOM_BITS_LENGTH, "BloomBits are not the correct length"); + return logs; + } + public IndexingStatus requestIndexing(final long fromBlock, final long toBlock) { boolean requestAccepted = false; try { @@ -152,6 +213,14 @@ public IndexingStatus requestIndexing(final long fromBlock, final long toBlock) return indexingStatus; } + public EthScheduler getScheduler() { + return scheduler; + } + + Path getCacheDir() { + return cacheDir; + } + public static final class IndexingStatus { long startBlock; long endBlock; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/Blockchain.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/Blockchain.java index 5e9e59816fa..f0b7a157203 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/Blockchain.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/Blockchain.java @@ -206,4 +206,22 @@ default long observeLogs(final Consumer logObserver) { * @return {@code true} if the observer was removed; otherwise {@code false} */ boolean removeObserver(long observerId); + + /** + * Adds an observer that will get called when a new block is added after reorg. + * + *

No guarantees are made about the order in which observers are invoked. + * + * @param observer the observer to call + * @return the observer ID that can be used to remove it later. + */ + long observeChainReorg(ChainReorgObserver observer); + + /** + * Removes a previously added {@link ChainReorgObserver}. + * + * @param observerId the ID of the observer to remove + * @return {@code true} if the observer was removed; otherwise {@code false} + */ + boolean removeChainReorgObserver(long observerId); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainReorgObserver.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainReorgObserver.java new file mode 100644 index 00000000000..aeed1411295 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/ChainReorgObserver.java @@ -0,0 +1,22 @@ +/* + * 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.chain; + +import org.hyperledger.besu.ethereum.core.BlockHeader; + +public interface ChainReorgObserver { + + void onBlockAdded(BlockHeader blockHeader, Blockchain blockchain); +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java index e64c03f2200..0602882c532 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/chain/DefaultBlockchain.java @@ -53,6 +53,7 @@ public class DefaultBlockchain implements MutableBlockchain { protected final BlockchainStorage blockchainStorage; private final Subscribers blockAddedObservers = Subscribers.create(); + private final Subscribers blockReorgObservers = Subscribers.create(); private volatile BlockHeader chainHeader; private volatile Difficulty totalDifficulty; @@ -344,7 +345,7 @@ private BlockAddedEvent handleChainReorg( newTransactions.put( blockHash, currentNewChainWithReceipts.getBlock().getBody().getTransactions()); addAddedLogsWithMetadata(addedLogsWithMetadata, currentNewChainWithReceipts); - + notifyChainReorgBlockAdded(currentNewChainWithReceipts.getHeader()); currentNewChainWithReceipts = getParentBlockWithReceipts(currentNewChainWithReceipts); } @@ -547,6 +548,17 @@ public boolean removeObserver(final long observerId) { return blockAddedObservers.unsubscribe(observerId); } + @Override + public long observeChainReorg(final ChainReorgObserver observer) { + checkNotNull(observer); + return blockReorgObservers.subscribe(observer); + } + + @Override + public boolean removeChainReorgObserver(final long observerId) { + return blockReorgObservers.unsubscribe(observerId); + } + @VisibleForTesting int observerCount() { return blockAddedObservers.getSubscriberCount(); @@ -555,4 +567,8 @@ int observerCount() { private void notifyBlockAdded(final BlockAddedEvent event) { blockAddedObservers.forEach(observer -> observer.onBlockAdded(event, this)); } + + private void notifyChainReorgBlockAdded(final BlockHeader blockHeader) { + blockReorgObservers.forEach(observer -> observer.onBlockAdded(blockHeader, this)); + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/TestBlockchain.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/TestBlockchain.java index 4e9991549c5..9f1790fea81 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/TestBlockchain.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/TestBlockchain.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.chain.BlockAddedObserver; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.ChainHead; +import org.hyperledger.besu.ethereum.chain.ChainReorgObserver; import org.hyperledger.besu.ethereum.chain.TransactionLocation; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.BlockHeader; @@ -149,6 +150,16 @@ public boolean removeObserver(final long observerId) { throw new NonDeterministicOperationException("Listening for new blocks is not deterministic"); } + @Override + public long observeChainReorg(final ChainReorgObserver observer) { + throw new NonDeterministicOperationException("Listening for chain reorg is not deterministic"); + } + + @Override + public boolean removeChainReorgObserver(final long observerId) { + throw new NonDeterministicOperationException("Listening for chain reorg is not deterministic"); + } + public static class NonDeterministicOperationException extends RuntimeException { public NonDeterministicOperationException(final String message) { super(message); From bb0c9cb017cab05931f7975f4995b13e89622f66 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 13 Feb 2020 09:29:58 -0700 Subject: [PATCH 19/38] [BESU-25] Use Devp2p Ping packets at v5 (#392) Broadcast that we support snappy compression Signed-off-by: Danno Ferrin --- CHANGELOG.md | 7 +++++++ .../ethereum/p2p/discovery/internal/PingPacketData.java | 4 ++-- .../p2p/discovery/PeerDiscoveryPacketPcapSedesTest.java | 3 ++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87f3384f738..096b4826311 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 1.4.1 + +### Bug Fixes + +- [BESU-25](https://jira.hyperledger.org/browse/BESU-25) Use v5 Devp2p when pinging [\#392](https://github.com/hyperledger/besu/pull/392) + + ## 1.4.0 RC-1 ### Additions and Improvements diff --git a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PingPacketData.java b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PingPacketData.java index a3fba289a0f..214115e8a10 100644 --- a/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PingPacketData.java +++ b/ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PingPacketData.java @@ -22,8 +22,8 @@ public class PingPacketData implements PacketData { - /* Fixed value that represents we're using v4 of the P2P discovery protocol. */ - private static final int VERSION = 4; + /* Fixed value that represents we're using v5 of the P2P discovery protocol. */ + private static final int VERSION = 5; /* Source. */ private final Endpoint from; diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryPacketPcapSedesTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryPacketPcapSedesTest.java index ba4366d01ef..29c0aae6946 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryPacketPcapSedesTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryPacketPcapSedesTest.java @@ -90,7 +90,8 @@ public void serializeDeserialize() { ping.getTo().getTcpPort().ifPresent(p -> assertThat(p).isPositive()); ping.getFrom().getTcpPort().ifPresent(p -> assertThat(p).isPositive()); assertThat(ping.getExpiration()).isPositive(); - break; + // because of the version upgrade the ping packet won't re-serialize, so we're done + return; case PONG: assertThat(packet.getPacketData(PongPacketData.class)).isPresent(); From 20664f96da734e5dc4179edf752ddabc2d32ed6f Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 13 Feb 2020 23:15:43 -0700 Subject: [PATCH 20/38] Rename logs bloom indexer to log bloom cache to match CLI flag. (#401) Changes class names, variables, and CLI flags as needed. Signed-off-by: Danno Ferrin --- .../dsl/node/ProcessBesuNodeRunner.java | 2 +- .../dsl/node/ThreadBesuNodeRunner.java | 2 +- .../java/org/hyperledger/besu/Runner.java | 19 +++---- .../org/hyperledger/besu/RunnerBuilder.java | 8 +-- .../org/hyperledger/besu/cli/BesuCommand.java | 8 +-- .../operator/GenerateLogBloomCache.java | 14 ++--- .../hyperledger/besu/cli/BesuCommandTest.java | 2 +- .../besu/cli/CommandTestAbstract.java | 2 +- .../src/test/resources/everything_config.toml | 3 +- .../methods/AdminGenerateLogBloomCache.java | 4 +- ...utoTransactionLogBloomCachingService.java} | 26 +++++----- .../ethereum/api/query/BlockchainQueries.java | 13 ++--- ...er.java => TransactionLogBloomCacher.java} | 52 +++++++++---------- .../AdminGenerateLogBloomCacheTest.java | 18 +++---- .../query/BlockchainQueriesLogCacheTest.java | 2 +- 15 files changed, 89 insertions(+), 86 deletions(-) rename ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/{AutoTransactionLogsIndexingService.java => AutoTransactionLogBloomCachingService.java} (69%) rename ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/{TransactionLogsIndexer.java => TransactionLogBloomCacher.java} (86%) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java index 829fad59900..73bc5c43ff0 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java @@ -257,7 +257,7 @@ public void startNode(final BesuNode node) { params.add("--key-value-storage"); params.add("rocksdb"); - params.add("--auto-logs-bloom-indexing-enabled"); + params.add("--auto-log-bloom-caching-enabled"); params.add("false"); LOG.info("Creating besu process with params {}", params); diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index a3fce047b3e..a73779aee6f 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -195,7 +195,7 @@ public void startNode(final BesuNode node) { .map(EnodeURL::fromString) .collect(Collectors.toList())) .besuPluginContext(new BesuPluginContextImpl()) - .autoLogsBloomIndexing(false) + .autoLogBloomCaching(false) .build(); runner.start(); diff --git a/besu/src/main/java/org/hyperledger/besu/Runner.java b/besu/src/main/java/org/hyperledger/besu/Runner.java index 4b67f5ec61c..f298611ad7c 100644 --- a/besu/src/main/java/org/hyperledger/besu/Runner.java +++ b/besu/src/main/java/org/hyperledger/besu/Runner.java @@ -18,8 +18,8 @@ import org.hyperledger.besu.ethereum.api.graphql.GraphQLHttpService; import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcHttpService; import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketService; -import org.hyperledger.besu.ethereum.api.query.AutoTransactionLogsIndexingService; -import org.hyperledger.besu.ethereum.api.query.TransactionLogsIndexer; +import org.hyperledger.besu.ethereum.api.query.AutoTransactionLogBloomCachingService; +import org.hyperledger.besu.ethereum.api.query.TransactionLogBloomCacher; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.p2p.network.NetworkRunner; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL; @@ -61,7 +61,8 @@ public class Runner implements AutoCloseable { private final BesuController besuController; private final Path dataDir; private final Optional stratumServer; - private final Optional autoTransactionLogsIndexingService; + private final Optional + autoTransactionLogBloomCachingService; Runner( final Vertx vertx, @@ -74,7 +75,7 @@ public class Runner implements AutoCloseable { final Optional metrics, final BesuController besuController, final Path dataDir, - final Optional transactionLogsIndexer, + final Optional transactionLogBloomCacher, final Blockchain blockchain) { this.vertx = vertx; this.networkRunner = networkRunner; @@ -86,9 +87,9 @@ public class Runner implements AutoCloseable { this.besuController = besuController; this.dataDir = dataDir; this.stratumServer = stratumServer; - this.autoTransactionLogsIndexingService = - transactionLogsIndexer.map( - indexer -> new AutoTransactionLogsIndexingService(blockchain, indexer)); + this.autoTransactionLogBloomCachingService = + transactionLogBloomCacher.map( + cacher -> new AutoTransactionLogBloomCachingService(blockchain, cacher)); } public void start() { @@ -112,7 +113,7 @@ public void start() { LOG.info("Ethereum main loop is up."); writeBesuPortsToFile(); writeBesuNetworksToFile(); - autoTransactionLogsIndexingService.ifPresent(AutoTransactionLogsIndexingService::start); + autoTransactionLogBloomCachingService.ifPresent(AutoTransactionLogBloomCachingService::start); } catch (final Exception ex) { LOG.error("Startup failed", ex); throw new IllegalStateException(ex); @@ -135,7 +136,7 @@ public void stop() { networkRunner.stop(); waitForServiceToStop("Network", networkRunner::awaitStop); - autoTransactionLogsIndexingService.ifPresent(AutoTransactionLogsIndexingService::stop); + autoTransactionLogBloomCachingService.ifPresent(AutoTransactionLogBloomCachingService::stop); natService.stop(); besuController.close(); vertx.close((res) -> vertxShutdownLatch.countDown()); diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index dc0e1754976..243da5cc07e 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -144,7 +144,7 @@ public class RunnerBuilder { private Collection staticNodes = Collections.emptyList(); private Optional identityString = Optional.empty(); private BesuPluginContextImpl besuPluginContext; - private boolean autoLogsBloomIndexing = true; + private boolean autoLogBloomCaching = true; public RunnerBuilder vertx(final Vertx vertx) { this.vertx = vertx; @@ -271,8 +271,8 @@ public RunnerBuilder besuPluginContext(final BesuPluginContextImpl besuPluginCon return this; } - public RunnerBuilder autoLogsBloomIndexing(final boolean autoLogsBloomIndexing) { - this.autoLogsBloomIndexing = autoLogsBloomIndexing; + public RunnerBuilder autoLogBloomCaching(final boolean autoLogBloomCaching) { + this.autoLogBloomCaching = autoLogBloomCaching; return this; } @@ -534,7 +534,7 @@ public Runner build() { metricsService, besuController, dataDir, - autoLogsBloomIndexing ? blockchainQueries.getTransactionLogsIndexer() : Optional.empty(), + autoLogBloomCaching ? blockchainQueries.getTransactionLogBloomCacher() : Optional.empty(), context.getBlockchain()); } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index 1a11521e98b..ec969df7279 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -791,10 +791,10 @@ void setBannedNodeIds(final List values) { private String keyValueStorageName = DEFAULT_KEY_VALUE_STORAGE_NAME; @Option( - names = {"--auto-logs-bloom-indexing-enabled"}, - description = "Enable Automatic logs bloom indexing (default: ${DEFAULT-VALUE})", + names = {"--auto-log-bloom-caching-enabled"}, + description = "Enable automatic log bloom caching (default: ${DEFAULT-VALUE})", arity = "1") - private final Boolean autoLogsBloomIndexingEnabled = true; + private final Boolean autoLogBloomCachingEnabled = true; @Option( names = {"--override-genesis-config"}, @@ -1701,7 +1701,7 @@ private void synchronize( .staticNodes(staticNodes) .identityString(identityString) .besuPluginContext(besuPluginContext) - .autoLogsBloomIndexing(autoLogsBloomIndexingEnabled) + .autoLogBloomCaching(autoLogBloomCachingEnabled) .build(); addShutdownHook(runner); diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateLogBloomCache.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateLogBloomCache.java index aeb089fe150..f2f6e4af69b 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateLogBloomCache.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateLogBloomCache.java @@ -19,10 +19,10 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static org.hyperledger.besu.cli.DefaultCommandValues.MANDATORY_LONG_FORMAT_HELP; -import static org.hyperledger.besu.ethereum.api.query.TransactionLogsIndexer.BLOCKS_PER_BLOOM_CACHE; +import static org.hyperledger.besu.ethereum.api.query.TransactionLogBloomCacher.BLOCKS_PER_BLOOM_CACHE; import org.hyperledger.besu.controller.BesuController; -import org.hyperledger.besu.ethereum.api.query.TransactionLogsIndexer; +import org.hyperledger.besu.ethereum.api.query.TransactionLogBloomCacher; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; @@ -43,7 +43,7 @@ public class GenerateLogBloomCache implements Runnable { names = "--start-block", paramLabel = MANDATORY_LONG_FORMAT_HELP, description = - "The block to start generating indexes. Must be an increment of " + "The block to start generating the cache. Must be an increment of " + BLOCKS_PER_BLOOM_CACHE + " (default: ${DEFAULT-VALUE})", arity = "1..1") @@ -52,7 +52,7 @@ public class GenerateLogBloomCache implements Runnable { @Option( names = "--end-block", paramLabel = MANDATORY_LONG_FORMAT_HELP, - description = "The block to stop generating indexes (default is last block of the chain).", + description = "The block to stop generating the cache (default is last block of the chain).", arity = "1..1") private final Long endBlock = Long.MAX_VALUE; @@ -69,9 +69,9 @@ public void run() { final EthScheduler scheduler = new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()); try { final long finalBlock = Math.min(blockchain.getChainHeadBlockNumber(), endBlock); - final TransactionLogsIndexer indexer = - new TransactionLogsIndexer(blockchain, cacheDir, scheduler); - indexer.generateLogBloomCache(startBlock, finalBlock); + final TransactionLogBloomCacher cacher = + new TransactionLogBloomCacher(blockchain, cacheDir, scheduler); + cacher.generateLogBloomCache(startBlock, finalBlock); } finally { scheduler.stop(); try { diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 373cbd2a995..985b7bd8884 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -175,7 +175,7 @@ public void callingBesuCommandWithoutOptionsMustSyncWithDefaultValues() throws E verify(mockRunnerBuilder).webSocketConfiguration(eq(DEFAULT_WEB_SOCKET_CONFIGURATION)); verify(mockRunnerBuilder).metricsConfiguration(eq(DEFAULT_METRICS_CONFIGURATION)); verify(mockRunnerBuilder).ethNetworkConfig(ethNetworkArg.capture()); - verify(mockRunnerBuilder).autoLogsBloomIndexing(eq(true)); + verify(mockRunnerBuilder).autoLogBloomCaching(eq(true)); verify(mockRunnerBuilder).build(); verify(mockControllerBuilderFactory).fromEthNetworkConfig(ethNetworkArg.capture(), any()); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index 4d4f31ed8ee..e689fffd7ee 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -220,7 +220,7 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.staticNodes(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.identityString(any())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.besuPluginContext(any())).thenReturn(mockRunnerBuilder); - when(mockRunnerBuilder.autoLogsBloomIndexing(anyBoolean())).thenReturn(mockRunnerBuilder); + when(mockRunnerBuilder.autoLogBloomCaching(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); when(storageService.getByName("rocksdb")).thenReturn(Optional.of(rocksDBStorageFactory)); diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index c0b19c30033..5fbc6f48470 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -136,4 +136,5 @@ key-value-storage="rocksdb" # Gas limit target-gas-limit=8000000 -auto-logs-bloom-indexing-enabled=true \ No newline at end of file +# transaction log bloom filter caching +auto-log-bloom-caching-enabled=true \ No newline at end of file diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminGenerateLogBloomCache.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminGenerateLogBloomCache.java index 24d6bdab150..3cb5e77127d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminGenerateLogBloomCache.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminGenerateLogBloomCache.java @@ -73,8 +73,8 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { return new JsonRpcSuccessResponse( requestContext.getRequest().getId(), blockchainQueries - .getTransactionLogsIndexer() - .map(indexer -> indexer.requestIndexing(startBlock, stopBlock)) + .getTransactionLogBloomCacher() + .map(cacher -> cacher.requestCaching(startBlock, stopBlock)) .orElse(null)); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogsIndexingService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java similarity index 69% rename from ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogsIndexingService.java rename to ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java index b1d71cf0890..ed7b84fc655 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogsIndexingService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java @@ -25,23 +25,23 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class AutoTransactionLogsIndexingService { +public class AutoTransactionLogBloomCachingService { protected static final Logger LOG = LogManager.getLogger(); private final Blockchain blockchain; - private final TransactionLogsIndexer transactionLogsIndexer; + private final TransactionLogBloomCacher transactionLogBloomCacher; private OptionalLong blockAddedSubscriptionId = OptionalLong.empty(); private OptionalLong chainReorgSubscriptionId = OptionalLong.empty(); - public AutoTransactionLogsIndexingService( - final Blockchain blockchain, final TransactionLogsIndexer transactionLogsIndexer) { + public AutoTransactionLogBloomCachingService( + final Blockchain blockchain, final TransactionLogBloomCacher transactionLogBloomCacher) { this.blockchain = blockchain; - this.transactionLogsIndexer = transactionLogsIndexer; + this.transactionLogBloomCacher = transactionLogBloomCacher; } public void start() { try { - LOG.info("Starting Auto transaction logs indexing service."); - final Path cacheDir = transactionLogsIndexer.getCacheDir(); + LOG.info("Starting auto transaction log bloom caching service."); + final Path cacheDir = transactionLogBloomCacher.getCacheDir(); if (!cacheDir.toFile().exists() || !cacheDir.toFile().isDirectory()) { Files.createDirectory(cacheDir); } @@ -50,25 +50,25 @@ public void start() { blockchain.observeBlockAdded( (event, __) -> { if (event.isNewCanonicalHead()) { - transactionLogsIndexer.cacheLogsBloomForBlockHeader( + transactionLogBloomCacher.cacheLogsBloomForBlockHeader( event.getBlock().getHeader()); } })); chainReorgSubscriptionId = OptionalLong.of( blockchain.observeChainReorg( - (header, __) -> transactionLogsIndexer.cacheLogsBloomForBlockHeader(header))); + (header, __) -> transactionLogBloomCacher.cacheLogsBloomForBlockHeader(header))); - transactionLogsIndexer + transactionLogBloomCacher .getScheduler() - .scheduleFutureTask(transactionLogsIndexer::indexAll, Duration.ofMinutes(1)); + .scheduleFutureTask(transactionLogBloomCacher::cacheAll, Duration.ofMinutes(1)); } catch (IOException e) { - LOG.error("Unhandled indexing exception.", e); + LOG.error("Unhandled caching exception.", e); } } public void stop() { - LOG.info("Shutting down Auto transaction logs indexing service."); + LOG.info("Shutting down Auto transaction logs caching service."); blockAddedSubscriptionId.ifPresent(blockchain::removeObserver); chainReorgSubscriptionId.ifPresent(blockchain::removeChainReorgObserver); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java index de70adb5b0b..4b49d760e8e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueries.java @@ -15,7 +15,7 @@ package org.hyperledger.besu.ethereum.api.query; import static com.google.common.base.Preconditions.checkArgument; -import static org.hyperledger.besu.ethereum.api.query.TransactionLogsIndexer.BLOCKS_PER_BLOOM_CACHE; +import static org.hyperledger.besu.ethereum.api.query.TransactionLogBloomCacher.BLOCKS_PER_BLOOM_CACHE; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.TransactionLocation; @@ -61,7 +61,7 @@ public class BlockchainQueries { private final WorldStateArchive worldStateArchive; private final Blockchain blockchain; private final Optional cachePath; - private final Optional transactionLogsIndexer; + private final Optional transactionLogBloomCacher; public BlockchainQueries(final Blockchain blockchain, final WorldStateArchive worldStateArchive) { this(blockchain, worldStateArchive, Optional.empty(), Optional.empty()); @@ -82,9 +82,10 @@ public BlockchainQueries( this.blockchain = blockchain; this.worldStateArchive = worldStateArchive; this.cachePath = cachePath; - this.transactionLogsIndexer = + this.transactionLogBloomCacher = (cachePath.isPresent() && scheduler.isPresent()) - ? Optional.of(new TransactionLogsIndexer(blockchain, cachePath.get(), scheduler.get())) + ? Optional.of( + new TransactionLogBloomCacher(blockchain, cachePath.get(), scheduler.get())) : Optional.empty(); } @@ -96,8 +97,8 @@ public WorldStateArchive getWorldStateArchive() { return worldStateArchive; } - public Optional getTransactionLogsIndexer() { - return transactionLogsIndexer; + public Optional getTransactionLogBloomCacher() { + return transactionLogBloomCacher; } /** diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogsIndexer.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java similarity index 86% rename from ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogsIndexer.java rename to ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java index ad7ed9d9da2..e72d274fca8 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogsIndexer.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java @@ -43,7 +43,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class TransactionLogsIndexer { +public class TransactionLogBloomCacher { private static final Logger LOG = LogManager.getLogger(); @@ -59,9 +59,9 @@ public class TransactionLogsIndexer { private final Path cacheDir; - private final IndexingStatus indexingStatus = new IndexingStatus(); + private final CachingStatus cachingStatus = new CachingStatus(); - public TransactionLogsIndexer( + public TransactionLogBloomCacher( final Blockchain blockchain, final Path cacheDir, final EthScheduler scheduler) { this.blockchain = blockchain; this.cacheDir = cacheDir; @@ -69,7 +69,7 @@ public TransactionLogsIndexer( this.cachedSegments = new TreeMap<>(); } - public void indexAll() { + public void cacheAll() { ensurePreviousSegmentsArePresent(blockchain.getChainHeadBlockNumber()); } @@ -81,24 +81,24 @@ private static File calculateCacheFileName(final long blockNumber, final Path ca return calculateCacheFileName(Long.toString(blockNumber / BLOCKS_PER_BLOOM_CACHE), cacheDir); } - public IndexingStatus generateLogBloomCache(final long start, final long stop) { + public CachingStatus generateLogBloomCache(final long start, final long stop) { checkArgument( start % BLOCKS_PER_BLOOM_CACHE == 0, "Start block must be at the beginning of a file"); try { - indexingStatus.indexing = true; + cachingStatus.caching = true; LOG.info( - "Generating transaction log indexes from block {} to block {} in {}", + "Generating transaction log bloom cache from block {} to block {} in {}", start, stop, cacheDir); if (!Files.isDirectory(cacheDir) && !cacheDir.toFile().mkdirs()) { LOG.error("Cache directory '{}' does not exist and could not be made.", cacheDir); - return indexingStatus; + return cachingStatus; } final File pendingFile = calculateCacheFileName(PENDING, cacheDir); for (long blockNum = start; blockNum < stop; blockNum += BLOCKS_PER_BLOOM_CACHE) { - LOG.info("Indexing segment at {}", blockNum); + LOG.info("Caching segment at {}", blockNum); try (final FileOutputStream fos = new FileOutputStream(pendingFile)) { final long blockCount = fillCacheFile(blockNum, blockNum + BLOCKS_PER_BLOOM_CACHE, fos); if (blockCount == BLOCKS_PER_BLOOM_CACHE) { @@ -114,12 +114,12 @@ public IndexingStatus generateLogBloomCache(final long start, final long stop) { } } } catch (final Exception e) { - LOG.error("Unhandled indexing exception", e); + LOG.error("Unhandled caching exception", e); } finally { - indexingStatus.indexing = false; - LOG.info("Indexing request complete"); + cachingStatus.caching = false; + LOG.info("Caching request complete"); } - return indexingStatus; + return cachingStatus; } private long fillCacheFile( @@ -131,7 +131,7 @@ private long fillCacheFile( break; } fillCacheFileWithBlock(maybeHeader.get(), fos); - indexingStatus.currentBlock = blockNum; + cachingStatus.currentBlock = blockNum; blockNum++; } return blockNum - startBlock; @@ -152,12 +152,12 @@ public void cacheLogsBloomForBlockHeader(final BlockHeader blockHeader) { writer.write(ensureBloomBitsAreCorrectLength(blockHeader.getLogsBloom().toArray())); } } catch (IOException e) { - LOG.error("Unhandled indexing exception.", e); + LOG.error("Unhandled caching exception.", e); } } private void ensurePreviousSegmentsArePresent(final long blockNumber) { - if (!indexingStatus.isIndexing()) { + if (!cachingStatus.isCaching()) { scheduler.scheduleFutureTask( () -> { long currentSegment = (blockNumber / BLOCKS_PER_BLOOM_CACHE) - 1; @@ -188,15 +188,15 @@ private byte[] ensureBloomBitsAreCorrectLength(final byte[] logs) { return logs; } - public IndexingStatus requestIndexing(final long fromBlock, final long toBlock) { + public CachingStatus requestCaching(final long fromBlock, final long toBlock) { boolean requestAccepted = false; try { if ((fromBlock < toBlock) && submissionLock.tryLock(100, TimeUnit.MILLISECONDS)) { try { - if (!indexingStatus.indexing) { + if (!cachingStatus.caching) { requestAccepted = true; - indexingStatus.startBlock = fromBlock; - indexingStatus.endBlock = toBlock; + cachingStatus.startBlock = fromBlock; + cachingStatus.endBlock = toBlock; scheduler.scheduleComputationTask( () -> generateLogBloomCache( @@ -209,8 +209,8 @@ public IndexingStatus requestIndexing(final long fromBlock, final long toBlock) } catch (final InterruptedException e) { // ignore } - indexingStatus.requestAccepted = requestAccepted; - return indexingStatus; + cachingStatus.requestAccepted = requestAccepted; + return cachingStatus; } public EthScheduler getScheduler() { @@ -221,11 +221,11 @@ Path getCacheDir() { return cacheDir; } - public static final class IndexingStatus { + public static final class CachingStatus { long startBlock; long endBlock; volatile long currentBlock; - volatile boolean indexing; + volatile boolean caching; boolean requestAccepted; @JsonGetter @@ -244,8 +244,8 @@ public String getCurrentBlock() { } @JsonGetter - public boolean isIndexing() { - return indexing; + public boolean isCaching() { + return caching; } @JsonGetter diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminGenerateLogBloomCacheTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminGenerateLogBloomCacheTest.java index 34b7c984a69..f9e734fd93a 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminGenerateLogBloomCacheTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AdminGenerateLogBloomCacheTest.java @@ -23,8 +23,8 @@ 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.BlockchainQueries; -import org.hyperledger.besu.ethereum.api.query.TransactionLogsIndexer; -import org.hyperledger.besu.ethereum.api.query.TransactionLogsIndexer.IndexingStatus; +import org.hyperledger.besu.ethereum.api.query.TransactionLogBloomCacher; +import org.hyperledger.besu.ethereum.api.query.TransactionLogBloomCacher.CachingStatus; import java.util.List; import java.util.Optional; @@ -41,7 +41,7 @@ public class AdminGenerateLogBloomCacheTest { @Mock private BlockchainQueries blockchainQueries; - @Mock private TransactionLogsIndexer transactionLogsIndexer; + @Mock private TransactionLogBloomCacher transactionLogBloomCacher; @Captor private ArgumentCaptor fromBlock; @Captor private ArgumentCaptor toBlock; @@ -53,12 +53,12 @@ public void setup() { } @Test - public void requestWithZeroParameters_NoIndexer_returnsNull() { + public void requestWithZeroParameters_NoCacher_returnsNull() { final JsonRpcRequestContext request = new JsonRpcRequestContext( new JsonRpcRequest("2.0", "admin_generateLogBloomCache", new String[] {})); - when(blockchainQueries.getTransactionLogsIndexer()).thenReturn(Optional.empty()); + when(blockchainQueries.getTransactionLogBloomCacher()).thenReturn(Optional.empty()); final JsonRpcResponse actualResponse = method.response(request); @@ -109,11 +109,11 @@ public void testMockedResult( final JsonRpcRequestContext request = new JsonRpcRequestContext(new JsonRpcRequest("2.0", "admin_generateLogBloomCache", args)); - final IndexingStatus expectedStatus = new IndexingStatus(); + final CachingStatus expectedStatus = new CachingStatus(); - when(blockchainQueries.getTransactionLogsIndexer()) - .thenReturn(Optional.of(transactionLogsIndexer)); - when(transactionLogsIndexer.requestIndexing(fromBlock.capture(), toBlock.capture())) + when(blockchainQueries.getTransactionLogBloomCacher()) + .thenReturn(Optional.of(transactionLogBloomCacher)); + when(transactionLogBloomCacher.requestCaching(fromBlock.capture(), toBlock.capture())) .thenReturn(expectedStatus); final JsonRpcResponse actualResponse = method.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesLogCacheTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesLogCacheTest.java index f1e26ca6b49..f81d7d94ffe 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesLogCacheTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/query/BlockchainQueriesLogCacheTest.java @@ -16,7 +16,7 @@ package org.hyperledger.besu.ethereum.api.query; -import static org.hyperledger.besu.ethereum.api.query.TransactionLogsIndexer.BLOCKS_PER_BLOOM_CACHE; +import static org.hyperledger.besu.ethereum.api.query.TransactionLogBloomCacher.BLOCKS_PER_BLOOM_CACHE; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.times; From 3773d255c8718563571ce565b1910585aa92e57b Mon Sep 17 00:00:00 2001 From: mark-terry <36909937+mark-terry@users.noreply.github.com> Date: Fri, 14 Feb 2020 17:04:46 +1000 Subject: [PATCH 21/38] [PIE-1798] Fail cases for multitenancy ATs (#400) Signed-off-by: Mark Terry --- .../condition/priv/ExpectJsonRpcError.java | 49 ++++ .../dsl/condition/priv/PrivConditions.java | 7 + .../MultiTenancyAcceptanceTest.java | 5 +- ...tiTenancyValidationFailAcceptanceTest.java | 218 ++++++++++++++++++ .../resources/authentication/auth_priv.toml | 5 + 5 files changed, 281 insertions(+), 3 deletions(-) create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/ExpectJsonRpcError.java create mode 100644 acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/ExpectJsonRpcError.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/ExpectJsonRpcError.java new file mode 100644 index 00000000000..0fd0b970ebe --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/ExpectJsonRpcError.java @@ -0,0 +1,49 @@ +/* + * 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.tests.acceptance.dsl.condition.priv; + +import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; +import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.node.Node; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import org.assertj.core.api.Assertions; +import org.web3j.protocol.exceptions.ClientConnectionException; + +public class ExpectJsonRpcError implements Condition { + + private final Transaction transaction; + private final JsonRpcError error; + + public ExpectJsonRpcError(final Transaction transaction, final JsonRpcError error) { + this.transaction = transaction; + this.error = error; + } + + @Override + public void verify(final Node node) { + try { + node.execute(transaction); + failBecauseExceptionWasNotThrown(ClientConnectionException.class); + } catch (final Exception e) { + Assertions.assertThat(e) + .isInstanceOf(ClientConnectionException.class) + .hasMessageContaining("400") + .hasMessageContaining(error.getMessage()); + } + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java index a763eab6957..043e00a81cb 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java @@ -14,10 +14,12 @@ */ package org.hyperledger.besu.tests.acceptance.dsl.condition.priv; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyTransactions; import java.util.List; @@ -90,4 +92,9 @@ public Condition getTransactionReceipt(final Hash transactionHash) { return new PrivGetTransactionReceiptSuccess( transactions.getTransactionReceipt(transactionHash)); } + + public Condition multiTenancyValidationFail( + final Transaction transaction, final JsonRpcError error) { + return new ExpectJsonRpcError(transaction, error); + } } diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java index 0c873402c2a..83aed561c28 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java @@ -56,7 +56,6 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase { private final ObjectMapper mapper = new ObjectMapper(); private Cluster multiTenancyCluster; - private static final int ENCLAVE_PORT = 1080; private static final String PRIVACY_GROUP_ID = "Z3JvdXBJZA=="; private static final String ENCLAVE_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; private static final String KEY1 = "sgFkVOyFndZe/5SAZJO5UYbrl7pezHetveriBBWWnE8="; @@ -65,7 +64,7 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase { private final Address senderAddress = Address.wrap(Bytes.fromHexString(accounts.getPrimaryBenefactor().getAddress())); - @Rule public WireMockRule wireMockRule = new WireMockRule(options().port(ENCLAVE_PORT)); + @Rule public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort()); @Before public void setUp() throws Exception { @@ -75,7 +74,7 @@ public void setUp() throws Exception { node = besu.createNodeWithMultiTenantedPrivacy( "node1", - "http://127.0.0.1:" + ENCLAVE_PORT, + "http://127.0.0.1:" + wireMockRule.port(), "authentication/auth_priv.toml", "authentication/auth_priv_key"); multiTenancyCluster.start(node); diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java new file mode 100644 index 00000000000..88df1930a6e --- /dev/null +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java @@ -0,0 +1,218 @@ +/* + * 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.tests.acceptance.privacy.multitenancy; + +import static com.github.tomakehurst.wiremock.client.WireMock.ok; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.DELETE_PRIVACY_GROUP_ERROR; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.ENCLAVE_ERROR; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.FIND_PRIVACY_GROUP_ERROR; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError.GET_PRIVATE_TRANSACTION_NONCE_ERROR; + +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.enclave.types.PrivacyGroup; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.ethereum.privacy.Restriction; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; +import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; +import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.ClusterConfiguration; +import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.ClusterConfigurationBuilder; +import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction; + +import java.math.BigInteger; +import java.util.List; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.tomakehurst.wiremock.junit.WireMockRule; +import org.apache.tuweni.bytes.Bytes; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +public class MultiTenancyValidationFailAcceptanceTest extends AcceptanceTestBase { + private BesuNode node; + private final ObjectMapper mapper = new ObjectMapper(); + private Cluster multiTenancyCluster; + + private static final String PRIVACY_GROUP_ID = "Z3JvdXBJZA=="; + private static final String ENCLAVE_PUBLIC_KEY = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; + private static final String OTHER_ENCLAVE_PUBLIC_KEY = + "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; + private final Address senderAddress = + Address.wrap(Bytes.fromHexString(accounts.getPrimaryBenefactor().getAddress())); + + @Rule public WireMockRule wireMockRule = new WireMockRule(options().dynamicPort()); + + @Before + public void setUp() throws Exception { + final ClusterConfiguration clusterConfiguration = + new ClusterConfigurationBuilder().awaitPeerDiscovery(false).build(); + multiTenancyCluster = new Cluster(clusterConfiguration, net); + node = + besu.createNodeWithMultiTenantedPrivacy( + "node1", + "http://127.0.0.1:" + wireMockRule.port(), + "authentication/auth_priv.toml", + "authentication/auth_priv_key"); + multiTenancyCluster.start(node); + + final String token = + node.execute(permissioningTransactions.createSuccessfulLogin("failUser", "pegasys")); + node.useAuthenticationTokenInHeaderForJsonRpc(token); + } + + @After + public void tearDown() { + multiTenancyCluster.close(); + } + + @Test + public void sendRawTransactionShouldFailWhenPrivateFromNotMatchEnclaveKey() + throws JsonProcessingException { + final PrivateTransaction validSignedPrivateTransaction = + getValidSignedPrivateTransaction(senderAddress, OTHER_ENCLAVE_PUBLIC_KEY); + retrievePrivacyGroupEnclaveStub(); + final Transaction transaction = + privacyTransactions.sendRawTransaction( + getRLPOutput(validSignedPrivateTransaction).encoded().toHexString()); + node.verify(priv.multiTenancyValidationFail(transaction, ENCLAVE_ERROR)); + } + + @Test + public void sendRawTransactionShouldFailWhenPrivacyGroupDoesNotContainEnclaveKey() + throws JsonProcessingException { + final PrivateTransaction validSignedPrivateTransaction = + getValidSignedPrivateTransaction(senderAddress, ENCLAVE_PUBLIC_KEY); + retrievePrivacyGroupEnclaveStub(); + final Transaction transaction = + privacyTransactions.sendRawTransaction( + getRLPOutput(validSignedPrivateTransaction).encoded().toHexString()); + node.verify(priv.multiTenancyValidationFail(transaction, ENCLAVE_ERROR)); + } + + @Test + public void distributeRawTransactionShouldFailWhenPrivateFromNotMatchEnclaveKey() { + final Address senderAddress = + Address.wrap(Bytes.fromHexString(accounts.getPrimaryBenefactor().getAddress())); + + final Transaction transaction = + privacyTransactions.distributeRawTransaction( + getRLPOutput(getValidSignedPrivateTransaction(senderAddress, OTHER_ENCLAVE_PUBLIC_KEY)) + .encoded() + .toHexString()); + node.verify(priv.multiTenancyValidationFail(transaction, ENCLAVE_ERROR)); + } + + @Test + public void distributeRawTransactionShouldFailWhenPrivacyGroupDoesNotContainEnclaveKey() + throws JsonProcessingException { + final Address senderAddress = + Address.wrap(Bytes.fromHexString(accounts.getPrimaryBenefactor().getAddress())); + + retrievePrivacyGroupEnclaveStub(); + + final Transaction transaction = + privacyTransactions.distributeRawTransaction( + getRLPOutput(getValidSignedPrivateTransaction(senderAddress, ENCLAVE_PUBLIC_KEY)) + .encoded() + .toHexString()); + node.verify(priv.multiTenancyValidationFail(transaction, ENCLAVE_ERROR)); + } + + @Test + public void deletePrivacyGroupShouldFailWhenEnclaveKeyNotInPrivacyGroup() + throws JsonProcessingException { + retrievePrivacyGroupEnclaveStub(); + final Transaction transaction = + privacyTransactions.deletePrivacyGroup(PRIVACY_GROUP_ID); + node.verify(priv.multiTenancyValidationFail(transaction, DELETE_PRIVACY_GROUP_ERROR)); + } + + @Test + public void findPrivacyGroupShouldFailWhenEnclaveKeyNotInPrivacyGroup() { + final Transaction transaction = + privacyTransactions.findPrivacyGroup(new String[] {OTHER_ENCLAVE_PUBLIC_KEY}); + node.verify(priv.multiTenancyValidationFail(transaction, FIND_PRIVACY_GROUP_ERROR)); + } + + @Test + public void determineEeaNonceShouldFailWhenPrivateFromNotMatchEnclaveKey() { + final String accountAddress = Address.ZERO.toHexString(); + final String senderAddressBase64 = Bytes.fromHexString(accountAddress).toBase64String(); + final String[] privateFor = {senderAddressBase64}; + final Transaction transaction = + privacyTransactions.getEeaTransactionCount( + accountAddress, OTHER_ENCLAVE_PUBLIC_KEY, privateFor); + node.verify(priv.multiTenancyValidationFail(transaction, GET_PRIVATE_TRANSACTION_NONCE_ERROR)); + } + + @Test + public void determineBesuNonceShouldFailWhenEnclaveKeyNotInPrivacyGroup() + throws JsonProcessingException { + retrievePrivacyGroupEnclaveStub(); + final String accountAddress = Address.ZERO.toHexString(); + final Transaction transaction = + privacyTransactions.getTransactionCount(accountAddress, PRIVACY_GROUP_ID); + node.verify(priv.multiTenancyValidationFail(transaction, GET_PRIVATE_TRANSACTION_NONCE_ERROR)); + } + + private void retrievePrivacyGroupEnclaveStub() throws JsonProcessingException { + final String retrieveGroupResponse = + mapper.writeValueAsString( + testPrivacyGroup(List.of(OTHER_ENCLAVE_PUBLIC_KEY), PrivacyGroup.Type.PANTHEON)); + stubFor(post("/retrievePrivacyGroup").willReturn(ok(retrieveGroupResponse))); + } + + private PrivacyGroup testPrivacyGroup( + final List groupMembers, final PrivacyGroup.Type groupType) { + return new PrivacyGroup(PRIVACY_GROUP_ID, groupType, "test", "testGroup", groupMembers); + } + + private BytesValueRLPOutput getRLPOutput(final PrivateTransaction validSignedPrivateTransaction) { + final BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput(); + validSignedPrivateTransaction.writeTo(bvrlpo); + return bvrlpo; + } + + private static PrivateTransaction getValidSignedPrivateTransaction( + final Address senderAddress, final String privateFrom) { + return PrivateTransaction.builder() + .nonce(0) + .gasPrice(Wei.ZERO) + .gasLimit(3000000) + .to(null) + .value(Wei.ZERO) + .payload(Bytes.wrap(new byte[] {})) + .sender(senderAddress) + .chainId(BigInteger.valueOf(2018)) + .privateFrom(Bytes.fromBase64String(privateFrom)) + .restriction(Restriction.RESTRICTED) + .privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID)) + .signAndBuild( + SECP256K1.KeyPair.create( + SECP256K1.PrivateKey.create( + new BigInteger( + "853d7f0010fd86d0d7811c1f9d968ea89a24484a8127b4a483ddf5d2cfec766d", 16)))); + } +} diff --git a/acceptance-tests/tests/src/test/resources/authentication/auth_priv.toml b/acceptance-tests/tests/src/test/resources/authentication/auth_priv.toml index 2c86f165bf7..bcd2673c1ae 100644 --- a/acceptance-tests/tests/src/test/resources/authentication/auth_priv.toml +++ b/acceptance-tests/tests/src/test/resources/authentication/auth_priv.toml @@ -2,3 +2,8 @@ password = "$2a$10$l3GA7K8g6rJ/Yv.YFSygCuI9byngpEzxgWS9qEg5emYDZomQW7fGC" permissions = ["fakePermission", "*:*"] privacyPublicKey = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=" + +[Users.failUser] +password = "$2a$10$l3GA7K8g6rJ/Yv.YFSygCuI9byngpEzxgWS9qEg5emYDZomQW7fGC" +permissions = ["fakePermission", "*:*"] +privacyPublicKey = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=" From 9fa9c858ead30d7d1cb82d6435cdaa1c815e5950 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Mon, 17 Feb 2020 15:08:51 +1300 Subject: [PATCH 22/38] Private state update metadata and migration (#404) (backport from release-1.4) Private state update metadata and migration Signed-off-by: Lucas Saldanha --- CHANGELOG.md | 7 + .../dsl/ethsigner/EthSignerClientTest.java | 3 +- .../MultiTenancyAcceptanceTest.java | 2 +- ...tiTenancyValidationFailAcceptanceTest.java | 2 +- .../privacy/EthSignerAcceptanceTest.java | 16 +- .../org/hyperledger/besu/cli/BesuCommand.java | 42 +- .../besu/cli/error/BesuExceptionHandler.java | 10 +- .../presynctasks/PreSynchronizationTask.java | 27 + .../PreSynchronizationTaskRunner.java | 33 ++ .../PrivateDatabaseMigrationPreSyncTask.java | 45 ++ .../util/PrivateStorageMigrationBuilder.java | 63 ++ .../hyperledger/besu/PrivacyReorgTest.java | 538 ++++++++++++++++++ .../org/hyperledger/besu/PrivacyTest.java | 5 +- .../hyperledger/besu/cli/BesuCommandTest.java | 18 +- .../besu/cli/CommandTestAbstract.java | 19 +- .../PrivateStorageMigrationServiceTest.java | 105 ++++ besu/src/test/resources/enclavePrivateKey | 1 + besu/src/test/resources/enclavePublicKey | 1 + .../src/test/resources/everything_config.toml | 1 + build.gradle | 2 +- docs/Private-Txns-Migration.md | 36 ++ ...vGetPrivateTransactionIntegrationTest.java | 2 +- .../privacy/methods/priv/PrivCall.java | 11 - .../priv/PrivGetTransactionReceipt.java | 62 +- .../privacy/PrivateTransactionResult.java | 2 +- .../PrivacyApiGroupJsonRpcMethods.java | 15 +- .../privacy/methods/priv/PrivCallTest.java | 1 - .../priv/PrivGetTransactionCountTest.java | 19 +- .../priv/PrivGetTransactionReceiptTest.java | 21 +- ...acyPrecompiledContractIntegrationTest.java | 25 +- .../mainnet/AbstractBlockProcessor.java | 9 + .../mainnet/MainnetTransactionProcessor.java | 2 + .../mainnet/PrivacyBlockProcessor.java | 54 ++ .../ethereum/mainnet/ProtocolSpecBuilder.java | 29 +- .../privacy/PrivacyPrecompiledContract.java | 101 +++- .../ChainHeadPrivateNonceProvider.java | 54 ++ .../privacy/DefaultPrivacyController.java | 46 +- .../privacy/PrivateNonceProvider.java | 23 + .../privacy/PrivateStateRootResolver.java | 70 +++ ...geMigrationTransactionProcessorResult.java | 76 +++ .../ethereum/privacy/PrivateTransaction.java | 2 +- .../privacy/PrivateTransactionProcessor.java | 2 + .../privacy/PrivateTransactionReceipt.java | 184 ++++++ .../privacy/PrivateTransactionSimulator.java | 14 +- .../privacy/PrivateTransactionValidator.java | 8 +- .../LegacyPrivateStateKeyValueStorage.java | 178 ++++++ .../storage/LegacyPrivateStateStorage.java | 68 +++ .../storage/PrivacyGroupHeadBlockMap.java | 134 +++++ .../storage/PrivacyStorageProvider.java | 3 + .../privacy/storage/PrivateBlockMetadata.java | 68 +++ .../storage/PrivateStateKeyValueStorage.java | 133 ++--- .../privacy/storage/PrivateStateStorage.java | 40 +- .../storage/PrivateTransactionMetadata.java | 12 +- .../ethereum/privacy/storage/RLPMapEntry.java | 71 +++ .../PrivacyKeyValueStorageProvider.java | 39 +- .../PrivateMigrationBlockProcessor.java | 160 ++++++ .../migration/PrivateStorageMigration.java | 176 ++++++ .../PrivateStorageMigrationException.java | 33 ++ .../PrivateStorageMigrationService.java | 78 +++ .../besu/ethereum/vm/MessageFrame.java | 22 + .../ethereum/core/BlockDataGenerator.java | 121 +++- .../core/InMemoryPrivacyStorageProvider.java | 7 + .../core/InMemoryStorageProvider.java | 6 + .../mainnet/PrivacyBlockProcessorTest.java | 77 +++ .../PrivacyPrecompiledContractTest.java | 45 +- .../ChainHeadPrivateNonceProviderTest.java | 102 ++++ .../privacy/DefaultPrivacyControllerTest.java | 107 +--- .../privacy/PrivateStateRootResolverTest.java | 180 ++++++ .../PrivateStateKeyValueStorageTest.java | 55 ++ .../PrivateStorageMigrationTest.java | 315 ++++++++++ .../PrivateTransactionDataFixture.java | 85 +++ 71 files changed, 3677 insertions(+), 446 deletions(-) create mode 100644 besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PreSynchronizationTask.java create mode 100644 besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PreSynchronizationTaskRunner.java create mode 100644 besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PrivateDatabaseMigrationPreSyncTask.java create mode 100644 besu/src/main/java/org/hyperledger/besu/util/PrivateStorageMigrationBuilder.java create mode 100644 besu/src/test/java/org/hyperledger/besu/PrivacyReorgTest.java create mode 100644 besu/src/test/java/org/hyperledger/besu/util/PrivateStorageMigrationServiceTest.java create mode 100644 besu/src/test/resources/enclavePrivateKey create mode 100644 besu/src/test/resources/enclavePublicKey create mode 100644 docs/Private-Txns-Migration.md create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessor.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/ChainHeadPrivateNonceProvider.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateNonceProvider.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRootResolver.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStorageMigrationTransactionProcessorResult.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionReceipt.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/LegacyPrivateStateKeyValueStorage.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/LegacyPrivateStateStorage.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivacyGroupHeadBlockMap.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateBlockMetadata.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/RLPMapEntry.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationException.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationService.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/ChainHeadPrivateNonceProviderTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRootResolverTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorageTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationTest.java create mode 100644 ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateTransactionDataFixture.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 096b4826311..72837156174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ - [BESU-25](https://jira.hyperledger.org/browse/BESU-25) Use v5 Devp2p when pinging [\#392](https://github.com/hyperledger/besu/pull/392) +## 1.4.0 RC-2 + +### Private State Migration +Hyperledger Besu v1.4 implements a new data structure for private state storage that is not backwards compatible. +A migration will be performed when starting v1.4 for the first time to reprocess existing private transactions +and re-create the private state data in the v1.4 format. +If you have existing private transactions, see [migration details](docs/Private-Txns-Migration.md). ## 1.4.0 RC-1 diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/ethsigner/EthSignerClientTest.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/ethsigner/EthSignerClientTest.java index 9648971f9d0..06b33f2b6b1 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/ethsigner/EthSignerClientTest.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/ethsigner/EthSignerClientTest.java @@ -38,8 +38,7 @@ public class EthSignerClientTest { @ClassRule public static final TemporaryFolder folder = new TemporaryFolder(); @ClassRule - public static final WireMockRule wireMockRule = - new WireMockRule(wireMockConfig().dynamicPort().dynamicPort()); + public static final WireMockRule wireMockRule = new WireMockRule(wireMockConfig().dynamicPort()); private static final String MOCK_RESPONSE = "mock_transaction_hash"; private static final String MOCK_SEND_TRANSACTION_RESPONSE = diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java index 83aed561c28..1971fbab6f2 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyAcceptanceTest.java @@ -56,7 +56,7 @@ public class MultiTenancyAcceptanceTest extends AcceptanceTestBase { private final ObjectMapper mapper = new ObjectMapper(); private Cluster multiTenancyCluster; - private static final String PRIVACY_GROUP_ID = "Z3JvdXBJZA=="; + private static final String PRIVACY_GROUP_ID = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; private static final String ENCLAVE_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; private static final String KEY1 = "sgFkVOyFndZe/5SAZJO5UYbrl7pezHetveriBBWWnE8="; private static final String KEY2 = "R1kW75NQC9XX3kwNpyPjCBFflM29+XvnKKS9VLrUkzo="; diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java index 88df1930a6e..fddfe2f28ca 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/privacy/multitenancy/MultiTenancyValidationFailAcceptanceTest.java @@ -55,7 +55,7 @@ public class MultiTenancyValidationFailAcceptanceTest extends AcceptanceTestBase private final ObjectMapper mapper = new ObjectMapper(); private Cluster multiTenancyCluster; - private static final String PRIVACY_GROUP_ID = "Z3JvdXBJZA=="; + private static final String PRIVACY_GROUP_ID = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; private static final String ENCLAVE_PUBLIC_KEY = "B1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; private static final String OTHER_ENCLAVE_PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="; diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/EthSignerAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/EthSignerAcceptanceTest.java index e334e42f3d0..0cbd711e3db 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/EthSignerAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/EthSignerAcceptanceTest.java @@ -57,12 +57,11 @@ public void setUp() throws Exception { } @Test - @Ignore public void privateSmartContractMustDeploy() throws IOException { final String transactionHash = ethSignerClient.eeaSendTransaction( null, - BigInteger.valueOf(63992), + BigInteger.valueOf(23176), BigInteger.valueOf(1000), EventEmitter.BINARY, BigInteger.valueOf(0), @@ -77,13 +76,15 @@ public void privateSmartContractMustDeploy() throws IOException { privateTransactionVerifier.validPrivateTransactionReceipt(transactionHash, receipt)); } + // requires ethsigner jar > 0.3.0 + // https://bintray.com/consensys/pegasys-repo/ethsigner @Test @Ignore public void privateSmartContractMustDeployNoNonce() throws IOException { final String transactionHash = ethSignerClient.eeaSendTransaction( null, - BigInteger.valueOf(63992), + BigInteger.valueOf(23176), BigInteger.valueOf(1000), EventEmitter.BINARY, minerNode.getEnclaveKey(), @@ -114,7 +115,7 @@ public void privateSmartContractMustDeployWithPrivacyGroup() throws IOException final String transactionHash = ethSignerClient.eeaSendTransaction( null, - BigInteger.valueOf(63992), + BigInteger.valueOf(23176), BigInteger.valueOf(1000), EventEmitter.BINARY, BigInteger.valueOf(0), @@ -130,7 +131,6 @@ public void privateSmartContractMustDeployWithPrivacyGroup() throws IOException } @Test - @Ignore public void privateSmartContractMustDeployWithPrivacyGroupNoNonce() throws IOException { final String privacyGroupId = minerNode.execute(privacyTransactions.createPrivacyGroup(null, null, minerNode)); @@ -140,14 +140,14 @@ public void privateSmartContractMustDeployWithPrivacyGroupNoNonce() throws IOExc new PrivacyGroup( privacyGroupId, PrivacyGroup.Type.PANTHEON, - "Default Name", - "Default Description", + "", + "", Base64String.wrapList(minerNode.getEnclaveKey())))); final String transactionHash = ethSignerClient.eeaSendTransaction( null, - BigInteger.valueOf(63992), + BigInteger.valueOf(23176), BigInteger.valueOf(1000), EventEmitter.BINARY, minerNode.getEnclaveKey(), diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index ec969df7279..784d4b90d35 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -48,6 +48,8 @@ import org.hyperledger.besu.cli.options.PrunerOptions; import org.hyperledger.besu.cli.options.SynchronizerOptions; import org.hyperledger.besu.cli.options.TransactionPoolOptions; +import org.hyperledger.besu.cli.presynctasks.PreSynchronizationTaskRunner; +import org.hyperledger.besu.cli.presynctasks.PrivateDatabaseMigrationPreSyncTask; import org.hyperledger.besu.cli.subcommands.PasswordSubCommand; import org.hyperledger.besu.cli.subcommands.PublicKeySubCommand; import org.hyperledger.besu.cli.subcommands.PublicKeySubCommand.KeyLoader; @@ -212,6 +214,9 @@ protected KeyLoader getKeyLoader() { // Property to indicate whether Besu has been launched via docker private final boolean isDocker = Boolean.getBoolean("besu.docker"); + private final PreSynchronizationTaskRunner preSynchronizationTaskRunner = + new PreSynchronizationTaskRunner(); + // CLI options defined by user at runtime. // Options parsing is done with CLI library Picocli https://picocli.info/ @@ -760,6 +765,11 @@ void setBannedNodeIds(final List values) { "The name of a file containing the private key used to sign privacy marker transactions. If unset, each will be signed with a random key.") private final Path privacyMarkerTransactionSigningKeyPath = null; + @Option( + names = {"--privacy-enable-database-migration"}, + description = "Enable private database metadata migration (default: ${DEFAULT-VALUE})") + private final Boolean migratePrivateDatabase = false; + @Option( names = {"--target-gas-limit"}, description = @@ -888,7 +898,11 @@ public void run() { // Need to create vertx after cmdline has been parsed, such that metricSystem is configurable vertx = createVertx(createVertxOptions(metricsSystem.get())); - validateOptions().configure().controller().startPlugins().startSynchronization(); + final BesuCommand controller = validateOptions().configure().controller(); + + preSynchronizationTaskRunner.runTasks(controller.besuController); + + controller.startPlugins().startSynchronization(); } catch (final Exception e) { throw new ParameterException(this.commandLine, e.getMessage(), e); } @@ -1596,7 +1610,14 @@ private PrivacyParameters privacyParameters() { } } - return privacyParametersBuilder.build(); + final PrivacyParameters privacyParameters = privacyParametersBuilder.build(); + + if (isPrivacyEnabled) { + preSynchronizationTaskRunner.addTask( + new PrivateDatabaseMigrationPreSyncTask(privacyParameters, migratePrivateDatabase)); + } + + return privacyParameters; } private boolean anyPrivacyApiEnabled() { @@ -1608,19 +1629,20 @@ private boolean anyPrivacyApiEnabled() { private PrivacyKeyValueStorageProvider privacyKeyStorageProvider(final String name) { return new PrivacyKeyValueStorageProviderBuilder() - .withStorageFactory( - (PrivacyKeyValueStorageFactory) - storageService - .getByName(name) - .orElseThrow( - () -> - new StorageException( - "No KeyValueStorageFactory found for key: " + name))) + .withStorageFactory(privacyKeyValueStorageFactory(name)) .withCommonConfiguration(pluginCommonConfiguration) .withMetricsSystem(getMetricsSystem()) .build(); } + private PrivacyKeyValueStorageFactory privacyKeyValueStorageFactory(final String name) { + return (PrivacyKeyValueStorageFactory) + storageService + .getByName(name) + .orElseThrow( + () -> new StorageException("No KeyValueStorageFactory found for key: " + name)); + } + private KeyValueStorageProvider keyStorageProvider(final String name) { return new KeyValueStorageProviderBuilder() .withStorageFactory( diff --git a/besu/src/main/java/org/hyperledger/besu/cli/error/BesuExceptionHandler.java b/besu/src/main/java/org/hyperledger/besu/cli/error/BesuExceptionHandler.java index 664f70c462c..59ccedefcbb 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/error/BesuExceptionHandler.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/error/BesuExceptionHandler.java @@ -14,11 +14,14 @@ */ package org.hyperledger.besu.cli.error; +import org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateStorageMigrationException; + import java.util.List; import java.util.function.Supplier; import org.apache.logging.log4j.Level; import picocli.CommandLine; +import picocli.CommandLine.ParameterException; public class BesuExceptionHandler extends CommandLine.AbstractHandler, BesuExceptionHandler> @@ -39,12 +42,17 @@ public List handleParseException( } else { err().println(ex.getMessage()); } - if (!CommandLine.UnmatchedArgumentException.printSuggestions(ex, err())) { + if (shouldPrintUsage(ex)) { ex.getCommandLine().usage(err(), ansi()); } return returnResultOrExit(null); } + private boolean shouldPrintUsage(final ParameterException ex) { + return !CommandLine.UnmatchedArgumentException.printSuggestions(ex, err()) + && !(ex.getCause() instanceof PrivateStorageMigrationException); + } + @Override public List handleExecutionException( final CommandLine.ExecutionException ex, final CommandLine.ParseResult parseResult) { diff --git a/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PreSynchronizationTask.java b/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PreSynchronizationTask.java new file mode 100644 index 00000000000..28238ca241c --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PreSynchronizationTask.java @@ -0,0 +1,27 @@ +/* + * 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.cli.presynctasks; + +import org.hyperledger.besu.cli.BesuCommand; +import org.hyperledger.besu.controller.BesuController; + +/** + * All PreSynchronizationTask instances execute after the {@link BesuController} instance in {@link + * BesuCommand} is ready and before {@link BesuCommand#startSynchronization()} is called + */ +public interface PreSynchronizationTask { + + void run(final BesuController besuController); +} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PreSynchronizationTaskRunner.java b/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PreSynchronizationTaskRunner.java new file mode 100644 index 00000000000..55cf9850d23 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PreSynchronizationTaskRunner.java @@ -0,0 +1,33 @@ +/* + * 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.cli.presynctasks; + +import org.hyperledger.besu.controller.BesuController; + +import java.util.ArrayList; +import java.util.List; + +public class PreSynchronizationTaskRunner { + + private final List tasks = new ArrayList<>(); + + public void addTask(final PreSynchronizationTask task) { + tasks.add(task); + } + + public void runTasks(final BesuController besuController) { + tasks.forEach(t -> t.run(besuController)); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PrivateDatabaseMigrationPreSyncTask.java b/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PrivateDatabaseMigrationPreSyncTask.java new file mode 100644 index 00000000000..a6ef39dc8a0 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/cli/presynctasks/PrivateDatabaseMigrationPreSyncTask.java @@ -0,0 +1,45 @@ +/* + * 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.cli.presynctasks; + +import org.hyperledger.besu.controller.BesuController; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; +import org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateStorageMigrationService; +import org.hyperledger.besu.util.PrivateStorageMigrationBuilder; + +public class PrivateDatabaseMigrationPreSyncTask implements PreSynchronizationTask { + + private final PrivacyParameters privacyParameters; + private final boolean migratePrivateDatabaseFlag; + + public PrivateDatabaseMigrationPreSyncTask( + final PrivacyParameters privacyParameters, final boolean migratePrivateDatabaseFlag) { + this.privacyParameters = privacyParameters; + this.migratePrivateDatabaseFlag = migratePrivateDatabaseFlag; + } + + @Override + public void run(final BesuController besuController) { + final PrivateStorageMigrationBuilder privateStorageMigrationBuilder = + new PrivateStorageMigrationBuilder(besuController, privacyParameters); + final PrivateStorageMigrationService privateStorageMigrationService = + new PrivateStorageMigrationService( + privacyParameters.getPrivateStateStorage(), + migratePrivateDatabaseFlag, + privateStorageMigrationBuilder::build); + + privateStorageMigrationService.runMigrationIfRequired(); + } +} diff --git a/besu/src/main/java/org/hyperledger/besu/util/PrivateStorageMigrationBuilder.java b/besu/src/main/java/org/hyperledger/besu/util/PrivateStorageMigrationBuilder.java new file mode 100644 index 00000000000..f8ffb9c39de --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/util/PrivateStorageMigrationBuilder.java @@ -0,0 +1,63 @@ +/* + * 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.util; + +import org.hyperledger.besu.controller.BesuController; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; +import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; +import org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateMigrationBlockProcessor; +import org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateStorageMigration; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; + +public class PrivateStorageMigrationBuilder { + + private final BesuController besuController; + private final PrivacyParameters privacyParameters; + + public PrivateStorageMigrationBuilder( + final BesuController besuController, final PrivacyParameters privacyParameters) { + this.besuController = besuController; + this.privacyParameters = privacyParameters; + } + + public PrivateStorageMigration build() { + final Blockchain blockchain = besuController.getProtocolContext().getBlockchain(); + final Address privacyPrecompileAddress = + Address.privacyPrecompiled(privacyParameters.getPrivacyAddress()); + final ProtocolSchedule protocolSchedule = besuController.getProtocolSchedule(); + final WorldStateArchive publicWorldStateArchive = + besuController.getProtocolContext().getWorldStateArchive(); + final PrivateStateStorage privateStateStorage = privacyParameters.getPrivateStateStorage(); + final PrivateStateRootResolver privateStateRootResolver = + new PrivateStateRootResolver(privateStateStorage); + final LegacyPrivateStateStorage legacyPrivateStateStorage = + privacyParameters.getPrivateStorageProvider().createLegacyPrivateStateStorage(); + + return new PrivateStorageMigration( + blockchain, + privacyPrecompileAddress, + protocolSchedule, + publicWorldStateArchive, + privateStateStorage, + privateStateRootResolver, + legacyPrivateStateStorage, + PrivateMigrationBlockProcessor::new); + } +} diff --git a/besu/src/test/java/org/hyperledger/besu/PrivacyReorgTest.java b/besu/src/test/java/org/hyperledger/besu/PrivacyReorgTest.java new file mode 100644 index 00000000000..704eaeaf423 --- /dev/null +++ b/besu/src/test/java/org/hyperledger/besu/PrivacyReorgTest.java @@ -0,0 +1,538 @@ +/* + * 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; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH; + +import org.hyperledger.besu.config.GenesisConfigFile; +import org.hyperledger.besu.controller.BesuController; +import org.hyperledger.besu.controller.GasLimitCalculator; +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.enclave.Enclave; +import org.hyperledger.besu.enclave.EnclaveFactory; +import org.hyperledger.besu.enclave.types.SendResponse; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.chain.DefaultBlockchain; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.Difficulty; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.InMemoryStorageProvider; +import org.hyperledger.besu.ethereum.core.LogsBloomFilter; +import org.hyperledger.besu.ethereum.core.MiningParametersTestBuilder; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; +import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; +import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; +import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; +import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.ethereum.privacy.Restriction; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyStorageProvider; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; +import org.hyperledger.besu.ethereum.privacy.storage.keyvalue.PrivacyKeyValueStorageProviderBuilder; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; +import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBKeyValuePrivacyStorageFactory; +import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBKeyValueStorageFactory; +import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBMetricsFactory; +import org.hyperledger.besu.plugin.services.storage.rocksdb.configuration.RocksDBFactoryConfiguration; +import org.hyperledger.besu.services.BesuConfigurationImpl; +import org.hyperledger.besu.testutil.TestClock; +import org.hyperledger.orion.testutil.OrionKeyConfiguration; +import org.hyperledger.orion.testutil.OrionTestHarness; +import org.hyperledger.orion.testutil.OrionTestHarnessFactory; + +import java.io.IOException; +import java.math.BigInteger; +import java.net.URI; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import io.vertx.core.Vertx; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +@SuppressWarnings("rawtypes") +public class PrivacyReorgTest { + private static final int MAX_OPEN_FILES = 1024; + private static final long CACHE_CAPACITY = 8388608; + private static final int MAX_BACKGROUND_COMPACTIONS = 4; + private static final int BACKGROUND_THREAD_COUNT = 4; + + @Rule public final TemporaryFolder folder = new TemporaryFolder(); + + private static final SECP256K1.KeyPair KEY_PAIR = + SECP256K1.KeyPair.create( + SECP256K1.PrivateKey.create( + new BigInteger( + "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); + private static final Bytes ENCLAVE_PUBLIC_KEY = + Bytes.fromBase64String("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo="); + + private static final String FIRST_BLOCK_WITH_NO_TRANSACTIONS_STATE_ROOT = + "0x1bdf13f6d14c7322d6e695498aab258949e55574bef7eac366eb777f43d7dd2b"; + private static final String FIRST_BLOCK_WITH_SINGLE_TRANSACTION_STATE_ROOT = + "0x16979b290f429e06d86a43584c7d8689d4292ade9a602e5c78e2867c6ebd904e"; + private static final String BLOCK_WITH_SINGLE_TRANSACTION_RECEIPTS_ROOT = + "0xc8267b3f9ed36df3ff8adb51a6d030716f23eeb50270e7fce8d9822ffa7f0461"; + private static final String STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMTPY_STATE = + "0x2121b68f1333e93bae8cd717a3ca68c9d7e7003f6b288c36dfc59b0f87be9590"; + private static final Bytes32 PRIVACY_GROUP_BYTES32 = + Bytes32.fromHexString("0xf250d523ae9164722b06ca25cfa2a7f3c45df96b09e215236f886c876f715bfa"); + + // EventEmitter contract binary + private static final Bytes MOCK_PAYLOAD = + Bytes.fromHexString( + "0x608060405234801561001057600080fd5b5060008054600160a060020a03191633179055610199806100326000396000f3fe6080604052600436106100565763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633fa4f245811461005b5780636057361d1461008257806367e404ce146100ae575b600080fd5b34801561006757600080fd5b506100706100ec565b60408051918252519081900360200190f35b34801561008e57600080fd5b506100ac600480360360208110156100a557600080fd5b50356100f2565b005b3480156100ba57600080fd5b506100c3610151565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60025490565b604080513381526020810183905281517fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5929181900390910190a16002556001805473ffffffffffffffffffffffffffffffffffffffff191633179055565b60015473ffffffffffffffffffffffffffffffffffffffff169056fea165627a7a72305820c7f729cb24e05c221f5aa913700793994656f233fe2ce3b9fd9a505ea17e8d8a0029"); + private static final PrivateTransaction PRIVATE_TRANSACTION = + PrivateTransaction.builder() + .chainId(BigInteger.valueOf(2018)) + .gasLimit(1000) + .gasPrice(Wei.ZERO) + .nonce(0) + .payload(MOCK_PAYLOAD) + .to(null) + .privateFrom(ENCLAVE_PUBLIC_KEY) + .privateFor(Collections.singletonList(ENCLAVE_PUBLIC_KEY)) + .restriction(Restriction.RESTRICTED) + .value(Wei.ZERO) + .signAndBuild(KEY_PAIR); + + private final BlockDataGenerator gen = new BlockDataGenerator(); + private BesuController besuController; + private OrionTestHarness enclave; + private PrivateStateRootResolver privateStateRootResolver; + private PrivacyParameters privacyParameters; + + @Before + public void setUp() throws IOException { + // Start Enclave + enclave = + OrionTestHarnessFactory.create( + folder.newFolder().toPath(), + new OrionKeyConfiguration("enclavePublicKey", "enclavePrivateKey")); + enclave.start(); + + // Create Storage + final Path dataDir = folder.newFolder().toPath(); + final Path dbDir = dataDir.resolve("database"); + + // Configure Privacy + privacyParameters = + new PrivacyParameters.Builder() + .setEnabled(true) + .setStorageProvider(createKeyValueStorageProvider(dataDir, dbDir)) + .setEnclaveUrl(enclave.clientUrl()) + .setEnclaveFactory(new EnclaveFactory(Vertx.vertx())) + .build(); + privacyParameters.setEnclavePublicKey(ENCLAVE_PUBLIC_KEY.toBase64String()); + + privateStateRootResolver = + new PrivateStateRootResolver(privacyParameters.getPrivateStateStorage()); + + besuController = + new BesuController.Builder() + .fromGenesisConfig(GenesisConfigFile.development()) + .synchronizerConfiguration(SynchronizerConfiguration.builder().build()) + .ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig()) + .storageProvider(new InMemoryStorageProvider()) + .networkId(BigInteger.ONE) + .miningParameters(new MiningParametersTestBuilder().enabled(false).build()) + .nodeKeys(SECP256K1.KeyPair.generate()) + .metricsSystem(new NoOpMetricsSystem()) + .dataDirectory(dataDir) + .clock(TestClock.fixed()) + .privacyParameters(privacyParameters) + .transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) + .targetGasLimit(GasLimitCalculator.DEFAULT) + .build(); + } + + @Test + public void privacyGroupHeadIsTracked() { + // Setup an initial blockchain with one private transaction + final ProtocolContext protocolContext = besuController.getProtocolContext(); + final DefaultBlockchain blockchain = (DefaultBlockchain) protocolContext.getBlockchain(); + final PrivateStateStorage privateStateStorage = privacyParameters.getPrivateStateStorage(); + + final Transaction privacyMarkerTransaction = + buildMarkerTransaction(getEnclaveKey(enclave.clientUrl())); + final Block firstBlock = + gen.block( + getBlockOptionsWithTransaction( + blockchain.getGenesisBlock(), + privacyMarkerTransaction, + FIRST_BLOCK_WITH_SINGLE_TRANSACTION_STATE_ROOT)); + + appendBlock(besuController, blockchain, protocolContext, firstBlock); + + final PrivacyGroupHeadBlockMap expected = + new PrivacyGroupHeadBlockMap( + Collections.singletonMap(PRIVACY_GROUP_BYTES32, firstBlock.getHash())); + + assertThat( + privateStateStorage.getPrivacyGroupHeadBlockMap(blockchain.getGenesisBlock().getHash())) + .isEmpty(); + assertThat(privateStateStorage.getPrivacyGroupHeadBlockMap(firstBlock.getHash())).isNotEmpty(); + assertThat(privateStateStorage.getPrivacyGroupHeadBlockMap(firstBlock.getHash())) + .contains(expected); + + final String secondBlockStateRoot = + "0xd86a520e49caf215e7e4028262924db50540a5b26e415ab7c944e46a0c01d704"; + final Block secondBlock = + gen.block(getBlockOptionsNoTransaction(firstBlock, secondBlockStateRoot)); + + appendBlock(besuController, blockchain, protocolContext, secondBlock); + + assertThat(privateStateStorage.getPrivacyGroupHeadBlockMap(secondBlock.getHash())).isNotEmpty(); + + assertThat(privateStateStorage.getPrivacyGroupHeadBlockMap(secondBlock.getHash())) + .contains(expected); + } + + @Test + public void reorgToChainAtEqualHeight() { + // Setup an initial blockchain with one private transaction + final ProtocolContext protocolContext = besuController.getProtocolContext(); + final DefaultBlockchain blockchain = (DefaultBlockchain) protocolContext.getBlockchain(); + + final Block firstBlock = + gen.block( + getBlockOptionsWithTransaction( + blockchain.getGenesisBlock(), + buildMarkerTransaction(getEnclaveKey(enclave.clientUrl())), + FIRST_BLOCK_WITH_SINGLE_TRANSACTION_STATE_ROOT)); + + appendBlock(besuController, blockchain, protocolContext, firstBlock); + + // Check that the private state root is not the empty state + assertPrivateStateRoot( + privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMTPY_STATE); + + // Create parallel fork of length 1 which removes privacy marker transaction + final Block forkBlock = + gen.block( + getBlockOptionsNoTransactionWithDifficulty( + blockchain.getGenesisBlock(), + blockchain.getChainHeadHeader().getDifficulty().plus(10L), + FIRST_BLOCK_WITH_NO_TRANSACTIONS_STATE_ROOT)); + + appendBlock(besuController, blockchain, protocolContext, forkBlock); + + // Check that the private state root is the empty state + assertPrivateStateRoot(privateStateRootResolver, blockchain, EMPTY_ROOT_HASH); + } + + @Test + public void reorgToShorterChain() { + // Setup an initial blockchain with one private transaction + final ProtocolContext protocolContext = besuController.getProtocolContext(); + final DefaultBlockchain blockchain = (DefaultBlockchain) protocolContext.getBlockchain(); + + final String firstBlockStateRoot = + "0xbca927086c294984d6c2add82731c386cf2df3cd75509907dac928de12b7c472"; + final Block firstBlock = + gen.block(getBlockOptionsNoTransaction(blockchain.getGenesisBlock(), firstBlockStateRoot)); + + final String secondBlockStateRoot = + "0x35c315ee7d272e5b612d454ee87c948657310ab33208b57122f8d0525e91f35e"; + final Block secondBlock = + gen.block( + getBlockOptionsWithTransaction( + firstBlock, + buildMarkerTransaction(getEnclaveKey(enclave.clientUrl())), + secondBlockStateRoot)); + + appendBlock(besuController, blockchain, protocolContext, firstBlock); + appendBlock(besuController, blockchain, protocolContext, secondBlock); + + assertThat(blockchain.getChainHeadBlockNumber()).isEqualTo(2); + + // Check that the private state root is not the empty state + assertPrivateStateRoot( + privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMTPY_STATE); + + // Create parallel fork of length 1 which removes privacy marker transaction + final Difficulty remainingDifficultyToOutpace = + blockchain + .getBlockByNumber(1) + .get() + .getHeader() + .getDifficulty() + .plus(blockchain.getBlockByNumber(2).get().getHeader().getDifficulty()); + + final String forkBlockStateRoot = + "0x4a33bdf9d16e6dd4f4c67f1638971f663f132ebceac0c7c65c9a3f35172af4de"; + final Block forkBlock = + gen.block( + getBlockOptionsNoTransactionWithDifficulty( + blockchain.getGenesisBlock(), + remainingDifficultyToOutpace.plus(10L), + forkBlockStateRoot)); + + appendBlock(besuController, blockchain, protocolContext, forkBlock); + + assertThat(blockchain.getChainHeadBlockNumber()).isEqualTo(1); + + // Check that the private state root is the empty state + assertPrivateStateRoot(privateStateRootResolver, blockchain, EMPTY_ROOT_HASH); + } + + @Test + public void reorgToLongerChain() { + // Setup an initial blockchain with one private transaction + final ProtocolContext protocolContext = besuController.getProtocolContext(); + final DefaultBlockchain blockchain = (DefaultBlockchain) protocolContext.getBlockchain(); + + final Block firstBlock = + gen.block( + getBlockOptionsWithTransaction( + blockchain.getGenesisBlock(), + buildMarkerTransaction(getEnclaveKey(enclave.clientUrl())), + FIRST_BLOCK_WITH_SINGLE_TRANSACTION_STATE_ROOT)); + + appendBlock(besuController, blockchain, protocolContext, firstBlock); + + assertThat(blockchain.getChainHeadBlockNumber()).isEqualTo(1); + + // Check that the private state root is not the empty state + assertPrivateStateRoot( + privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMTPY_STATE); + + // Create parallel fork of length 1 which removes privacy marker transaction + final Block forkBlock = + gen.block( + getBlockOptionsNoTransactionWithDifficulty( + blockchain.getGenesisBlock(), + firstBlock.getHeader().getDifficulty().plus(10L), + FIRST_BLOCK_WITH_NO_TRANSACTIONS_STATE_ROOT)); + + // Check that the private state root did not change + assertPrivateStateRoot( + privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMTPY_STATE); + + final String secondForkBlockStateRoot = + "0xd35eea814b8b5a0b12e690ab320785f3a33d9685bbf6875637c40a64203915da"; + final Block secondForkBlock = + gen.block( + getBlockOptionsNoTransactionWithDifficulty( + forkBlock, + forkBlock.getHeader().getDifficulty().plus(10L), + secondForkBlockStateRoot)); + + appendBlock(besuController, blockchain, protocolContext, forkBlock); + appendBlock(besuController, blockchain, protocolContext, secondForkBlock); + + assertThat(blockchain.getChainHeadBlockNumber()).isEqualTo(2); + + // Check that the private state root is the empty state + assertPrivateStateRoot(privateStateRootResolver, blockchain, EMPTY_ROOT_HASH); + + // Add another private transaction + final String thirdForkBlockStateRoot = + "0xe22344ade05260177b79dcc6c4fed8f87ab95a506c2a6147631ac6547cf44846"; + final Block thirdForkBlock = + gen.block( + getBlockOptionsWithTransactionAndDifficulty( + secondForkBlock, + buildMarkerTransaction(getEnclaveKey(enclave.clientUrl())), + secondForkBlock.getHeader().getDifficulty().plus(10L), + thirdForkBlockStateRoot)); + + appendBlock(besuController, blockchain, protocolContext, thirdForkBlock); + + // Check that the private state did change after reorg + assertPrivateStateRoot( + privateStateRootResolver, blockchain, STATE_ROOT_AFTER_TRANSACTION_APPENDED_TO_EMTPY_STATE); + } + + @SuppressWarnings("unchecked") + private void appendBlock( + final BesuController besuController, + final DefaultBlockchain blockchain, + final ProtocolContext protocolContext, + final Block block) { + besuController + .getProtocolSchedule() + .getByBlockNumber(blockchain.getChainHeadBlockNumber()) + .getBlockImporter() + .importBlock(protocolContext, block, HeaderValidationMode.NONE); + } + + private PrivacyStorageProvider createKeyValueStorageProvider( + final Path dataLocation, final Path dbLocation) { + return new PrivacyKeyValueStorageProviderBuilder() + .withStorageFactory( + new RocksDBKeyValuePrivacyStorageFactory( + new RocksDBKeyValueStorageFactory( + () -> + new RocksDBFactoryConfiguration( + MAX_OPEN_FILES, + MAX_BACKGROUND_COMPACTIONS, + BACKGROUND_THREAD_COUNT, + CACHE_CAPACITY), + Arrays.asList(KeyValueSegmentIdentifier.values()), + RocksDBMetricsFactory.PRIVATE_ROCKS_DB_METRICS))) + .withCommonConfiguration(new BesuConfigurationImpl(dataLocation, dbLocation)) + .withMetricsSystem(new NoOpMetricsSystem()) + .build(); + } + + private Bytes getEnclaveKey(final URI enclaveURI) { + final Enclave enclave = new EnclaveFactory(Vertx.vertx()).createVertxEnclave(enclaveURI); + final SendResponse sendResponse = + sendRequest(enclave, PRIVATE_TRANSACTION, ENCLAVE_PUBLIC_KEY.toBase64String()); + final Bytes payload = Bytes.fromBase64String(sendResponse.getKey()); + + // If the key has 0 bytes generate a new key. + // This is to keep the gasUsed constant allowing + // hard-coded receipt roots in the block headers + for (int i = 0; i < payload.size(); i++) { + if (payload.get(i) == 0) { + return getEnclaveKey(enclaveURI); + } + } + + return payload; + } + + private SendResponse sendRequest( + final Enclave enclave, + final PrivateTransaction privateTransaction, + final String enclavePublicKey) { + final BytesValueRLPOutput rlpOutput = new BytesValueRLPOutput(); + privateTransaction.writeTo(rlpOutput); + final String payload = rlpOutput.encoded().toBase64String(); + + if (privateTransaction.getPrivacyGroupId().isPresent()) { + return enclave.send( + payload, enclavePublicKey, privateTransaction.getPrivacyGroupId().get().toBase64String()); + } else { + final List privateFor = + privateTransaction.getPrivateFor().get().stream() + .map(Bytes::toBase64String) + .collect(Collectors.toList()); + + if (privateFor.isEmpty()) { + privateFor.add(privateTransaction.getPrivateFrom().toBase64String()); + } + return enclave.send( + payload, privateTransaction.getPrivateFrom().toBase64String(), privateFor); + } + } + + private Transaction buildMarkerTransaction(final Bytes payload) { + return Transaction.builder() + .chainId(BigInteger.valueOf(2018)) + .gasLimit(60000) + .gasPrice(Wei.of(1000)) + .nonce(0) + .payload(payload) + .to(Address.DEFAULT_PRIVACY) + .value(Wei.ZERO) + .signAndBuild(KEY_PAIR); + } + + private void assertPrivateStateRoot( + final PrivateStateRootResolver privateStateRootResolver, + final DefaultBlockchain blockchain, + final String expected) { + assertPrivateStateRoot(privateStateRootResolver, blockchain, Hash.fromHexString(expected)); + } + + private void assertPrivateStateRoot( + final PrivateStateRootResolver privateStateRootResolver, + final DefaultBlockchain blockchain, + final Hash expected) { + assertThat( + privateStateRootResolver.resolveLastStateRoot( + Bytes32.wrap( + Bytes.fromBase64String("8lDVI66RZHIrBsolz6Kn88Rd+WsJ4hUjb4hsh29xW/o=")), + blockchain.getChainHeadHash())) + .isEqualTo(expected); + } + + private BlockDataGenerator.BlockOptions getBlockOptionsNoTransaction( + final Block parentBlock, final String stateRoot) { + return getBlockOptions( + new BlockDataGenerator.BlockOptions() + .hasTransactions(false) + .setReceiptsRoot(PrivateStateRootResolver.EMPTY_ROOT_HASH) + .setGasUsed(0) + .setStateRoot(Hash.fromHexString(stateRoot)), + parentBlock); + } + + private BlockDataGenerator.BlockOptions getBlockOptionsWithTransaction( + final Block parentBlock, final Transaction transaction, final String stateRoot) { + return getBlockOptions( + new BlockDataGenerator.BlockOptions() + .addTransaction(transaction) + .setReceiptsRoot(Hash.fromHexString(BLOCK_WITH_SINGLE_TRANSACTION_RECEIPTS_ROOT)) + .setGasUsed(23176) + .setStateRoot(Hash.fromHexString(stateRoot)), + parentBlock); + } + + private BlockDataGenerator.BlockOptions getBlockOptionsNoTransactionWithDifficulty( + final Block parentBlock, final Difficulty difficulty, final String stateRoot) { + return getBlockOptions( + new BlockDataGenerator.BlockOptions() + .hasTransactions(false) + .setDifficulty(difficulty) + .setReceiptsRoot(PrivateStateRootResolver.EMPTY_ROOT_HASH) + .setGasUsed(0) + .setStateRoot(Hash.fromHexString(stateRoot)), + parentBlock); + } + + private BlockDataGenerator.BlockOptions getBlockOptionsWithTransactionAndDifficulty( + final Block parentBlock, + final Transaction transaction, + final Difficulty difficulty, + final String stateRoot) { + return getBlockOptions( + new BlockDataGenerator.BlockOptions() + .addTransaction(transaction) + .setDifficulty(difficulty) + .setReceiptsRoot(Hash.fromHexString(BLOCK_WITH_SINGLE_TRANSACTION_RECEIPTS_ROOT)) + .setGasUsed(23176) + .setStateRoot(Hash.fromHexString(stateRoot)), + parentBlock); + } + + private BlockDataGenerator.BlockOptions getBlockOptions( + final BlockDataGenerator.BlockOptions blockOptions, final Block parentBlock) { + return blockOptions + .setBlockNumber(parentBlock.getHeader().getNumber() + 1) + .setParentHash(parentBlock.getHash()) + .hasOmmers(false) + .setLogsBloom(LogsBloomFilter.empty()); + } +} diff --git a/besu/src/test/java/org/hyperledger/besu/PrivacyTest.java b/besu/src/test/java/org/hyperledger/besu/PrivacyTest.java index 39aa32c74ff..bdb6a9728eb 100644 --- a/besu/src/test/java/org/hyperledger/besu/PrivacyTest.java +++ b/besu/src/test/java/org/hyperledger/besu/PrivacyTest.java @@ -62,7 +62,6 @@ public class PrivacyTest { private static final int BACKGROUND_THREAD_COUNT = 4; private final Vertx vertx = Vertx.vertx(); - private static final Integer ADDRESS = 9; @Rule public final TemporaryFolder folder = new TemporaryFolder(); @After @@ -76,7 +75,7 @@ public void privacyPrecompiled() throws IOException, URISyntaxException { final Path dbDir = dataDir.resolve("database"); final PrivacyParameters privacyParameters = new PrivacyParameters.Builder() - .setPrivacyAddress(ADDRESS) + .setPrivacyAddress(Address.PRIVACY) .setEnabled(true) .setEnclaveUrl(new URI("http://127.0.0.1:8000")) .setStorageProvider(createKeyValueStorageProvider(dataDir, dbDir)) @@ -99,7 +98,7 @@ public void privacyPrecompiled() throws IOException, URISyntaxException { .targetGasLimit(GasLimitCalculator.DEFAULT) .build(); - final Address privacyContractAddress = Address.privacyPrecompiled(ADDRESS); + final Address privacyContractAddress = Address.DEFAULT_PRIVACY; final PrecompiledContract precompiledContract = besuController .getProtocolSchedule() diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index 985b7bd8884..b65bcfd4185 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -807,7 +807,7 @@ public void configOptionDisabledUnderDocker() { @Test public void nodekeyOptionMustBeUsed() throws Exception { - final File file = new File("./specific/key"); + final File file = new File("./specific/enclavePrivateKey"); file.deleteOnExit(); parseCommand("--node-private-key-file", file.getPath()); @@ -828,13 +828,12 @@ public void nodekeyOptionDisabledUnderDocker() { assumeFalse(isFullInstantiation()); - final File file = new File("./specific/key"); + final File file = new File("./specific/enclavePrivateKey"); file.deleteOnExit(); parseCommand("--node-private-key-file", file.getPath()); - assertThat(commandErrorOutput.toString()) - .startsWith("Unknown options: '--node-private-key-file', './specific/key'"); + .startsWith("Unknown options: '--node-private-key-file', './specific/enclavePrivateKey'"); assertThat(commandOutput.toString()).isEmpty(); } @@ -2877,8 +2876,6 @@ public void fullCLIOptionsShownWhenNotInDockerContainer() { @Test public void mustUseEnclaveUriAndOptions() { - when(storageService.getByName("rocksdb-privacy")) - .thenReturn(Optional.of(rocksDBSPrivacyStorageFactory)); final URL configFile = this.getClass().getResource("/orion_publickey.pub"); parseCommand( @@ -2905,7 +2902,7 @@ public void mustUseEnclaveUriAndOptions() { @Test public void privacyOptionsRequiresServiceToBeEnabled() { - final File file = new File("./specific/public_key"); + final File file = new File("./specific/enclavePublicKey"); file.deleteOnExit(); parseCommand( @@ -2951,9 +2948,6 @@ public void mustVerifyPrivacyIsDisabled() { @Test public void privacyMultiTenancyIsConfiguredWhenConfiguredWithNecessaryOptions() { - when(storageService.getByName("rocksdb-privacy")) - .thenReturn(Optional.of(rocksDBSPrivacyStorageFactory)); - parseCommand( "--privacy-enabled", "--rpc-http-authentication-enabled", @@ -3064,9 +3058,9 @@ public void privacyPublicKeyFileOptionDisabledUnderDocker() { assumeFalse(isFullInstantiation()); final Path path = Paths.get("."); - parseCommand("--privacy-public-key-file", path.toString()); + parseCommand("--privacy-public-enclavePrivateKey-file", path.toString()); assertThat(commandErrorOutput.toString()) - .startsWith("Unknown options: '--privacy-public-key-file', '.'"); + .startsWith("Unknown options: '--privacy-public-enclavePrivateKey-file', '.'"); assertThat(commandOutput.toString()).isEmpty(); } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index e689fffd7ee..36678d287f7 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -19,6 +19,7 @@ import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; @@ -55,10 +56,12 @@ import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.PicoCLIOptions; +import org.hyperledger.besu.plugin.services.StorageService; import org.hyperledger.besu.plugin.services.storage.KeyValueStorageFactory; import org.hyperledger.besu.plugin.services.storage.PrivacyKeyValueStorageFactory; import org.hyperledger.besu.services.BesuPluginContextImpl; import org.hyperledger.besu.services.StorageServiceImpl; +import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import java.io.ByteArrayOutputStream; import java.io.File; @@ -223,10 +226,22 @@ public void initMocks() throws Exception { when(mockRunnerBuilder.autoLogBloomCaching(anyBoolean())).thenReturn(mockRunnerBuilder); when(mockRunnerBuilder.build()).thenReturn(mockRunner); - when(storageService.getByName("rocksdb")).thenReturn(Optional.of(rocksDBStorageFactory)); + lenient() + .when(storageService.getByName(eq("rocksdb"))) + .thenReturn(Optional.of(rocksDBStorageFactory)); + lenient() + .when(storageService.getByName(eq("rocksdb-privacy"))) + .thenReturn(Optional.of(rocksDBSPrivacyStorageFactory)); + lenient() + .when(rocksDBSPrivacyStorageFactory.create(any(), any(), any())) + .thenReturn(new InMemoryKeyValueStorage()); - when(mockBesuPluginContext.getService(PicoCLIOptions.class)) + lenient() + .when(mockBesuPluginContext.getService(PicoCLIOptions.class)) .thenReturn(Optional.of(cliOptions)); + lenient() + .when(mockBesuPluginContext.getService(StorageService.class)) + .thenReturn(Optional.of(storageService)); } // Display outputs for debug purpose diff --git a/besu/src/test/java/org/hyperledger/besu/util/PrivateStorageMigrationServiceTest.java b/besu/src/test/java/org/hyperledger/besu/util/PrivateStorageMigrationServiceTest.java new file mode 100644 index 00000000000..fdccf5b8740 --- /dev/null +++ b/besu/src/test/java/org/hyperledger/besu/util/PrivateStorageMigrationServiceTest.java @@ -0,0 +1,105 @@ +/* + * 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.util; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_0_0; +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_4_0; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage.Updater; +import org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateStorageMigration; +import org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateStorageMigrationException; +import org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateStorageMigrationService; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class PrivateStorageMigrationServiceTest { + + @Mock private PrivateStateStorage privateStateStorage; + @Mock private PrivateStorageMigration migration; + + private PrivateStorageMigrationService migrationService; + + @Test + public void migrationShouldNotRunIfDatabaseIsFreshAndVersionShouldBeSet() { + when(privateStateStorage.getSchemaVersion()).thenReturn(SCHEMA_VERSION_1_0_0); + when(privateStateStorage.isEmpty()).thenReturn(true); + final Updater privateStateStorageUpdater = mock(Updater.class); + when(privateStateStorage.updater()).thenReturn(privateStateStorageUpdater); + when(privateStateStorageUpdater.putDatabaseVersion(anyInt())) + .thenReturn(privateStateStorageUpdater); + + migrationService = + new PrivateStorageMigrationService(privateStateStorage, true, () -> migration); + + migrationService.runMigrationIfRequired(); + + verify(privateStateStorageUpdater).putDatabaseVersion(eq(SCHEMA_VERSION_1_4_0)); + verifyNoInteractions(migration); + } + + @Test + public void migrationShouldNotRunIfSchemaVersionIsGreaterThanOne() { + when(privateStateStorage.getSchemaVersion()).thenReturn(SCHEMA_VERSION_1_4_0); + + migrationService = + new PrivateStorageMigrationService(privateStateStorage, true, () -> migration); + + migrationService.runMigrationIfRequired(); + + verifyNoInteractions(migration); + } + + @Test + public void migrationShouldNotRunIfFlagIsNotSetEvenIfVersionRequiresMigration() { + when(privateStateStorage.getSchemaVersion()).thenReturn(SCHEMA_VERSION_1_0_0); + when(privateStateStorage.isEmpty()).thenReturn(false); + + migrationService = + new PrivateStorageMigrationService(privateStateStorage, false, () -> migration); + + final Throwable thrown = catchThrowable(() -> migrationService.runMigrationIfRequired()); + assertThat(thrown) + .isInstanceOf(PrivateStorageMigrationException.class) + .hasMessageContaining("Private database metadata requires migration"); + + verifyNoInteractions(migration); + } + + @Test + public void migrationShouldRunIfVersionIsOneAndFlagIsSet() { + when(privateStateStorage.getSchemaVersion()).thenReturn(SCHEMA_VERSION_1_0_0); + when(privateStateStorage.isEmpty()).thenReturn(false); + + migrationService = + new PrivateStorageMigrationService(privateStateStorage, true, () -> migration); + + migrationService.runMigrationIfRequired(); + + verify(migration).migratePrivateStorage(); + } +} diff --git a/besu/src/test/resources/enclavePrivateKey b/besu/src/test/resources/enclavePrivateKey new file mode 100644 index 00000000000..eaae9b0867c --- /dev/null +++ b/besu/src/test/resources/enclavePrivateKey @@ -0,0 +1 @@ +{"data":{"bytes":"hBsuQsGJzx4QHmFmBkNoI7YGnTmaZP4P+wBOdu56ljk="},"type":"unlocked"} \ No newline at end of file diff --git a/besu/src/test/resources/enclavePublicKey b/besu/src/test/resources/enclavePublicKey new file mode 100644 index 00000000000..3c8b7f3bfb8 --- /dev/null +++ b/besu/src/test/resources/enclavePublicKey @@ -0,0 +1 @@ +A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo= \ No newline at end of file diff --git a/besu/src/test/resources/everything_config.toml b/besu/src/test/resources/everything_config.toml index 5fbc6f48470..f776ca43a80 100644 --- a/besu/src/test/resources/everything_config.toml +++ b/besu/src/test/resources/everything_config.toml @@ -120,6 +120,7 @@ privacy-enabled=false privacy-multi-tenancy-enabled=true privacy-precompiled-address=9 privacy-marker-transaction-signing-key-file="./signerKey" +privacy-enable-database-migration=false # Transaction Pool tx-pool-retention-hours=999 diff --git a/build.gradle b/build.gradle index 840728d0537..ec056cb081b 100644 --- a/build.gradle +++ b/build.gradle @@ -601,7 +601,7 @@ task checkSpdxHeader(type: CheckSpdxHeader) { rootPath = "${projectDir}" spdxHeader = "* SPDX-License-Identifier: Apache-2.0" filesRegex = "(.*.java)|(.*.groovy)" - excludeRegex = "(.*generalstate/GeneralStateRegressionReferenceTest.*)|(.*generalstate/GeneralStateReferenceTest.*)|(.*blockchain/BlockchainReferenceTest.*)|(.*.gradle/.*)" + excludeRegex = "(.*generalstate/GeneralStateRegressionReferenceTest.*)|(.*generalstate/GeneralStateReferenceTest.*)|(.*blockchain/BlockchainReferenceTest.*)|(.*.gradle/.*)|(.*.idea/.*)" } task jacocoRootReport(type: org.gradle.testing.jacoco.tasks.JacocoReport) { diff --git a/docs/Private-Txns-Migration.md b/docs/Private-Txns-Migration.md new file mode 100644 index 00000000000..9e828008dc9 --- /dev/null +++ b/docs/Private-Txns-Migration.md @@ -0,0 +1,36 @@ +# Private Transactions Migration + +Hyperledger Besu v1.4 implements a new data structure for private state storage that is not backwards compatible. +A migration will be performed when starting v1.4 for the first time to reprocess existing private transactions +and re-create the private state data in the v1.4 format. + +**Important** + +All nodes with existing private transactions will be migrated to the new private state storage +when upgrading to v1.4. It is not possible to upgrade to v1.4 without migrating. + +## How to migrate + +**Important** + +As a precaution (that is, resyncing should not be required), ensure your Hyperledger Besu database is backed-up +or other Besu nodes in your network are available to resync from if the migration does not complete as expected. + +We recommend that all nodes in a network do not upgrade and migrate at once. + +To migrate, add the `--privacy-enable-database-migration` flag to your Besu command line options. The migration starts +automatically when this flag is specified. If you have existing private transactions and do not specify this flag, +v1.4 will not start. + +After the migration starts, logs display the progress. When the migration is complete, Besu continues +starting up as usual. + +## During migration + +Do not stop the migration running once it has started. If the migration is stopped, you will need to restore +your Besu database from backup and restart the migration process. + +## Migration support + +If you have a long running network with a large volume of private transactions and/or would like to discuss +the migration process with us before upgrading, contact us at support@pegasys.tech \ No newline at end of file diff --git a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivGetPrivateTransactionIntegrationTest.java b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivGetPrivateTransactionIntegrationTest.java index 37289c7fb68..cc55a245466 100644 --- a/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivGetPrivateTransactionIntegrationTest.java +++ b/ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivGetPrivateTransactionIntegrationTest.java @@ -92,7 +92,7 @@ public static void setUpOnce() throws Exception { final EnclaveFactory factory = new EnclaveFactory(vertx); enclave = factory.createVertxEnclave(testHarness.clientUrl()); - privacyController = new DefaultPrivacyController(enclave, null, null, null, null, null); + privacyController = new DefaultPrivacyController(enclave, null, null, null, null); } @AfterClass diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivCall.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivCall.java index 58bb3966733..97210f68663 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivCall.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivCall.java @@ -22,7 +22,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.JsonCallParameter; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.EnclavePublicKeyProvider; -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; @@ -60,16 +59,6 @@ protected Object resultByBlockNumber( final CallParameter callParams = validateAndGetCallParams(request); final String privacyGroupId = request.getRequiredParameter(0, String.class); - // For now we do only support privCall on the head of the chain. - // TODO: Once we support privacy on PoW chains (mainnet) this can be removed and the - // blockchainQueries field can be made private again - if (blockNumber != blockchainQueries.get().headBlockNumber()) { - // TODO: Remove PRIV_CALL_ONLY_SUPPORTED_ON_CHAIN_HEAD in JsonRpcError when removing this - // code. - return new JsonRpcErrorResponse( - request.getRequest().getId(), JsonRpcError.PRIV_CALL_ONLY_SUPPORTED_ON_CHAIN_HEAD); - } - final String enclavePublicKey = enclavePublicKeyProvider.getEnclaveKey(request.getUser()); return privacyController diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceipt.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceipt.java index 03aad796a0b..28c1fb9eb04 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceipt.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceipt.java @@ -34,16 +34,14 @@ import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.BlockBody; import org.hyperledger.besu.ethereum.core.Hash; -import org.hyperledger.besu.ethereum.core.Log; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.privacy.PrivacyController; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.RLP; -import java.util.Collections; -import java.util.List; import java.util.Optional; import org.apache.logging.log4j.Logger; @@ -78,18 +76,19 @@ public String getName() { @Override public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { LOG.trace("Executing {}", RpcMethod.PRIV_GET_TRANSACTION_RECEIPT.getMethodName()); - final Hash transactionHash = requestContext.getRequiredParameter(0, Hash.class); + final Hash pmtTransactionHash = requestContext.getRequiredParameter(0, Hash.class); final Optional maybeLocation = - blockchain.getBlockchain().getTransactionLocation(transactionHash); + blockchain.getBlockchain().getTransactionLocation(pmtTransactionHash); if (maybeLocation.isEmpty()) { return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), null); } - final TransactionLocation location = maybeLocation.get(); + final TransactionLocation pmtLocation = maybeLocation.get(); final BlockBody blockBody = - blockchain.getBlockchain().getBlockBody(location.getBlockHash()).get(); - final Transaction transaction = blockBody.getTransactions().get(location.getTransactionIndex()); + blockchain.getBlockchain().getBlockBody(pmtLocation.getBlockHash()).get(); + final Transaction pmtTransaction = + blockBody.getTransactions().get(pmtLocation.getTransactionIndex()); - final Hash blockhash = location.getBlockHash(); + final Hash blockhash = pmtLocation.getBlockHash(); final long blockNumber = blockchain.getBlockchain().getBlockHeader(blockhash).get().getNumber(); final PrivateTransaction privateTransaction; @@ -97,9 +96,9 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { try { final ReceiveResponse receiveResponse = privacyController.retrieveTransaction( - transaction.getPayload().toBase64String(), + pmtTransaction.getPayload().toBase64String(), enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser())); - LOG.trace("Received transaction information"); + LOG.trace("Received private transaction information"); final BytesValueRLPInput input = new BytesValueRLPInput( @@ -124,52 +123,35 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { final Bytes rlpEncoded = RLP.encode(privateTransaction::writeTo); final Bytes32 txHash = org.hyperledger.besu.crypto.Hash.keccak256(rlpEncoded); - LOG.trace("Calculated private transaction hash: {}", txHash); - final List transactionLogs = + final PrivateTransactionReceipt privateTransactioReceipt = privacyParameters .getPrivateStateStorage() - .getTransactionLogs(txHash) - .orElse(Collections.emptyList()); - - LOG.trace("Processed private transaction events"); - - final Bytes transactionOutput = - privacyParameters.getPrivateStateStorage().getTransactionOutput(txHash).orElse(Bytes.EMPTY); - - final Bytes revertReason = - privacyParameters.getPrivateStateStorage().getRevertReason(txHash).orElse(null); - - final String transactionStatus = - Quantity.create( - privacyParameters - .getPrivateStateStorage() - .getStatus(txHash) - .orElse(Bytes.EMPTY) - .toUnsignedBigInteger()); + .getTransactionReceipt(blockhash, txHash) + .orElse(PrivateTransactionReceipt.EMPTY); - LOG.trace("Processed private transaction output"); + LOG.trace("Processed private transaction receipt"); final PrivateTransactionReceiptResult result = new PrivateTransactionReceiptResult( contractAddress, privateTransaction.getSender().toString(), privateTransaction.getTo().map(Address::toString).orElse(null), - transactionLogs, - transactionOutput, + privateTransactioReceipt.getLogs(), + privateTransactioReceipt.getOutput(), blockhash, blockNumber, - location.getTransactionIndex(), - transaction.getHash(), - privateTransaction.hash(), + pmtLocation.getTransactionIndex(), + pmtTransaction.getHash(), + privateTransaction.getHash(), privateTransaction.getPrivateFrom(), privateTransaction.getPrivateFor().orElse(null), privateTransaction.getPrivacyGroupId().orElse(null), - revertReason, - transactionStatus); + privateTransactioReceipt.getRevertReason().orElse(null), + Quantity.create(privateTransactioReceipt.getStatus())); - LOG.trace("Created Private Transaction from given Transaction Hash"); + LOG.trace("Created Private Transaction Receipt Result from given Transaction Hash"); return new JsonRpcSuccessResponse(requestContext.getRequest().getId(), result); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/privacy/PrivateTransactionResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/privacy/PrivateTransactionResult.java index ab50299b7b6..9808446d661 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/privacy/PrivateTransactionResult.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/privacy/PrivateTransactionResult.java @@ -41,7 +41,7 @@ public PrivateTransactionResult(final PrivateTransaction tx) { this.from = tx.getSender().toString(); this.gas = Quantity.create(tx.getGasLimit()); this.gasPrice = Quantity.create(tx.getGasPrice()); - this.hash = tx.hash().toString(); + this.hash = tx.getHash().toString(); this.input = tx.getPayload().toString(); this.nonce = Quantity.create(tx.getNonce()); this.to = tx.getTo().map(Address::toHexString).orElse(null); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java index 4b8f56ffb6a..bd17c4fe5d9 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivacyApiGroupJsonRpcMethods.java @@ -27,9 +27,12 @@ import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.privacy.ChainHeadPrivateNonceProvider; import org.hyperledger.besu.ethereum.privacy.DefaultPrivacyController; import org.hyperledger.besu.ethereum.privacy.MultiTenancyPrivacyController; import org.hyperledger.besu.ethereum.privacy.PrivacyController; +import org.hyperledger.besu.ethereum.privacy.PrivateNonceProvider; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionSimulator; import org.hyperledger.besu.ethereum.privacy.markertransaction.FixedKeySigningPrivateMarkerTransactionFactory; import org.hyperledger.besu.ethereum.privacy.markertransaction.PrivateMarkerTransactionFactory; @@ -45,6 +48,7 @@ public abstract class PrivacyApiGroupJsonRpcMethods extends ApiGroupJsonRpcMetho private final ProtocolSchedule protocolSchedule; private final TransactionPool transactionPool; private final PrivacyParameters privacyParameters; + private final PrivateNonceProvider privateNonceProvider; public PrivacyApiGroupJsonRpcMethods( final BlockchainQueries blockchainQueries, @@ -55,6 +59,14 @@ public PrivacyApiGroupJsonRpcMethods( this.protocolSchedule = protocolSchedule; this.transactionPool = transactionPool; this.privacyParameters = privacyParameters; + + final PrivateStateRootResolver privateStateRootResolver = + new PrivateStateRootResolver(privacyParameters.getPrivateStateStorage()); + this.privateNonceProvider = + new ChainHeadPrivateNonceProvider( + blockchainQueries.getBlockchain(), + privateStateRootResolver, + privacyParameters.getPrivateWorldStateArchive()); } public BlockchainQueries getBlockchainQueries() { @@ -131,7 +143,8 @@ private PrivacyController createPrivacyController( privacyParameters, protocolSchedule.getChainId(), markerTransactionFactory, - createPrivateTransactionSimulator()); + createPrivateTransactionSimulator(), + privateNonceProvider); return privacyParameters.isMultiTenancyEnabled() ? new MultiTenancyPrivacyController( defaultPrivacyController, privacyParameters.getEnclave()) diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivCallTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivCallTest.java index 02750f08882..8e2abb60897 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivCallTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivCallTest.java @@ -148,7 +148,6 @@ public void shouldUseCorrectBlockNumberWhenEarliest() { public void shouldUseCorrectBlockNumberWhenSpecified() { final JsonRpcRequestContext request = ethCallRequest(privacyGroupId, callParameter(), Quantity.create(13L)); - when(blockchainQueries.headBlockNumber()).thenReturn(13L); method.response(request); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionCountTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionCountTest.java index e4a928153ce..5d9a507f7b8 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionCountTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionCountTest.java @@ -14,7 +14,6 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -36,7 +35,6 @@ import io.vertx.core.json.JsonObject; import io.vertx.ext.auth.User; import io.vertx.ext.auth.jwt.impl.JWTUser; -import org.apache.tuweni.bytes.Bytes; import org.junit.Before; import org.junit.Test; @@ -46,7 +44,7 @@ public class PrivGetTransactionCountTest { private final PrivacyParameters privacyParameters = mock(PrivacyParameters.class); private final PrivacyController privacyController = mock(PrivacyController.class); - private final String privacyGroupId = Bytes.wrap("0x123".getBytes(UTF_8)).toBase64String(); + private static final String PRIVACY_GROUP_ID = "DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w="; private final Address senderAddress = Address.fromHexString("0x627306090abab3a6e1400e9345bc60c78a8bef57"); @@ -58,7 +56,7 @@ public class PrivGetTransactionCountTest { @Before public void before() { when(privacyParameters.isEnabled()).thenReturn(true); - when(privacyController.determineBesuNonce(senderAddress, privacyGroupId, ENCLAVE_PUBLIC_KEY)) + when(privacyController.determineBesuNonce(senderAddress, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY)) .thenReturn(NONCE); } @@ -67,7 +65,7 @@ public void verifyTransactionCount() { final PrivGetTransactionCount privGetTransactionCount = new PrivGetTransactionCount(privacyController, enclavePublicKeyProvider); - final Object[] params = new Object[] {senderAddress, privacyGroupId}; + final Object[] params = new Object[] {senderAddress, PRIVACY_GROUP_ID}; final JsonRpcRequestContext request = new JsonRpcRequestContext( new JsonRpcRequest("1", "priv_getTransactionCount", params), user); @@ -76,7 +74,8 @@ public void verifyTransactionCount() { (JsonRpcSuccessResponse) privGetTransactionCount.response(request); assertThat(response.getResult()).isEqualTo(String.format("0x%X", NONCE)); - verify(privacyController).determineBesuNonce(senderAddress, privacyGroupId, ENCLAVE_PUBLIC_KEY); + verify(privacyController) + .determineBesuNonce(senderAddress, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY); } @Test @@ -84,10 +83,10 @@ public void failsWithNonceErrorIfExceptionIsThrown() { final PrivGetTransactionCount privGetTransactionCount = new PrivGetTransactionCount(privacyController, enclavePublicKeyProvider); - when(privacyController.determineBesuNonce(senderAddress, privacyGroupId, ENCLAVE_PUBLIC_KEY)) + when(privacyController.determineBesuNonce(senderAddress, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY)) .thenThrow(EnclaveClientException.class); - final Object[] params = new Object[] {senderAddress, privacyGroupId}; + final Object[] params = new Object[] {senderAddress, PRIVACY_GROUP_ID}; final JsonRpcRequestContext request = new JsonRpcRequestContext( new JsonRpcRequest("1", "priv_getTransactionCount", params), user); @@ -104,10 +103,10 @@ public void failsWithUnauthorizedErrorIfMultiTenancyValidationFails() { final PrivGetTransactionCount privGetTransactionCount = new PrivGetTransactionCount(privacyController, enclavePublicKeyProvider); - when(privacyController.determineBesuNonce(senderAddress, privacyGroupId, ENCLAVE_PUBLIC_KEY)) + when(privacyController.determineBesuNonce(senderAddress, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY)) .thenThrow(new MultiTenancyValidationException("validation failed")); - final Object[] params = new Object[] {senderAddress, privacyGroupId}; + final Object[] params = new Object[] {senderAddress, PRIVACY_GROUP_ID}; final JsonRpcRequestContext request = new JsonRpcRequestContext( new JsonRpcRequest("1", "priv_getTransactionCount", params), user); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceiptTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceiptTest.java index c6a4650d432..4e1035bbd0c 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceiptTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionReceiptTest.java @@ -46,6 +46,7 @@ import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.privacy.PrivacyController; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt; import org.hyperledger.besu.ethereum.privacy.Restriction; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.rlp.RLP; @@ -174,9 +175,12 @@ public void setUp() { when(privacyParameters.isEnabled()).thenReturn(true); when(privacyParameters.getPrivateStateStorage()).thenReturn(privateStateStorage); - when(privateStateStorage.getTransactionLogs(any(Bytes32.class))).thenReturn(Optional.empty()); - when(privateStateStorage.getTransactionOutput(any(Bytes32.class))).thenReturn(Optional.empty()); - when(privateStateStorage.getStatus(any(Bytes32.class))).thenReturn(Optional.of(Bytes.of(1))); + @SuppressWarnings("unchecked") + final PrivateTransactionReceipt receipt = + new PrivateTransactionReceipt( + 1, Collections.EMPTY_LIST, Bytes.EMPTY, Optional.ofNullable(null)); + when(privateStateStorage.getTransactionReceipt(any(Bytes32.class), any(Bytes32.class))) + .thenReturn(Optional.of(receipt)); } @Test @@ -255,8 +259,15 @@ public void enclaveConnectionIssueThrowsRuntimeException() { @Test public void transactionReceiptContainsRevertReasonWhenInvalidTransactionOccurs() { - when(privateStateStorage.getRevertReason(any(Bytes32.class))) - .thenReturn(Optional.of(Bytes.fromHexString("0x01"))); + @SuppressWarnings("unchecked") + final PrivateTransactionReceipt privateTransactionReceipt = + new PrivateTransactionReceipt( + 1, + Collections.EMPTY_LIST, + Bytes.EMPTY, + Optional.of(Bytes.wrap(new byte[] {(byte) 0x01}))); + when(privateStateStorage.getTransactionReceipt(any(Bytes32.class), any(Bytes32.class))) + .thenReturn(Optional.of(privateTransactionReceipt)); final PrivGetTransactionReceipt privGetTransactionReceipt = new PrivGetTransactionReceipt( diff --git a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java index 5028a2e8e40..e22b7e493dc 100644 --- a/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java +++ b/ethereum/core/src/integration-test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractIntegrationTest.java @@ -27,12 +27,15 @@ import org.hyperledger.besu.enclave.types.SendResponse; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.WorldUpdater; import org.hyperledger.besu.ethereum.mainnet.SpuriousDragonGasCalculator; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.MessageFrame; @@ -79,6 +82,7 @@ public class PrivacyPrecompiledContractIntegrationTest { private static Enclave enclave; private static MessageFrame messageFrame; + private static Blockchain blockchain; private static OrionTestHarness testHarness; private static WorldStateArchive worldStateArchive; @@ -120,6 +124,17 @@ public static void setUpOnce() throws Exception { final EnclaveFactory factory = new EnclaveFactory(vertx); enclave = factory.createVertxEnclave(testHarness.clientUrl()); messageFrame = mock(MessageFrame.class); + blockchain = mock(Blockchain.class); + final BlockDataGenerator blockGenerator = new BlockDataGenerator(); + final Block genesis = blockGenerator.genesisBlock(); + final Block block = + blockGenerator.block( + new BlockDataGenerator.BlockOptions().setParentHash(genesis.getHeader().getHash())); + when(blockchain.getGenesisBlock()).thenReturn(genesis); + when(blockchain.getBlockByHash(block.getHash())).thenReturn(Optional.of(block)); + when(blockchain.getBlockByHash(genesis.getHash())).thenReturn(Optional.of(genesis)); + when(messageFrame.getBlockchain()).thenReturn(blockchain); + when(messageFrame.getBlockHeader()).thenReturn(block.getHeader()); worldStateArchive = mock(WorldStateArchive.class); final MutableWorldState mutableWorldState = mock(MutableWorldState.class); @@ -129,11 +144,13 @@ public static void setUpOnce() throws Exception { privateStateStorage = mock(PrivateStateStorage.class); final PrivateStateStorage.Updater storageUpdater = mock(PrivateStateStorage.Updater.class); - when(storageUpdater.putLatestStateRoot(nullable(Bytes32.class), any())) + when(privateStateStorage.getPrivacyGroupHeadBlockMap(any())) + .thenReturn(Optional.of(PrivacyGroupHeadBlockMap.EMPTY)); + when(storageUpdater.putPrivateBlockMetadata( + nullable(Bytes32.class), nullable(Bytes32.class), any())) .thenReturn(storageUpdater); - when(storageUpdater.putTransactionLogs(nullable(Bytes32.class), any())) - .thenReturn(storageUpdater); - when(storageUpdater.putTransactionResult(nullable(Bytes32.class), any())) + when(storageUpdater.putTransactionReceipt( + nullable(Bytes32.class), nullable(Bytes32.class), any())) .thenReturn(storageUpdater); when(privateStateStorage.updater()).thenReturn(storageUpdater); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index 4dd937f2940..ba64659fc59 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -89,6 +89,15 @@ public boolean isSuccessful() { private final MiningBeneficiaryCalculator miningBeneficiaryCalculator; + public AbstractBlockProcessor(final AbstractBlockProcessor blockProcessor) { + this( + blockProcessor.transactionProcessor, + blockProcessor.transactionReceiptFactory, + blockProcessor.blockReward, + blockProcessor.miningBeneficiaryCalculator, + blockProcessor.skipZeroBlockRewards); + } + public AbstractBlockProcessor( final TransactionProcessor transactionProcessor, final MainnetBlockProcessor.TransactionReceiptFactory transactionReceiptFactory, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 9fa880392a8..7c195da373a 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -248,6 +248,7 @@ public Result processTransaction( .blockHashLookup(blockHashLookup) .isPersistingState(isPersistingState) .maxStackSize(maxStackSize) + .transactionHash(transaction.getHash()) .build(); } else { @@ -279,6 +280,7 @@ public Result processTransaction( .blockHashLookup(blockHashLookup) .maxStackSize(maxStackSize) .isPersistingState(isPersistingState) + .transactionHash(transaction.getHash()) .build(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessor.java new file mode 100644 index 00000000000..3f77dcadf62 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessor.java @@ -0,0 +1,54 @@ +/* + * 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.mainnet; + +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; + +import java.util.List; + +public class PrivacyBlockProcessor implements BlockProcessor { + private final BlockProcessor blockProcessor; + private final PrivateStateStorage privateStateStorage; + + public PrivacyBlockProcessor( + final BlockProcessor blockProcessor, final PrivateStateStorage privateStateStorage) { + this.blockProcessor = blockProcessor; + this.privateStateStorage = privateStateStorage; + } + + @Override + public Result processBlock( + final Blockchain blockchain, + final MutableWorldState worldState, + final BlockHeader blockHeader, + final List transactions, + final List ommers) { + final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap = + new PrivacyGroupHeadBlockMap( + privateStateStorage + .getPrivacyGroupHeadBlockMap(blockHeader.getParentHash()) + .orElse(PrivacyGroupHeadBlockMap.EMPTY)); + privateStateStorage + .updater() + .putPrivacyGroupHeadBlockMap(blockHeader.getHash(), privacyGroupHeadBlockMap) + .commit(); + return blockProcessor.processBlock(blockchain, worldState, blockHeader, transactions, ommers); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java index 67251aab581..524491c625b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSpecBuilder.java @@ -281,6 +281,20 @@ public ProtocolSpec build(final ProtocolSchedule protocolSchedule) { transactionProcessorBuilder.apply( gasCalculator, transactionValidator, contractCreationProcessor, messageCallProcessor); + final BlockHeaderValidator blockHeaderValidator = + blockHeaderValidatorBuilder.apply(difficultyCalculator); + final BlockHeaderValidator ommerHeaderValidator = + ommerHeaderValidatorBuilder.apply(difficultyCalculator); + final BlockBodyValidator blockBodyValidator = + blockBodyValidatorBuilder.apply(protocolSchedule); + + BlockProcessor blockProcessor = + blockProcessorBuilder.apply( + transactionProcessor, + transactionReceiptFactory, + blockReward, + miningBeneficiaryCalculator, + skipZeroBlockRewards); // Set private Tx Processor PrivateTransactionProcessor privateTransactionProcessor = null; if (privacyParameters.isEnabled()) { @@ -298,21 +312,10 @@ public ProtocolSpec build(final ProtocolSchedule protocolSchedule) { (PrivacyPrecompiledContract) precompileContractRegistry.get(address, Account.DEFAULT_VERSION); privacyPrecompiledContract.setPrivateTransactionProcessor(privateTransactionProcessor); + blockProcessor = + new PrivacyBlockProcessor(blockProcessor, privacyParameters.getPrivateStateStorage()); } - final BlockHeaderValidator blockHeaderValidator = - blockHeaderValidatorBuilder.apply(difficultyCalculator); - final BlockHeaderValidator ommerHeaderValidator = - ommerHeaderValidatorBuilder.apply(difficultyCalculator); - final BlockBodyValidator blockBodyValidator = - blockBodyValidatorBuilder.apply(protocolSchedule); - final BlockProcessor blockProcessor = - blockProcessorBuilder.apply( - transactionProcessor, - transactionReceiptFactory, - blockReward, - miningBeneficiaryCalculator, - skipZeroBlockRewards); final BlockValidator blockValidator = blockValidatorBuilder.apply(blockHeaderValidator, blockBodyValidator, blockProcessor); final BlockImporter blockImporter = blockImporterBuilder.apply(blockValidator); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java index 0fee4c5746d..2a3c21b53d3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java @@ -21,28 +21,32 @@ import org.hyperledger.besu.enclave.EnclaveIOException; import org.hyperledger.besu.enclave.EnclaveServerException; import org.hyperledger.besu.enclave.types.ReceiveResponse; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Gas; import org.hyperledger.besu.ethereum.core.Hash; -import org.hyperledger.besu.ethereum.core.Log; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.PrivacyParameters; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.WorldUpdater; import org.hyperledger.besu.ethereum.debug.TraceOptions; import org.hyperledger.besu.ethereum.mainnet.AbstractPrecompiledContract; -import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; +import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateBlockMetadata; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.RLP; -import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.ethereum.vm.GasCalculator; import org.hyperledger.besu.ethereum.vm.MessageFrame; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.util.Base64; -import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -50,12 +54,11 @@ import org.apache.tuweni.bytes.Bytes32; public class PrivacyPrecompiledContract extends AbstractPrecompiledContract { - private final Enclave enclave; private final WorldStateArchive privateWorldStateArchive; private final PrivateStateStorage privateStateStorage; + private final PrivateStateRootResolver privateStateRootResolver; private PrivateTransactionProcessor privateTransactionProcessor; - private static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); private static final Logger LOG = LogManager.getLogger(); @@ -77,6 +80,7 @@ public PrivacyPrecompiledContract( this.enclave = enclave; this.privateWorldStateArchive = worldStateArchive; this.privateStateStorage = privateStateStorage; + this.privateStateRootResolver = new PrivateStateRootResolver(privateStateStorage); } public void setPrivateTransactionProcessor( @@ -86,11 +90,23 @@ public void setPrivateTransactionProcessor( @Override public Gas gasRequirement(final Bytes input) { - return Gas.of(40_000L); // Not sure + return Gas.of(0L); } @Override public Bytes compute(final Bytes input, final MessageFrame messageFrame) { + final ProcessableBlockHeader currentBlockHeader = messageFrame.getBlockHeader(); + if (!BlockHeader.class.isAssignableFrom(currentBlockHeader.getClass())) { + if (!messageFrame.isPersistingState()) { + // We get in here from block mining. + return Bytes.EMPTY; + } else { + throw new IllegalArgumentException( + "The MessageFrame contains an illegal block header type. Cannot persist private block metadata without current block hash."); + } + } + final Hash currentBlockHash = ((BlockHeader) currentBlockHeader).getHash(); + final String key = input.toBase64String(); final ReceiveResponse receiveResponse; @@ -112,16 +128,21 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) { Bytes.wrap(Base64.getDecoder().decode(receiveResponse.getPayload())), false); final PrivateTransaction privateTransaction = PrivateTransaction.readFrom(bytesValueRLPInput); final WorldUpdater publicWorldState = messageFrame.getWorldState(); - final Bytes privacyGroupId = Bytes.fromBase64String(receiveResponse.getPrivacyGroupId()); + final Bytes32 privacyGroupId = + Bytes32.wrap(Bytes.fromBase64String(receiveResponse.getPrivacyGroupId())); LOG.trace( "Processing private transaction {} in privacy group {}", - privateTransaction.hash(), + privateTransaction.getHash(), privacyGroupId); - // get the last world state root hash or create a new one + final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap = + privateStateStorage.getPrivacyGroupHeadBlockMap(currentBlockHash).orElseThrow(); + + final Blockchain currentBlockchain = messageFrame.getBlockchain(); + final Hash lastRootHash = - privateStateStorage.getLatestStateRoot(privacyGroupId).orElse(EMPTY_ROOT_HASH); + privateStateRootResolver.resolveLastStateRoot(privacyGroupId, currentBlockHash); final MutableWorldState disposablePrivateState = privateWorldStateArchive.getMutable(lastRootHash).get(); @@ -129,10 +150,10 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) { final WorldUpdater privateWorldStateUpdater = disposablePrivateState.updater(); final PrivateTransactionProcessor.Result result = privateTransactionProcessor.processTransaction( - messageFrame.getBlockchain(), + currentBlockchain, publicWorldState, privateWorldStateUpdater, - messageFrame.getBlockHeader(), + currentBlockHeader, privateTransaction, messageFrame.getMiningBeneficiary(), new DebugOperationTracer(TraceOptions.DEFAULT), @@ -142,7 +163,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) { if (result.isInvalid() || !result.isSuccessful()) { LOG.error( "Failed to process private transaction {}: {}", - privateTransaction.hash(), + privateTransaction.getHash(), result.getValidationResult().getErrorMessage()); return Bytes.EMPTY; } @@ -156,24 +177,52 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) { disposablePrivateState.persist(); final PrivateStateStorage.Updater privateStateUpdater = privateStateStorage.updater(); - privateStateUpdater.putLatestStateRoot(privacyGroupId, disposablePrivateState.rootHash()); + + updatePrivateBlockMetadata( + messageFrame.getTransactionHash(), + currentBlockHash, + privacyGroupId, + disposablePrivateState.rootHash(), + privateStateUpdater); final Bytes32 txHash = keccak256(RLP.encode(privateTransaction::writeTo)); - final List logs = result.getLogs(); - if (!logs.isEmpty()) { - privateStateUpdater.putTransactionLogs(txHash, result.getLogs()); - } - if (result.getRevertReason().isPresent()) { - privateStateUpdater.putTransactionRevertReason(txHash, result.getRevertReason().get()); - } - privateStateUpdater.putTransactionStatus( - txHash, - Bytes.of(result.getStatus() == TransactionProcessor.Result.Status.SUCCESSFUL ? 1 : 0)); - privateStateUpdater.putTransactionResult(txHash, result.getOutput()); + final int txStatus = + result.getStatus() == PrivateTransactionProcessor.Result.Status.SUCCESSFUL ? 1 : 0; + + final PrivateTransactionReceipt privateTransactionReceipt = + new PrivateTransactionReceipt( + txStatus, result.getLogs(), result.getOutput(), result.getRevertReason()); + + privateStateUpdater.putTransactionReceipt( + currentBlockHash, txHash, privateTransactionReceipt); + + // TODO: this map could be passed through from @PrivacyBlockProcessor and saved once at the + // end of block processing + if (!privacyGroupHeadBlockMap.contains(Bytes32.wrap(privacyGroupId), currentBlockHash)) { + privacyGroupHeadBlockMap.put(Bytes32.wrap(privacyGroupId), currentBlockHash); + privateStateUpdater.putPrivacyGroupHeadBlockMap( + currentBlockHash, new PrivacyGroupHeadBlockMap(privacyGroupHeadBlockMap)); + } privateStateUpdater.commit(); } return result.getOutput(); } + + private void updatePrivateBlockMetadata( + final Hash markerTransactionHash, + final Hash currentBlockHash, + final Bytes32 privacyGroupId, + final Hash rootHash, + final PrivateStateStorage.Updater privateStateUpdater) { + final PrivateBlockMetadata privateBlockMetadata = + privateStateStorage + .getPrivateBlockMetadata(currentBlockHash, Bytes32.wrap(privacyGroupId)) + .orElseGet(PrivateBlockMetadata::empty); + privateBlockMetadata.addPrivateTransactionMetadata( + new PrivateTransactionMetadata(markerTransactionHash, rootHash)); + privateStateUpdater.putPrivateBlockMetadata( + Bytes32.wrap(currentBlockHash), Bytes32.wrap(privacyGroupId), privateBlockMetadata); + } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/ChainHeadPrivateNonceProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/ChainHeadPrivateNonceProvider.java new file mode 100644 index 00000000000..8d10f565a8e --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/ChainHeadPrivateNonceProvider.java @@ -0,0 +1,54 @@ +/* + * 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.privacy; + +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Account; +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.worldstate.WorldStateArchive; + +import org.apache.tuweni.bytes.Bytes32; + +public class ChainHeadPrivateNonceProvider implements PrivateNonceProvider { + private final Blockchain blockchain; + private final PrivateStateRootResolver privateStateRootResolver; + private final WorldStateArchive privateWorldStateArchive; + + public ChainHeadPrivateNonceProvider( + final Blockchain blockchain, + final PrivateStateRootResolver privateStateRootResolver, + final WorldStateArchive privateWorldStateArchive) { + this.blockchain = blockchain; + this.privateStateRootResolver = privateStateRootResolver; + this.privateWorldStateArchive = privateWorldStateArchive; + } + + @Override + public long getNonce(final Address sender, final Bytes32 privacyGroupId) { + final BlockHeader chainHeadHeader = blockchain.getChainHeadHeader(); + final Hash stateRoot = + privateStateRootResolver.resolveLastStateRoot(privacyGroupId, chainHeadHeader.getHash()); + return privateWorldStateArchive + .get(stateRoot) + .map( + privateWorldState -> { + final Account account = privateWorldState.get(sender); + return account == null ? 0L : account.getNonce(); + }) + .orElse(Account.DEFAULT_NONCE); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java index 09a402aacc7..5c25200833b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyController.java @@ -19,17 +19,14 @@ import org.hyperledger.besu.enclave.types.PrivacyGroup.Type; import org.hyperledger.besu.enclave.types.ReceiveResponse; import org.hyperledger.besu.enclave.types.SendResponse; -import org.hyperledger.besu.ethereum.core.Account; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.PrivacyParameters; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.mainnet.TransactionValidator; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.privacy.markertransaction.PrivateMarkerTransactionFactory; -import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; import org.hyperledger.besu.ethereum.transaction.CallParameter; -import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import java.math.BigInteger; import java.util.List; @@ -41,45 +38,43 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; public class DefaultPrivacyController implements PrivacyController { private static final Logger LOG = LogManager.getLogger(); private final Enclave enclave; - private final PrivateStateStorage privateStateStorage; - private final WorldStateArchive privateWorldStateArchive; private final PrivateTransactionValidator privateTransactionValidator; private final PrivateMarkerTransactionFactory privateMarkerTransactionFactory; private final PrivateTransactionSimulator privateTransactionSimulator; + private final PrivateNonceProvider privateNonceProvider; public DefaultPrivacyController( final PrivacyParameters privacyParameters, final Optional chainId, final PrivateMarkerTransactionFactory privateMarkerTransactionFactory, - final PrivateTransactionSimulator privateTransactionSimulator) { + final PrivateTransactionSimulator privateTransactionSimulator, + final PrivateNonceProvider privateNonceProvider) { this( privacyParameters.getEnclave(), - privacyParameters.getPrivateStateStorage(), - privacyParameters.getPrivateWorldStateArchive(), new PrivateTransactionValidator(chainId), privateMarkerTransactionFactory, - privateTransactionSimulator); + privateTransactionSimulator, + privateNonceProvider); } public DefaultPrivacyController( final Enclave enclave, - final PrivateStateStorage privateStateStorage, - final WorldStateArchive privateWorldStateArchive, final PrivateTransactionValidator privateTransactionValidator, final PrivateMarkerTransactionFactory privateMarkerTransactionFactory, - final PrivateTransactionSimulator privateTransactionSimulator) { + final PrivateTransactionSimulator privateTransactionSimulator, + final PrivateNonceProvider privateNonceProvider) { this.enclave = enclave; - this.privateStateStorage = privateStateStorage; - this.privateWorldStateArchive = privateWorldStateArchive; this.privateTransactionValidator = privateTransactionValidator; this.privateMarkerTransactionFactory = privateMarkerTransactionFactory; this.privateTransactionSimulator = privateTransactionSimulator; + this.privateNonceProvider = privateNonceProvider; } @Override @@ -169,27 +164,8 @@ public long determineEeaNonce( @Override public long determineBesuNonce( final Address sender, final String privacyGroupId, final String enclavePublicKey) { - return privateStateStorage - .getLatestStateRoot(Bytes.fromBase64String(privacyGroupId)) - .map( - lastRootHash -> - privateWorldStateArchive - .getMutable(lastRootHash) - .map( - worldState -> { - final Account maybePrivateSender = worldState.get(sender); - - if (maybePrivateSender != null) { - return maybePrivateSender.getNonce(); - } - // account has not interacted in this private state - return Account.DEFAULT_NONCE; - }) - // private state does not exist - .orElse(Account.DEFAULT_NONCE)) - .orElse( - // private state does not exist - Account.DEFAULT_NONCE); + return privateNonceProvider.getNonce( + sender, Bytes32.wrap(Bytes.fromBase64String(privacyGroupId))); } @Override diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateNonceProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateNonceProvider.java new file mode 100644 index 00000000000..5544bce92cd --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateNonceProvider.java @@ -0,0 +1,23 @@ +/* + * 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.privacy; + +import org.hyperledger.besu.ethereum.core.Address; + +import org.apache.tuweni.bytes.Bytes32; + +public interface PrivateNonceProvider { + long getNonce(Address sender, Bytes32 privacyGroupId); +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRootResolver.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRootResolver.java new file mode 100644 index 00000000000..1c48ca7de64 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRootResolver.java @@ -0,0 +1,70 @@ +/* + * 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.privacy; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateBlockMetadata; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; +import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes32; + +public class PrivateStateRootResolver { + public static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); + + private final PrivateStateStorage privateStateStorage; + + public PrivateStateRootResolver(final PrivateStateStorage privateStateStorage) { + this.privateStateStorage = privateStateStorage; + } + + public Hash resolveLastStateRoot(final Bytes32 privacyGroupId, final Hash blockHash) { + final Optional privateBlockMetadataOptional = + privateStateStorage.getPrivateBlockMetadata(blockHash, privacyGroupId); + if (privateBlockMetadataOptional.isPresent()) { + // Check if block already has meta data for the privacy group + return privateBlockMetadataOptional.get().getLatestStateRoot().orElse(EMPTY_ROOT_HASH); + } + + final Optional maybePrivacyGroupHeadBlockMap = + privateStateStorage.getPrivacyGroupHeadBlockMap(blockHash); + if (maybePrivacyGroupHeadBlockMap.isPresent()) { + return resolveLastStateRoot(privacyGroupId, maybePrivacyGroupHeadBlockMap.get()); + } else { + return EMPTY_ROOT_HASH; + } + } + + private Hash resolveLastStateRoot( + final Bytes32 privacyGroupId, final PrivacyGroupHeadBlockMap privacyGroupHeadBlockMap) { + final Hash lastRootHash; + if (privacyGroupHeadBlockMap.containsKey(privacyGroupId)) { + // Check this PG head block is being tracked + final Hash blockHashForLastBlockWithTx = privacyGroupHeadBlockMap.get(privacyGroupId); + lastRootHash = + privateStateStorage + .getPrivateBlockMetadata(blockHashForLastBlockWithTx, privacyGroupId) + .flatMap(PrivateBlockMetadata::getLatestStateRoot) + .orElse(EMPTY_ROOT_HASH); + } else { + // First transaction for this PG + lastRootHash = EMPTY_ROOT_HASH; + } + return lastRootHash; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStorageMigrationTransactionProcessorResult.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStorageMigrationTransactionProcessorResult.java new file mode 100644 index 00000000000..80c33f17394 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateStorageMigrationTransactionProcessorResult.java @@ -0,0 +1,76 @@ +/* + * 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.privacy; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.TransactionValidator; +import org.hyperledger.besu.ethereum.mainnet.ValidationResult; +import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor.Result; + +import java.util.Objects; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; + +public class PrivateStorageMigrationTransactionProcessorResult { + + private final PrivateTransactionProcessor.Result result; + private final Optional resultingRootHash; + + public PrivateStorageMigrationTransactionProcessorResult( + final Result result, final Optional resultingRootHash) { + this.result = result; + this.resultingRootHash = resultingRootHash; + } + + public boolean isSuccessful() { + return result.isSuccessful(); + } + + public Bytes getOutput() { + return result.getOutput(); + } + + public ValidationResult getValidationResult() { + return result.getValidationResult(); + } + + public TransactionProcessor.Result getResult() { + return result; + } + + public Optional getResultingRootHash() { + return resultingRootHash; + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final PrivateStorageMigrationTransactionProcessorResult that = + (PrivateStorageMigrationTransactionProcessorResult) o; + return result.equals(that.result) && resultingRootHash.equals(that.resultingRootHash); + } + + @Override + public int hashCode() { + return Objects.hash(result, resultingRootHash); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransaction.java index e488bdcea82..21bb1021723 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransaction.java @@ -416,7 +416,7 @@ public BigInteger getV() { * * @return the transaction hash */ - public Hash hash() { + public Hash getHash() { if (hash == null) { final Bytes rlp = RLP.encode(this::writeTo); hash = Hash.hash(rlp); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java index 948975c342a..04054a772b0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionProcessor.java @@ -248,6 +248,7 @@ public Result processTransaction( .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) .maxStackSize(maxStackSize) + .transactionHash(transaction.getHash()) .build(); } else { @@ -278,6 +279,7 @@ public Result processTransaction( .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) .maxStackSize(maxStackSize) + .transactionHash(transaction.getHash()) .build(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionReceipt.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionReceipt.java new file mode 100644 index 00000000000..a3595565809 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionReceipt.java @@ -0,0 +1,184 @@ +/* + * 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.privacy; + +import org.hyperledger.besu.ethereum.core.Log; +import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; +import org.hyperledger.besu.ethereum.rlp.RLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import com.google.common.base.MoreObjects; +import org.apache.tuweni.bytes.Bytes; + +/** + * A transaction receipt for a private transaction, containing information pertaining a transaction + * execution. + */ +public class PrivateTransactionReceipt { + + @SuppressWarnings("unchecked") + public static final PrivateTransactionReceipt EMPTY = + new PrivateTransactionReceipt( + 0, Collections.EMPTY_LIST, Bytes.EMPTY, Optional.ofNullable(null)); + + private final int status; + private final List logs; + private final Bytes output; + private final Optional revertReason; + + /** + * Creates an instance of a state root-encoded transaction receipt. + * + * @param status the state root for the world state after the transaction has been processed + * @param logs the total amount of gas consumed in the block after this transaction + * @param output output from the transaction + * @param revertReason the revert reason for a failed transaction (if applicable) + */ + public PrivateTransactionReceipt( + final int status, + final List logs, + final Bytes output, + final Optional revertReason) { + this.status = status; + this.logs = logs; + this.output = output; + this.revertReason = revertReason; + } + + public PrivateTransactionReceipt(final TransactionProcessor.Result result) { + this( + result.getStatus() == PrivateTransactionProcessor.Result.Status.SUCCESSFUL ? 1 : 0, + result.getLogs(), + result.getOutput(), + result.getRevertReason()); + } + + /** + * Write an RLP representation. + * + * @param out The RLP output to write to + */ + public void writeTo(final RLPOutput out) { + out.startList(); + + out.writeLongScalar(status); + out.writeList(logs, Log::writeTo); + out.writeBytes(output); + if (revertReason.isPresent()) { + out.writeBytes(revertReason.get()); + } + out.endList(); + } + + /** + * Creates a transaction receipt for the given RLP + * + * @param input the RLP-encoded transaction receipt + * @return the transaction receipt + */ + public static PrivateTransactionReceipt readFrom(final RLPInput input) { + input.enterList(); + + try { + // Get the first element to check later to determine the + // correct transaction receipt encoding to use. + final int status = input.readIntScalar(); + final List logs = input.readList(Log::readFrom); + final Bytes output = input.readBytes(); + final Optional revertReason; + if (input.isEndOfCurrentList()) { + revertReason = Optional.empty(); + } else { + revertReason = Optional.of(input.readBytes()); + } + return new PrivateTransactionReceipt(status, logs, output, revertReason); + } finally { + input.leaveList(); + } + } + + /** + * Returns the status code for the status-encoded transaction receipt + * + * @return the status code if the transaction receipt is status-encoded; otherwise {@code -1} + */ + public int getStatus() { + return status; + } + + /** + * Returns the logs generated by the transaction. + * + * @return the logs generated by the transaction + */ + public List getLogs() { + return logs; + } + + /** + * Returns the output generated by the transaction. + * + * @return the output generated by the transaction + */ + public Bytes getOutput() { + return output; + } + + /** + * Returns the revert reason generated by the transaction. + * + * @return the revert reason generated by the transaction + */ + public Optional getRevertReason() { + return revertReason; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof PrivateTransactionReceipt)) { + return false; + } + final PrivateTransactionReceipt other = (PrivateTransactionReceipt) obj; + return logs.equals(other.getLogs()) + && status == other.status + && output.equals(other.output) + && revertReason.isPresent() + ? revertReason.get().equals(other.revertReason.get()) + : true; + } + + @Override + public int hashCode() { + return Objects.hash(status, logs, output, revertReason); + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("status", status) + .add("logs", logs) + .add("output", output) + .add("revertReason", revertReason) + .toString(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java index acb87410971..2211f37e507 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionSimulator.java @@ -27,7 +27,6 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; import org.hyperledger.besu.ethereum.transaction.CallParameter; -import org.hyperledger.besu.ethereum.trie.MerklePatriciaTrie; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.DebugOperationTracer; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; @@ -35,6 +34,7 @@ import java.util.Optional; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; /* * Used to process transactions for priv_call. @@ -44,8 +44,6 @@ */ public class PrivateTransactionSimulator { - private static final Hash EMPTY_ROOT_HASH = Hash.wrap(MerklePatriciaTrie.EMPTY_TRIE_NODE_HASH); - // Dummy signature for transactions to not fail being processed. private static final SECP256K1.Signature FAKE_SIGNATURE = SECP256K1.Signature.create(SECP256K1.HALF_CURVE_ORDER, SECP256K1.HALF_CURVE_ORDER, (byte) 0); @@ -57,6 +55,7 @@ public class PrivateTransactionSimulator { private final WorldStateArchive worldStateArchive; private final ProtocolSchedule protocolSchedule; private final PrivacyParameters privacyParameters; + private final PrivateStateRootResolver privateStateRootResolver; public PrivateTransactionSimulator( final Blockchain blockchain, @@ -67,6 +66,8 @@ public PrivateTransactionSimulator( this.worldStateArchive = worldStateArchive; this.protocolSchedule = protocolSchedule; this.privacyParameters = privacyParameters; + this.privateStateRootResolver = + new PrivateStateRootResolver(privacyParameters.getPrivateStateStorage()); } public Optional process( @@ -94,12 +95,9 @@ private Optional process( } // get the last world state root hash or create a new one - final Bytes privacyGroupId = Bytes.fromBase64String(privacyGroupIdString); + final Bytes32 privacyGroupId = Bytes32.wrap(Bytes.fromBase64String(privacyGroupIdString)); final Hash lastRootHash = - privacyParameters - .getPrivateStateStorage() - .getLatestStateRoot(privacyGroupId) - .orElse(EMPTY_ROOT_HASH); + privateStateRootResolver.resolveLastStateRoot(privacyGroupId, header.getHash()); final MutableWorldState disposablePrivateState = privacyParameters.getPrivateWorldStateArchive().getMutable(lastRootHash).get(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidator.java index c1a42e911a8..130dc0ea14e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionValidator.java @@ -36,25 +36,25 @@ public PrivateTransactionValidator(final Optional chainId) { public ValidationResult validate( final PrivateTransaction transaction, final Long accountNonce) { - LOG.debug("Validating private transaction fields of {}", transaction.hash()); + LOG.debug("Validating private transaction fields of {}", transaction.getHash()); final ValidationResult privateFieldsValidationResult = validatePrivateTransactionFields(transaction); if (!privateFieldsValidationResult.isValid()) { LOG.debug( "Private Transaction fields are invalid {}, {}", - transaction.hash(), + transaction.getHash(), privateFieldsValidationResult.getErrorMessage()); return privateFieldsValidationResult; } - LOG.debug("Validating the signature of Private Transaction {} ", transaction.hash()); + LOG.debug("Validating the signature of Private Transaction {} ", transaction.getHash()); final ValidationResult signatureValidationResult = validateTransactionSignature(transaction); if (!signatureValidationResult.isValid()) { LOG.debug( "Private Transaction {}, failed validation {}, {}", - transaction.hash(), + transaction.getHash(), signatureValidationResult.getInvalidReason(), signatureValidationResult.getErrorMessage()); return signatureValidationResult; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/LegacyPrivateStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/LegacyPrivateStateKeyValueStorage.java new file mode 100644 index 00000000000..c3175c33b0f --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/LegacyPrivateStateKeyValueStorage.java @@ -0,0 +1,178 @@ +/* + * 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.privacy.storage; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Log; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; +import org.hyperledger.besu.ethereum.rlp.RLP; +import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; +import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; + +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; + +@Deprecated +public class LegacyPrivateStateKeyValueStorage implements LegacyPrivateStateStorage { + + public static final Bytes EVENTS_KEY_SUFFIX = Bytes.of("EVENTS".getBytes(UTF_8)); + public static final Bytes LOGS_KEY_SUFFIX = Bytes.of("LOGS".getBytes(UTF_8)); + public static final Bytes OUTPUT_KEY_SUFFIX = Bytes.of("OUTPUT".getBytes(UTF_8)); + public static final Bytes METADATA_KEY_SUFFIX = Bytes.of("METADATA".getBytes(UTF_8)); + public static final Bytes STATUS_KEY_SUFFIX = Bytes.of("STATUS".getBytes(UTF_8)); + public static final Bytes REVERT_KEY_SUFFIX = Bytes.of("REVERT".getBytes(UTF_8)); + + private final KeyValueStorage keyValueStorage; + + public LegacyPrivateStateKeyValueStorage(final KeyValueStorage keyValueStorage) { + this.keyValueStorage = keyValueStorage; + } + + @Override + public Optional getLatestStateRoot(final Bytes privacyId) { + final byte[] id = privacyId.toArrayUnsafe(); + + if (keyValueStorage.get(id).isPresent()) { + return Optional.of(Hash.wrap(Bytes32.wrap(keyValueStorage.get(id).get()))); + } else { + return Optional.empty(); + } + } + + @Override + public Optional> getTransactionLogs(final Bytes32 transactionHash) { + final Optional> logs = get(transactionHash, LOGS_KEY_SUFFIX).map(this::rlpDecodeLog); + if (logs.isEmpty()) { + return get(transactionHash, EVENTS_KEY_SUFFIX).map(this::rlpDecodeLog); + } + return logs; + } + + @Override + public Optional getTransactionOutput(final Bytes32 transactionHash) { + return get(transactionHash, OUTPUT_KEY_SUFFIX); + } + + @Override + public Optional getStatus(final Bytes32 transactionHash) { + return get(transactionHash, STATUS_KEY_SUFFIX); + } + + @Override + public Optional getRevertReason(final Bytes32 transactionHash) { + return get(transactionHash, REVERT_KEY_SUFFIX); + } + + @Override + public Optional getTransactionMetadata( + final Bytes32 blockHash, final Bytes32 transactionHash) { + return get(Bytes.concatenate(blockHash, transactionHash), METADATA_KEY_SUFFIX) + .map(bytes -> PrivateTransactionMetadata.readFrom(new BytesValueRLPInput(bytes, false))); + } + + @Override + public boolean isPrivateStateAvailable(final Bytes32 transactionHash) { + return false; + } + + @Override + public boolean isWorldStateAvailable(final Bytes32 rootHash) { + return false; + } + + private Optional get(final Bytes key, final Bytes keySuffix) { + return keyValueStorage.get(Bytes.concatenate(key, keySuffix).toArrayUnsafe()).map(Bytes::wrap); + } + + private List rlpDecodeLog(final Bytes bytes) { + return RLP.input(bytes).readList(Log::readFrom); + } + + @Override + public LegacyPrivateStateStorage.Updater updater() { + return new LegacyPrivateStateKeyValueStorage.Updater(keyValueStorage.startTransaction()); + } + + public static class Updater implements LegacyPrivateStateStorage.Updater { + + private final KeyValueStorageTransaction transaction; + + private Updater(final KeyValueStorageTransaction transaction) { + this.transaction = transaction; + } + + @Override + public Updater putLatestStateRoot(final Bytes privacyId, final Hash privateStateHash) { + transaction.put(privacyId.toArrayUnsafe(), privateStateHash.toArray()); + return this; + } + + @Override + public Updater putTransactionLogs(final Bytes32 transactionHash, final List logs) { + set(transactionHash, LOGS_KEY_SUFFIX, RLP.encode(out -> out.writeList(logs, Log::writeTo))); + return this; + } + + @Override + public Updater putTransactionResult(final Bytes32 transactionHash, final Bytes events) { + set(transactionHash, OUTPUT_KEY_SUFFIX, events); + return this; + } + + @Override + public Updater putTransactionStatus(final Bytes32 transactionHash, final Bytes status) { + set(transactionHash, STATUS_KEY_SUFFIX, status); + return this; + } + + @Override + public Updater putTransactionRevertReason( + final Bytes32 transactionHash, final Bytes revertReason) { + set(transactionHash, REVERT_KEY_SUFFIX, revertReason); + return this; + } + + @Override + public Updater putTransactionMetadata( + final Bytes32 blockHash, + final Bytes32 transactionHash, + final PrivateTransactionMetadata metadata) { + set( + Bytes.concatenate(blockHash, transactionHash), + METADATA_KEY_SUFFIX, + RLP.encode(metadata::writeTo)); + return this; + } + + @Override + public void commit() { + transaction.commit(); + } + + @Override + public void rollback() { + transaction.rollback(); + } + + private void set(final Bytes key, final Bytes keySuffix, final Bytes value) { + transaction.put(Bytes.concatenate(key, keySuffix).toArrayUnsafe(), value.toArrayUnsafe()); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/LegacyPrivateStateStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/LegacyPrivateStateStorage.java new file mode 100644 index 00000000000..4ea10e7dcc6 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/LegacyPrivateStateStorage.java @@ -0,0 +1,68 @@ +/* + * 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.privacy.storage; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Log; + +import java.util.List; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; + +/** This interface contains the methods used to access the private state until version 1.3 */ +@Deprecated +public interface LegacyPrivateStateStorage { + + Optional getLatestStateRoot(Bytes privacyId); + + Optional> getTransactionLogs(Bytes32 transactionHash); + + Optional getTransactionOutput(Bytes32 transactionHash); + + Optional getStatus(Bytes32 transactionHash); + + Optional getRevertReason(Bytes32 transactionHash); + + Optional getTransactionMetadata( + Bytes32 blockHash, Bytes32 transactionHash); + + boolean isPrivateStateAvailable(Bytes32 transactionHash); + + boolean isWorldStateAvailable(Bytes32 rootHash); + + Updater updater(); + + interface Updater { + + Updater putLatestStateRoot(Bytes privacyId, Hash privateStateHash); + + Updater putTransactionLogs(Bytes32 transactionHash, List logs); + + Updater putTransactionResult(Bytes32 transactionHash, Bytes events); + + Updater putTransactionStatus(Bytes32 transactionHash, Bytes status); + + Updater putTransactionRevertReason(Bytes32 txHash, Bytes bytesValue); + + Updater putTransactionMetadata( + Bytes32 blockHash, Bytes32 transactionHash, PrivateTransactionMetadata metadata); + + void commit(); + + void rollback(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivacyGroupHeadBlockMap.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivacyGroupHeadBlockMap.java new file mode 100644 index 00000000000..f0f8db26221 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivacyGroupHeadBlockMap.java @@ -0,0 +1,134 @@ +/* + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.privacy.storage; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.rlp.RLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import org.apache.tuweni.bytes.Bytes32; + +public class PrivacyGroupHeadBlockMap implements Map { + private final HashMap map; + + public static final PrivacyGroupHeadBlockMap EMPTY = + new PrivacyGroupHeadBlockMap(Collections.emptyMap()); + + public PrivacyGroupHeadBlockMap(final Map map) { + this.map = new HashMap<>(map); + } + + public void writeTo(final RLPOutput out) { + out.startList(); + + map.forEach((key, value) -> new RLPMapEntry(key, value).writeTo(out)); + + out.endList(); + } + + public static PrivacyGroupHeadBlockMap readFrom(final RLPInput input) { + final List entries = input.readList(RLPMapEntry::readFrom); + + final HashMap map = new HashMap<>(); + entries.forEach(e -> map.put(Bytes32.wrap(e.getKey()), Hash.wrap(Bytes32.wrap(e.getValue())))); + + return new PrivacyGroupHeadBlockMap(map); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final PrivacyGroupHeadBlockMap that = (PrivacyGroupHeadBlockMap) o; + return map.equals(that.map); + } + + public boolean contains(final Bytes32 key, final Hash value) { + return map.containsKey(key) && map.get(key).equals(value); + } + + @Override + public int hashCode() { + return Objects.hash(map); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean containsKey(final Object key) { + return map.containsKey(key); + } + + @Override + public boolean containsValue(final Object value) { + return map.containsValue(value); + } + + @Override + public Hash get(final Object key) { + return map.get(key); + } + + @Override + public Hash put(final Bytes32 key, final Hash value) { + return map.put(key, value); + } + + @Override + public Hash remove(final Object key) { + return map.remove(key); + } + + @Override + public void putAll(final Map m) { + map.putAll(m); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Set keySet() { + return map.keySet(); + } + + @Override + public Collection values() { + return map.values(); + } + + @Override + public Set> entrySet() { + return map.entrySet(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivacyStorageProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivacyStorageProvider.java index fd275d0c692..2dc167a884b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivacyStorageProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivacyStorageProvider.java @@ -27,5 +27,8 @@ public interface PrivacyStorageProvider extends Closeable { PrivateStateStorage createPrivateStateStorage(); + @Deprecated + LegacyPrivateStateStorage createLegacyPrivateStateStorage(); + int getFactoryVersion(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateBlockMetadata.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateBlockMetadata.java new file mode 100644 index 00000000000..ed69e9e89fa --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateBlockMetadata.java @@ -0,0 +1,68 @@ +/* + * 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.privacy.storage; + +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.rlp.RLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class PrivateBlockMetadata { + + public static PrivateBlockMetadata empty() { + return new PrivateBlockMetadata(new ArrayList<>()); + } + + private final List privateTransactionMetadataList; + + public PrivateBlockMetadata( + final List privateTransactionMetadataList) { + this.privateTransactionMetadataList = + privateTransactionMetadataList == null ? new ArrayList<>() : privateTransactionMetadataList; + } + + public List getPrivateTransactionMetadataList() { + return privateTransactionMetadataList; + } + + public void addPrivateTransactionMetadata( + final PrivateTransactionMetadata privateTransactionMetadata) { + privateTransactionMetadataList.add(privateTransactionMetadata); + } + + public void writeTo(final RLPOutput out) { + out.writeList(privateTransactionMetadataList, PrivateTransactionMetadata::writeTo); + } + + public static PrivateBlockMetadata readFrom(final RLPInput in) { + final List privateTransactionMetadataList = + in.readList(PrivateTransactionMetadata::readFrom); + return new PrivateBlockMetadata(privateTransactionMetadataList); + } + + public Optional getLatestStateRoot() { + if (privateTransactionMetadataList.size() > 0) { + return Optional.ofNullable( + privateTransactionMetadataList + .get(privateTransactionMetadataList.size() - 1) + .getStateRoot()); + } else { + return Optional.empty(); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorage.java index 0a8642b8796..c3ef13e7235 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorage.java @@ -16,28 +16,30 @@ import static java.nio.charset.StandardCharsets.UTF_8; -import org.hyperledger.besu.ethereum.core.Hash; -import org.hyperledger.besu.ethereum.core.Log; +import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; import org.hyperledger.besu.plugin.services.storage.KeyValueStorageTransaction; -import java.util.List; +import java.util.Arrays; import java.util.Optional; +import java.util.function.Predicate; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; public class PrivateStateKeyValueStorage implements PrivateStateStorage { - @Deprecated private static final Bytes EVENTS_KEY_SUFFIX = Bytes.of("EVENTS".getBytes(UTF_8)); + public static final int SCHEMA_VERSION_1_0_0 = 1; + public static final int SCHEMA_VERSION_1_4_0 = 2; - private static final Bytes LOGS_KEY_SUFFIX = Bytes.of("LOGS".getBytes(UTF_8)); - private static final Bytes OUTPUT_KEY_SUFFIX = Bytes.of("OUTPUT".getBytes(UTF_8)); + private static final Bytes DB_VERSION_KEY = Bytes.of("DBVERSION".getBytes(UTF_8)); + private static final Bytes TX_RECEIPT_SUFFIX = Bytes.of("RECEIPT".getBytes(UTF_8)); private static final Bytes METADATA_KEY_SUFFIX = Bytes.of("METADATA".getBytes(UTF_8)); - private static final Bytes STATUS_KEY_SUFFIX = Bytes.of("STATUS".getBytes(UTF_8)); - private static final Bytes REVERT_KEY_SUFFIX = Bytes.of("REVERT".getBytes(UTF_8)); + private static final Bytes PRIVACY_GROUP_HEAD_BLOCK_MAP_SUFFIX = + Bytes.of("PGHEADMAP".getBytes(UTF_8)); + private static final Bytes LEGACY_STATUS_KEY_SUFFIX = Bytes.of("STATUS".getBytes(UTF_8)); private final KeyValueStorage keyValueStorage; @@ -46,63 +48,52 @@ public PrivateStateKeyValueStorage(final KeyValueStorage keyValueStorage) { } @Override - public Optional getLatestStateRoot(final Bytes privacyId) { - final byte[] id = privacyId.toArrayUnsafe(); - - if (keyValueStorage.get(id).isPresent()) { - return Optional.of(Hash.wrap(Bytes32.wrap(keyValueStorage.get(id).get()))); - } else { - return Optional.empty(); - } - } - - @Override - public Optional> getTransactionLogs(final Bytes32 transactionHash) { - final Optional> logs = get(transactionHash, LOGS_KEY_SUFFIX).map(this::rlpDecodeLog); - if (logs.isEmpty()) { - return get(transactionHash, EVENTS_KEY_SUFFIX).map(this::rlpDecodeLog); - } - return logs; - } - - @Override - public Optional getTransactionOutput(final Bytes32 transactionHash) { - return get(transactionHash, OUTPUT_KEY_SUFFIX); + public Optional getTransactionReceipt( + final Bytes32 blockHash, final Bytes32 txHash) { + final Bytes blockHashTxHash = Bytes.concatenate(blockHash, txHash); + return get(blockHashTxHash, TX_RECEIPT_SUFFIX) + .map(b -> PrivateTransactionReceipt.readFrom(new BytesValueRLPInput(b, false))); } @Override - public Optional getStatus(final Bytes32 transactionHash) { - return get(transactionHash, STATUS_KEY_SUFFIX); + public Optional getPrivateBlockMetadata( + final Bytes32 blockHash, final Bytes32 privacyGroupId) { + return get(Bytes.concatenate(blockHash, privacyGroupId), METADATA_KEY_SUFFIX) + .map(this::rlpDecodePrivateBlockMetadata); } @Override - public Optional getRevertReason(final Bytes32 transactionHash) { - return get(transactionHash, REVERT_KEY_SUFFIX); + public Optional getPrivacyGroupHeadBlockMap(final Bytes32 blockHash) { + return get(blockHash, PRIVACY_GROUP_HEAD_BLOCK_MAP_SUFFIX) + .map(b -> PrivacyGroupHeadBlockMap.readFrom(new BytesValueRLPInput(b, false))); } @Override - public Optional getTransactionMetadata( - final Bytes32 blockHash, final Bytes32 transactionHash) { - return get(Bytes.concatenate(blockHash, transactionHash), METADATA_KEY_SUFFIX) - .map(bytes -> PrivateTransactionMetadata.readFrom(new BytesValueRLPInput(bytes, false))); + public int getSchemaVersion() { + return get(Bytes.EMPTY, DB_VERSION_KEY).map(Bytes::toInt).orElse(SCHEMA_VERSION_1_0_0); } @Override - public boolean isPrivateStateAvailable(final Bytes32 transactionHash) { - return false; + public boolean isEmpty() { + return keyValueStorage.getAllKeysThat(containsSuffix(LEGACY_STATUS_KEY_SUFFIX)).isEmpty() + && keyValueStorage.getAllKeysThat(containsSuffix(TX_RECEIPT_SUFFIX)).isEmpty() + && keyValueStorage.getAllKeysThat(containsSuffix(METADATA_KEY_SUFFIX)).isEmpty(); } - @Override - public boolean isWorldStateAvailable(final Bytes32 rootHash) { - return false; + private Predicate containsSuffix(final Bytes suffix) { + return key -> + key.length > suffix.toArrayUnsafe().length + && Arrays.equals( + Arrays.copyOfRange(key, key.length - suffix.toArrayUnsafe().length, key.length), + suffix.toArrayUnsafe()); } private Optional get(final Bytes key, final Bytes keySuffix) { return keyValueStorage.get(Bytes.concatenate(key, keySuffix).toArrayUnsafe()).map(Bytes::wrap); } - private List rlpDecodeLog(final Bytes bytes) { - return RLP.input(bytes).readList(Log::readFrom); + private PrivateBlockMetadata rlpDecodePrivateBlockMetadata(final Bytes bytes) { + return PrivateBlockMetadata.readFrom(RLP.input(bytes)); } @Override @@ -119,46 +110,37 @@ private Updater(final KeyValueStorageTransaction transaction) { } @Override - public Updater putLatestStateRoot(final Bytes privacyId, final Hash privateStateHash) { - transaction.put(privacyId.toArrayUnsafe(), privateStateHash.toArray()); - return this; - } - - @Override - public Updater putTransactionLogs(final Bytes32 transactionHash, final List logs) { - set(transactionHash, LOGS_KEY_SUFFIX, RLP.encode(out -> out.writeList(logs, Log::writeTo))); - return this; - } - - @Override - public Updater putTransactionResult(final Bytes32 transactionHash, final Bytes events) { - set(transactionHash, OUTPUT_KEY_SUFFIX, events); + public PrivateStateStorage.Updater putTransactionReceipt( + final Bytes32 blockHash, + final Bytes32 transactionHash, + final PrivateTransactionReceipt receipt) { + final Bytes blockHashTxHash = Bytes.concatenate(blockHash, transactionHash); + set(blockHashTxHash, TX_RECEIPT_SUFFIX, RLP.encode(receipt::writeTo)); return this; } @Override - public PrivateStateStorage.Updater putTransactionStatus( - final Bytes32 transactionHash, final Bytes status) { - set(transactionHash, STATUS_KEY_SUFFIX, status); + public PrivateStateStorage.Updater putPrivateBlockMetadata( + final Bytes32 blockHash, + final Bytes32 privacyGroupId, + final PrivateBlockMetadata metadata) { + set( + Bytes.concatenate(blockHash, privacyGroupId), + METADATA_KEY_SUFFIX, + RLP.encode(metadata::writeTo)); return this; } @Override - public PrivateStateStorage.Updater putTransactionRevertReason( - final Bytes32 transactionHash, final Bytes revertReason) { - set(transactionHash, REVERT_KEY_SUFFIX, revertReason); + public PrivateStateStorage.Updater putPrivacyGroupHeadBlockMap( + final Bytes32 blockHash, final PrivacyGroupHeadBlockMap map) { + set(blockHash, PRIVACY_GROUP_HEAD_BLOCK_MAP_SUFFIX, RLP.encode(map::writeTo)); return this; } @Override - public Updater putTransactionMetadata( - final Bytes32 blockHash, - final Bytes32 transactionHash, - final PrivateTransactionMetadata metadata) { - set( - Bytes.concatenate(blockHash, transactionHash), - METADATA_KEY_SUFFIX, - RLP.encode(metadata::writeTo)); + public PrivateStateStorage.Updater putDatabaseVersion(final int version) { + set(Bytes.EMPTY, DB_VERSION_KEY, Bytes.ofUnsignedInt(version)); return this; } @@ -175,5 +157,10 @@ public void rollback() { private void set(final Bytes key, final Bytes keySuffix, final Bytes value) { transaction.put(Bytes.concatenate(key, keySuffix).toArrayUnsafe(), value.toArrayUnsafe()); } + + @Override + public void remove(final Bytes key, final Bytes keySuffix) { + transaction.remove(Bytes.concatenate(key, keySuffix).toArrayUnsafe()); + } } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateStorage.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateStorage.java index 1fc0f37df1f..29d8b57db53 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateStorage.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateStorage.java @@ -14,10 +14,8 @@ */ package org.hyperledger.besu.ethereum.privacy.storage; -import org.hyperledger.besu.ethereum.core.Hash; -import org.hyperledger.besu.ethereum.core.Log; +import org.hyperledger.besu.ethereum.privacy.PrivateTransactionReceipt; -import java.util.List; import java.util.Optional; import org.apache.tuweni.bytes.Bytes; @@ -25,44 +23,34 @@ public interface PrivateStateStorage { - @Deprecated - Optional getLatestStateRoot(Bytes privacyId); + Optional getTransactionReceipt(Bytes32 blockHash, Bytes32 txHash); - Optional> getTransactionLogs(Bytes32 transactionHash); + Optional getPrivateBlockMetadata(Bytes32 blockHash, Bytes32 privacyGroupId); - Optional getTransactionOutput(Bytes32 transactionHash); + Optional getPrivacyGroupHeadBlockMap(Bytes32 blockHash); - Optional getStatus(Bytes32 transactionHash); + int getSchemaVersion(); - Optional getRevertReason(Bytes32 transactionHash); - - Optional getTransactionMetadata( - Bytes32 blockHash, Bytes32 transactionHash); - - boolean isPrivateStateAvailable(Bytes32 transactionHash); - - boolean isWorldStateAvailable(Bytes32 rootHash); + boolean isEmpty(); Updater updater(); interface Updater { - @Deprecated - Updater putLatestStateRoot(Bytes privacyId, Hash privateStateHash); + Updater putTransactionReceipt( + Bytes32 blockHash, Bytes32 transactionHash, PrivateTransactionReceipt receipt); - Updater putTransactionLogs(Bytes32 transactionHash, List logs); + Updater putPrivateBlockMetadata( + Bytes32 blockHash, Bytes32 privacyGroupId, PrivateBlockMetadata metadata); - Updater putTransactionResult(Bytes32 transactionHash, Bytes events); + Updater putPrivacyGroupHeadBlockMap(Bytes32 blockHash, PrivacyGroupHeadBlockMap map); - Updater putTransactionStatus(Bytes32 transactionHash, Bytes status); - - Updater putTransactionRevertReason(Bytes32 txHash, Bytes bytesValue); - - Updater putTransactionMetadata( - Bytes32 blockHash, Bytes32 transactionHash, PrivateTransactionMetadata metadata); + Updater putDatabaseVersion(int version); void commit(); void rollback(); + + void remove(final Bytes key, final Bytes keySuffix); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateTransactionMetadata.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateTransactionMetadata.java index b4e9233833c..01f95c2eaa3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateTransactionMetadata.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateTransactionMetadata.java @@ -20,9 +20,11 @@ /** Mined private transaction metadata. */ public class PrivateTransactionMetadata { + private final Hash privacyMarkerTransactionHash; private final Hash stateRoot; - public PrivateTransactionMetadata(final Hash stateRoot) { + public PrivateTransactionMetadata(final Hash privacyMarkerTransactionHash, final Hash stateRoot) { + this.privacyMarkerTransactionHash = privacyMarkerTransactionHash; this.stateRoot = stateRoot; } @@ -30,9 +32,14 @@ public Hash getStateRoot() { return stateRoot; } + public Hash getPrivacyMarkerTransactionHash() { + return privacyMarkerTransactionHash; + } + public void writeTo(final RLPOutput out) { out.startList(); + out.writeBytes(privacyMarkerTransactionHash); out.writeBytes(stateRoot); out.endList(); @@ -42,7 +49,8 @@ public static PrivateTransactionMetadata readFrom(final RLPInput input) { input.enterList(); final PrivateTransactionMetadata privateTransactionMetadata = - new PrivateTransactionMetadata(Hash.wrap(input.readBytes32())); + new PrivateTransactionMetadata( + Hash.wrap(input.readBytes32()), Hash.wrap(input.readBytes32())); input.leaveList(); return privateTransactionMetadata; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/RLPMapEntry.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/RLPMapEntry.java new file mode 100644 index 00000000000..d703e49121e --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/RLPMapEntry.java @@ -0,0 +1,71 @@ +/* + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.privacy.storage; + +import org.hyperledger.besu.ethereum.rlp.RLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPOutput; + +import java.util.Objects; + +import org.apache.tuweni.bytes.Bytes; + +public class RLPMapEntry { + private final Bytes key; + private final Bytes value; + + public RLPMapEntry(final Bytes key, final Bytes value) { + this.key = key; + this.value = value; + } + + public Bytes getKey() { + return key; + } + + public Bytes getValue() { + return value; + } + + public void writeTo(final RLPOutput out) { + out.startList(); + + out.writeBytes(key); + out.writeBytes(value); + + out.endList(); + } + + public static RLPMapEntry readFrom(final RLPInput input) { + input.enterList(); + + final RLPMapEntry rlpMapEntry = new RLPMapEntry(input.readBytes(), input.readBytes()); + + input.leaveList(); + return rlpMapEntry; + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final RLPMapEntry rlpMapEntry = (RLPMapEntry) o; + return key.equals(rlpMapEntry.key) && value.equals(rlpMapEntry.value); + } + + @Override + public int hashCode() { + return Objects.hash(key, value); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/keyvalue/PrivacyKeyValueStorageProvider.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/keyvalue/PrivacyKeyValueStorageProvider.java index 1b9b67831a8..d54a4ba164f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/keyvalue/PrivacyKeyValueStorageProvider.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/keyvalue/PrivacyKeyValueStorageProvider.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.privacy.storage.keyvalue; +import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateKeyValueStorage; +import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateStorage; import org.hyperledger.besu.ethereum.privacy.storage.PrivacyStorageProvider; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; @@ -27,36 +29,41 @@ public class PrivacyKeyValueStorageProvider implements PrivacyStorageProvider { - private final KeyValueStorage privateWorldStateStorage; - private final KeyValueStorage privateWorldStatePreimageStorage; - private final KeyValueStorage privateStateStorage; + private final KeyValueStorage privateWorldStateKeyValueStorage; + private final KeyValueStorage privateWorldStatePreimageKeyValueStorage; + private final KeyValueStorage privateStateKeyValueStorage; private final int factoryVersion; public PrivacyKeyValueStorageProvider( - final KeyValueStorage privateWorldStateStorage, - final KeyValueStorage privateWorldStatePreimageStorage, - final KeyValueStorage privateStateStorage, + final KeyValueStorage privateWorldStateKeyValueStorage, + final KeyValueStorage privateWorldStatePreimageKeyValueStorage, + final KeyValueStorage privateStateKeyValueStorage, final int factoryVersion) { - this.privateWorldStateStorage = privateWorldStateStorage; - this.privateWorldStatePreimageStorage = privateWorldStatePreimageStorage; - this.privateStateStorage = privateStateStorage; + this.privateWorldStateKeyValueStorage = privateWorldStateKeyValueStorage; + this.privateWorldStatePreimageKeyValueStorage = privateWorldStatePreimageKeyValueStorage; + this.privateStateKeyValueStorage = privateStateKeyValueStorage; this.factoryVersion = factoryVersion; } @Override public WorldStateStorage createWorldStateStorage() { - return new WorldStateKeyValueStorage(privateWorldStateStorage); + return new WorldStateKeyValueStorage(privateWorldStateKeyValueStorage); + } + + @Override + public WorldStatePreimageStorage createWorldStatePreimageStorage() { + return new WorldStatePreimageKeyValueStorage(privateWorldStatePreimageKeyValueStorage); } @Override public PrivateStateStorage createPrivateStateStorage() { - return new PrivateStateKeyValueStorage(privateStateStorage); + return new PrivateStateKeyValueStorage(privateStateKeyValueStorage); } @Override - public WorldStatePreimageStorage createWorldStatePreimageStorage() { - return new WorldStatePreimageKeyValueStorage(privateWorldStatePreimageStorage); + public LegacyPrivateStateStorage createLegacyPrivateStateStorage() { + return new LegacyPrivateStateKeyValueStorage(privateStateKeyValueStorage); } @Override @@ -66,8 +73,8 @@ public int getFactoryVersion() { @Override public void close() throws IOException { - privateWorldStateStorage.close(); - privateWorldStatePreimageStorage.close(); - privateStateStorage.close(); + privateWorldStateKeyValueStorage.close(); + privateWorldStatePreimageKeyValueStorage.close(); + privateStateKeyValueStorage.close(); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java new file mode 100644 index 00000000000..3e6bce0e0c2 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java @@ -0,0 +1,160 @@ +/* + * 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.privacy.storage.migration; + +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.MutableAccount; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.core.WorldUpdater; +import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor; +import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor; +import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; +import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams; +import org.hyperledger.besu.ethereum.vm.BlockHashLookup; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PrivateMigrationBlockProcessor { + + private static final Logger LOG = LogManager.getLogger(); + + static final int MAX_GENERATION = 6; + + private final TransactionProcessor transactionProcessor; + private final MainnetBlockProcessor.TransactionReceiptFactory transactionReceiptFactory; + final Wei blockReward; + private final boolean skipZeroBlockRewards; + private final MiningBeneficiaryCalculator miningBeneficiaryCalculator; + + public PrivateMigrationBlockProcessor( + final TransactionProcessor transactionProcessor, + final MainnetBlockProcessor.TransactionReceiptFactory transactionReceiptFactory, + final Wei blockReward, + final MiningBeneficiaryCalculator miningBeneficiaryCalculator, + final boolean skipZeroBlockRewards) { + this.transactionProcessor = transactionProcessor; + this.transactionReceiptFactory = transactionReceiptFactory; + this.blockReward = blockReward; + this.miningBeneficiaryCalculator = miningBeneficiaryCalculator; + this.skipZeroBlockRewards = skipZeroBlockRewards; + } + + public PrivateMigrationBlockProcessor(final ProtocolSpec protocolSpec) { + this( + protocolSpec.getTransactionProcessor(), + protocolSpec.getTransactionReceiptFactory(), + protocolSpec.getBlockReward(), + protocolSpec.getMiningBeneficiaryCalculator(), + protocolSpec.isSkipZeroBlockRewards()); + } + + public AbstractBlockProcessor.Result processBlock( + final Blockchain blockchain, + final MutableWorldState worldState, + final BlockHeader blockHeader, + final List transactions, + final List ommers) { + long gasUsed = 0; + final List receipts = new ArrayList<>(); + + for (final Transaction transaction : transactions) { + final long remainingGasBudget = blockHeader.getGasLimit() - gasUsed; + if (Long.compareUnsigned(transaction.getGasLimit(), remainingGasBudget) > 0) { + LOG.warn( + "Transaction processing error: transaction gas limit {} exceeds available block budget remaining {}", + transaction.getGasLimit(), + remainingGasBudget); + return AbstractBlockProcessor.Result.failed(); + } + + final WorldUpdater worldStateUpdater = worldState.updater(); + final BlockHashLookup blockHashLookup = new BlockHashLookup(blockHeader, blockchain); + final Address miningBeneficiary = + miningBeneficiaryCalculator.calculateBeneficiary(blockHeader); + + final TransactionProcessor.Result result = + transactionProcessor.processTransaction( + blockchain, + worldStateUpdater, + blockHeader, + transaction, + miningBeneficiary, + blockHashLookup, + true, + TransactionValidationParams.processingBlock()); + if (result.isInvalid()) { + return AbstractBlockProcessor.Result.failed(); + } + + worldStateUpdater.commit(); + gasUsed = transaction.getGasLimit() - result.getGasRemaining() + gasUsed; + final TransactionReceipt transactionReceipt = + transactionReceiptFactory.create(result, worldState, gasUsed); + receipts.add(transactionReceipt); + } + + if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) { + return AbstractBlockProcessor.Result.failed(); + } + + return AbstractBlockProcessor.Result.successful(receipts); + } + + private boolean rewardCoinbase( + final MutableWorldState worldState, + final ProcessableBlockHeader header, + final List ommers, + final boolean skipZeroBlockRewards) { + if (skipZeroBlockRewards && blockReward.isZero()) { + return true; + } + + final Wei coinbaseReward = blockReward.add(blockReward.multiply(ommers.size()).divide(32)); + final WorldUpdater updater = worldState.updater(); + final MutableAccount coinbase = updater.getOrCreate(header.getCoinbase()).getMutable(); + + coinbase.incrementBalance(coinbaseReward); + for (final BlockHeader ommerHeader : ommers) { + if (ommerHeader.getNumber() - header.getNumber() > MAX_GENERATION) { + LOG.warn( + "Block processing error: ommer block number {} more than {} generations current block number {}", + ommerHeader.getNumber(), + MAX_GENERATION, + header.getNumber()); + return false; + } + + final MutableAccount ommerCoinbase = + updater.getOrCreate(ommerHeader.getCoinbase()).getMutable(); + final long distance = header.getNumber() - ommerHeader.getNumber(); + final Wei ommerReward = blockReward.subtract(blockReward.multiply(distance).divide(8)); + ommerCoinbase.incrementBalance(ommerReward); + } + + return true; + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java new file mode 100644 index 00000000000..5f25246f8b8 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigration.java @@ -0,0 +1,176 @@ +/* + * 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.privacy.storage.migration; + +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_4_0; + +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; +import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tuweni.bytes.Bytes32; + +public class PrivateStorageMigration { + + private static final Logger LOG = LogManager.getLogger(); + + private final Blockchain blockchain; + private final Address privacyPrecompileAddress; + private final ProtocolSchedule protocolSchedule; + private final WorldStateArchive publicWorldStateArchive; + private final PrivateStateStorage privateStateStorage; + private final PrivateStateRootResolver privateStateRootResolver; + private final LegacyPrivateStateStorage legacyPrivateStateStorage; + private final Function, PrivateMigrationBlockProcessor> + privateMigrationBlockProcessorBuilder; + + public PrivateStorageMigration( + final Blockchain blockchain, + final Address privacyPrecompileAddress, + final ProtocolSchedule protocolSchedule, + final WorldStateArchive publicWorldStateArchive, + final PrivateStateStorage privateStateStorage, + final PrivateStateRootResolver privateStateRootResolver, + final LegacyPrivateStateStorage legacyPrivateStateStorage, + final Function, PrivateMigrationBlockProcessor> + privateMigrationBlockProcessorBuilder) { + this.privateStateStorage = privateStateStorage; + this.blockchain = blockchain; + this.privacyPrecompileAddress = privacyPrecompileAddress; + this.protocolSchedule = protocolSchedule; + this.publicWorldStateArchive = publicWorldStateArchive; + this.privateStateRootResolver = privateStateRootResolver; + this.legacyPrivateStateStorage = legacyPrivateStateStorage; + this.privateMigrationBlockProcessorBuilder = privateMigrationBlockProcessorBuilder; + } + + public void migratePrivateStorage() { + final long migrationStartTimestamp = System.currentTimeMillis(); + final long chainHeadBlockNumber = blockchain.getChainHeadBlockNumber(); + + LOG.info("Migrating private storage database..."); + + for (int blockNumber = 0; blockNumber <= chainHeadBlockNumber; blockNumber++) { + final Block block = + blockchain + .getBlockByNumber(blockNumber) + .orElseThrow(PrivateStorageMigrationException::new); + final Hash blockHash = block.getHash(); + final BlockHeader blockHeader = block.getHeader(); + LOG.info("Processing block {} ({}/{})", blockHash, blockNumber, chainHeadBlockNumber); + + createPrivacyGroupHeadBlockMap(blockHeader); + + final int lastPmtIndex = findLastPMTIndexInBlock(block); + if (lastPmtIndex >= 0) { + final ProtocolSpec protocolSpec = protocolSchedule.getByBlockNumber(blockNumber); + final PrivateMigrationBlockProcessor privateMigrationBlockProcessor = + privateMigrationBlockProcessorBuilder.apply(protocolSpec); + + final MutableWorldState publicWorldState = + blockchain + .getBlockHeader(blockHeader.getParentHash()) + .map(BlockHeader::getStateRoot) + .flatMap(publicWorldStateArchive::getMutable) + .orElseThrow(PrivateStorageMigrationException::new); + + final List transactionsToProcess = + block.getBody().getTransactions().subList(0, lastPmtIndex + 1); + final List ommers = block.getBody().getOmmers(); + + privateMigrationBlockProcessor.processBlock( + blockchain, publicWorldState, blockHeader, transactionsToProcess, ommers); + } + } + + if (isResultingPrivateStateRootAtHeadValid()) { + privateStateStorage.updater().putDatabaseVersion(SCHEMA_VERSION_1_4_0).commit(); + } else { + throw new PrivateStorageMigrationException("Inconsistent state root. Please re-sync."); + } + + final long migrationDuration = System.currentTimeMillis() - migrationStartTimestamp; + LOG.info("Migration took {} seconds", migrationDuration / 1000.0); + } + + /* + Returns the index of the last PMT in the block, or -1 if there are no PMTs in the block. + */ + private int findLastPMTIndexInBlock(final Block block) { + final List txs = block.getBody().getTransactions(); + int lastPmtIndex = -1; + for (int i = 0; i < txs.size(); i++) { + if (isPrivacyMarkerTransaction(txs.get(i))) { + lastPmtIndex = i; + } + } + return lastPmtIndex; + } + + private boolean isPrivacyMarkerTransaction(final Transaction tx) { + return tx.getTo().isPresent() && tx.getTo().get().equals(privacyPrecompileAddress); + } + + private boolean isResultingPrivateStateRootAtHeadValid() { + final Optional privacyGroupHeadBlockMap = + privateStateStorage.getPrivacyGroupHeadBlockMap(blockchain.getChainHeadHash()); + final Set privacyGroupIds = + privacyGroupHeadBlockMap.orElseThrow(PrivateStorageMigrationException::new).keySet(); + + privacyGroupIds.forEach( + pgId -> { + final Optional legacyStateRoot = legacyPrivateStateStorage.getLatestStateRoot(pgId); + final Hash newStateRoot = + privateStateRootResolver.resolveLastStateRoot(pgId, blockchain.getChainHeadHash()); + if (!newStateRoot.equals(legacyStateRoot.orElse(Hash.EMPTY))) { + throw new PrivateStorageMigrationException( + "Inconsistent state root. Please delete your database and re-sync your node to avoid inconsistencies in your database."); + } + }); + + return true; + } + + private void createPrivacyGroupHeadBlockMap(final BlockHeader blockHeader) { + final PrivacyGroupHeadBlockMap privacyGroupHeadBlockHash = + new PrivacyGroupHeadBlockMap( + privateStateStorage + .getPrivacyGroupHeadBlockMap(blockHeader.getParentHash()) + .orElse(PrivacyGroupHeadBlockMap.EMPTY)); + + privateStateStorage + .updater() + .putPrivacyGroupHeadBlockMap(blockHeader.getHash(), privacyGroupHeadBlockHash) + .commit(); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationException.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationException.java new file mode 100644 index 00000000000..0175bb6a753 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationException.java @@ -0,0 +1,33 @@ +/* + * 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.privacy.storage.migration; + +public class PrivateStorageMigrationException extends RuntimeException { + + private static final String MIGRATION_ERROR_MSG = + "Unexpected error during private database migration. Please re-sync your node to avoid data corruption."; + + public PrivateStorageMigrationException(final String message) { + super(message); + } + + public PrivateStorageMigrationException() { + super(MIGRATION_ERROR_MSG); + } + + public PrivateStorageMigrationException(final Throwable th) { + super(MIGRATION_ERROR_MSG, th); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationService.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationService.java new file mode 100644 index 00000000000..9b2c7c43653 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationService.java @@ -0,0 +1,78 @@ +/* + * 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.privacy.storage.migration; + +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_0_0; +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_4_0; + +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; + +import java.util.function.Supplier; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class PrivateStorageMigrationService { + + private static final Logger LOG = LogManager.getLogger(); + + private final PrivateStateStorage privateStateStorage; + private final boolean migrationFlag; + private final Supplier migrationBuilder; + + public PrivateStorageMigrationService( + final PrivateStateStorage privateStateStorage, + final boolean migrationFlag, + final Supplier migrationBuilder) { + this.privateStateStorage = privateStateStorage; + this.migrationFlag = migrationFlag; + this.migrationBuilder = migrationBuilder; + } + + /** + * Migration only happens if the system detects that the private schema version is lower than + * version 2 (1.4.x), and the user has set the` privacy-enable-database-migration` option. + */ + public void runMigrationIfRequired() { + final int schemaVersion = privateStateStorage.getSchemaVersion(); + + if (schemaVersion >= SCHEMA_VERSION_1_4_0) { + LOG.debug("Private database metadata does not require migration."); + return; + } + + /* + If this is a new database, we need to set the version and no migration is required + */ + if (privateStateStorage.isEmpty()) { + privateStateStorage.updater().putDatabaseVersion(SCHEMA_VERSION_1_4_0).commit(); + LOG.debug("Private database metadata does not require migration."); + return; + } + + if (schemaVersion == SCHEMA_VERSION_1_0_0 && !migrationFlag) { + final String message = + "Private database metadata requires migration. For more information check the 1.4 changelog."; + LOG.warn(message); + throw new PrivateStorageMigrationException(message); + } + + if (schemaVersion == SCHEMA_VERSION_1_0_0 && migrationFlag) { + LOG.info( + "Private database metadata requires migration and `privacy-enable-database-migration` was set. Starting migration!"); + migrationBuilder.get().migratePrivateStorage(); + } + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java index 411f7294b79..f5e80cb7c73 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Address; import org.hyperledger.besu.ethereum.core.Gas; +import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.Log; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import org.hyperledger.besu.ethereum.core.Transaction; @@ -229,6 +230,9 @@ public enum Type { private final Boolean isPersistingState; private Optional revertReason; + // Privacy Execution Environment fields. + private final Hash transactionHash; + // Miscellaneous fields. private final EnumSet exceptionalHaltReasons = EnumSet.noneOf(ExceptionalHaltReason.class); @@ -264,6 +268,7 @@ private MessageFrame( final Address miningBeneficiary, final BlockHashLookup blockHashLookup, final Boolean isPersistingState, + final Hash transactionHash, final Optional revertReason, final int maxStackSize) { this.type = type; @@ -299,6 +304,7 @@ private MessageFrame( this.completer = completer; this.miningBeneficiary = miningBeneficiary; this.isPersistingState = isPersistingState; + this.transactionHash = transactionHash; this.revertReason = revertReason; } @@ -960,6 +966,15 @@ public Boolean isPersistingState() { return isPersistingState; } + /** + * Returns the transaction hash of the transaction being processed + * + * @return the transaction hash of the transaction being processed + */ + public Hash getTransactionHash() { + return transactionHash; + } + public void setCurrentOperation(final Operation currentOperation) { this.currentOperation = currentOperation; } @@ -1006,6 +1021,7 @@ public static class Builder { private Address miningBeneficiary; private BlockHashLookup blockHashLookup; private Boolean isPersistingState = false; + private Hash transactionHash; private Optional reason = Optional.empty(); public Builder type(final Type type) { @@ -1124,6 +1140,11 @@ public Builder isPersistingState(final Boolean isPersistingState) { return this; } + public Builder transactionHash(final Hash transactionHash) { + this.transactionHash = transactionHash; + return this; + } + public Builder reason(final Bytes reason) { this.reason = Optional.ofNullable(reason); return this; @@ -1179,6 +1200,7 @@ public MessageFrame build() { miningBeneficiary, blockHashLookup, isPersistingState, + transactionHash, reason, maxStackSize); } diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java index 13f416c366b..5f8d7a1be69 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.crypto.SECP256K1; import org.hyperledger.besu.crypto.SecureRandomProvider; +import org.hyperledger.besu.ethereum.mainnet.BodyValidation; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; @@ -226,9 +227,9 @@ public Block genesisBlock(final BlockOptions options) { public Block block(final BlockOptions options) { final long blockNumber = options.getBlockNumber(positiveLong()); - final BlockHeader header = header(blockNumber, options); final BlockBody body = blockNumber == BlockHeader.GENESIS_BLOCK_NUMBER ? BlockBody.empty() : body(options); + final BlockHeader header = header(blockNumber, body, options); return new Block(header, body); } @@ -248,30 +249,34 @@ public Block nextBlock(final Block afterBlock) { return block(options); } + public BlockHeader header(final long blockNumber, final BlockBody blockBody) { + return header(blockNumber, blockBody, new BlockOptions()); + } + public BlockHeader header(final long blockNumber) { - return header(blockNumber, blockOptionsSupplier.get()); + return header(blockNumber, body(), blockOptionsSupplier.get()); } public BlockHeader header() { - return header(positiveLong(), blockOptionsSupplier.get()); + return header(positiveLong(), body(), blockOptionsSupplier.get()); } - public BlockHeader header(final long number, final BlockOptions options) { + public BlockHeader header(final long number, final BlockBody body, final BlockOptions options) { final int gasLimit = random.nextInt() & Integer.MAX_VALUE; final int gasUsed = Math.max(0, gasLimit - 1); final long blockNonce = random.nextLong(); return BlockHeaderBuilder.create() .parentHash(options.getParentHash(hash())) - .ommersHash(hash()) + .ommersHash(BodyValidation.ommersHash(body.getOmmers())) .coinbase(address()) .stateRoot(options.getStateRoot(hash())) - .transactionsRoot(hash()) - .receiptsRoot(hash()) - .logsBloom(logsBloom()) + .transactionsRoot(BodyValidation.transactionsRoot(body.getTransactions())) + .receiptsRoot(options.getReceiptsRoot(hash())) + .logsBloom(options.getLogsBloom(logsBloom())) .difficulty(options.getDifficulty(Difficulty.of(uint256(4)))) .number(number) .gasLimit(gasLimit) - .gasUsed(gasUsed) + .gasUsed(options.getGasUsed(gasUsed)) .timestamp(Instant.now().truncatedTo(ChronoUnit.SECONDS).getEpochSecond()) .extraData(options.getExtraData(bytes32())) .mixHash(hash()) @@ -286,37 +291,41 @@ public BlockBody body() { public BlockBody body(final BlockOptions options) { final List ommers = new ArrayList<>(); - final int ommerCount = random.nextInt(3); - for (int i = 0; i < ommerCount; i++) { - ommers.add(header()); + if (options.hasOmmers()) { + final int ommerCount = random.nextInt(3); + for (int i = 0; i < ommerCount; i++) { + ommers.add(ommer()); + } } final List defaultTxs = new ArrayList<>(); - defaultTxs.add(transaction()); - defaultTxs.add(transaction()); + if (options.hasTransactions()) { + defaultTxs.add(transaction()); + defaultTxs.add(transaction()); + } return new BlockBody(options.getTransactions(defaultTxs), ommers); } - public Transaction transaction(final Bytes payload) { - return Transaction.builder() - .nonce(positiveLong()) - .gasPrice(Wei.wrap(bytes32())) - .gasLimit(positiveLong()) - .to(address()) - .value(Wei.wrap(bytes32())) - .payload(payload) - .chainId(BigInteger.ONE) - .signAndBuild(generateKeyPair()); + private BlockHeader ommer() { + return header(positiveLong(), body(BlockOptions.create().hasOmmers(false))); } public Transaction transaction() { + return transaction(bytes32()); + } + + public Transaction transaction(final Bytes payload) { + return transaction(payload, address()); + } + + public Transaction transaction(final Bytes payload, final Address to) { return Transaction.builder() .nonce(positiveLong()) .gasPrice(Wei.wrap(bytes32())) .gasLimit(positiveLong()) - .to(address()) + .to(to) .value(Wei.wrap(bytes32())) - .payload(bytes32()) + .payload(payload) .chainId(BigInteger.ONE) .signAndBuild(generateKeyPair()); } @@ -496,8 +505,14 @@ public static class BlockOptions { private Optional stateRoot = Optional.empty(); private Optional difficulty = Optional.empty(); private final List transactions = new ArrayList<>(); + private final List ommers = new ArrayList<>(); private Optional extraData = Optional.empty(); private Optional blockHeaderFunctions = Optional.empty(); + private Optional receiptsRoot = Optional.empty(); + private Optional gasUsed = Optional.empty(); + private Optional logsBloom = Optional.empty(); + private boolean hasOmmers = true; + private boolean hasTransactions = true; public static BlockOptions create() { return new BlockOptions(); @@ -507,6 +522,10 @@ public List getTransactions(final List defaultValue) { return transactions.isEmpty() ? defaultValue : transactions; } + public List getOmmers(final List defaultValue) { + return ommers.isEmpty() ? defaultValue : ommers; + } + public long getBlockNumber(final long defaultValue) { return blockNumber.orElse(defaultValue); } @@ -531,11 +550,36 @@ public BlockHeaderFunctions getBlockHeaderFunctions(final BlockHeaderFunctions d return blockHeaderFunctions.orElse(defaultValue); } + public Hash getReceiptsRoot(final Hash defaultValue) { + return receiptsRoot.orElse(defaultValue); + } + + public long getGasUsed(final long defaultValue) { + return gasUsed.orElse(defaultValue); + } + + public LogsBloomFilter getLogsBloom(final LogsBloomFilter defaultValue) { + return logsBloom.orElse(defaultValue); + } + + public boolean hasTransactions() { + return hasTransactions; + } + + public boolean hasOmmers() { + return hasOmmers; + } + public BlockOptions addTransaction(final Transaction... tx) { transactions.addAll(Arrays.asList(tx)); return this; } + public BlockOptions addOmmers(final BlockHeader... headers) { + ommers.addAll(Arrays.asList(headers)); + return this; + } + public BlockOptions addTransaction(final Collection txs) { return addTransaction(txs.toArray(new Transaction[] {})); } @@ -569,5 +613,30 @@ public BlockOptions setBlockHeaderFunctions(final BlockHeaderFunctions blockHead this.blockHeaderFunctions = Optional.of(blockHeaderFunctions); return this; } + + public BlockOptions setReceiptsRoot(final Hash receiptsRoot) { + this.receiptsRoot = Optional.of(receiptsRoot); + return this; + } + + public BlockOptions setGasUsed(final long gasUsed) { + this.gasUsed = Optional.of(gasUsed); + return this; + } + + public BlockOptions setLogsBloom(final LogsBloomFilter logsBloom) { + this.logsBloom = Optional.of(logsBloom); + return this; + } + + public BlockOptions hasTransactions(final boolean hasTransactions) { + this.hasTransactions = hasTransactions; + return this; + } + + public BlockOptions hasOmmers(final boolean hasOmmers) { + this.hasOmmers = hasOmmers; + return this; + } } } diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java index cf7bec9b6a3..cf7c2209ba5 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryPrivacyStorageProvider.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.core; +import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateKeyValueStorage; +import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateStorage; import org.hyperledger.besu.ethereum.privacy.storage.PrivacyStorageProvider; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; @@ -54,6 +56,11 @@ public PrivateStateStorage createPrivateStateStorage() { return new PrivateStateKeyValueStorage(new InMemoryKeyValueStorage()); } + @Override + public LegacyPrivateStateStorage createLegacyPrivateStateStorage() { + return new LegacyPrivateStateKeyValueStorage(new InMemoryKeyValueStorage()); + } + @Override public int getFactoryVersion() { return 1; diff --git a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java index 525ea1e4e27..5c8b271f677 100644 --- a/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java +++ b/ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/InMemoryStorageProvider.java @@ -20,6 +20,8 @@ import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.storage.StorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage; import org.hyperledger.besu.ethereum.storage.keyvalue.WorldStateKeyValueStorage; @@ -59,6 +61,10 @@ public static MutableWorldState createInMemoryWorldState() { provider.createWorldStateStorage(), provider.createWorldStatePreimageStorage()); } + public static PrivateStateStorage createInMemoryPrivateStateStorage() { + return new PrivateStateKeyValueStorage(new InMemoryKeyValueStorage()); + } + @Override public BlockchainStorage createBlockchainStorage(final ProtocolSchedule protocolSchedule) { return new KeyValueStoragePrefixedKeyBlockchainStorage( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java new file mode 100644 index 00000000000..8d09c9d6d6f --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessorTest.java @@ -0,0 +1,77 @@ +/* + * 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.mainnet; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; +import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; + +import java.util.Collections; + +import org.apache.tuweni.bytes.Bytes32; +import org.junit.Before; +import org.junit.Test; + +public class PrivacyBlockProcessorTest { + + private PrivacyBlockProcessor privacyBlockProcessor; + private PrivateStateStorage privateStateStorage; + private AbstractBlockProcessor blockProcessor; + + @Before + public void setUp() { + blockProcessor = mock(AbstractBlockProcessor.class); + privateStateStorage = new PrivateStateKeyValueStorage(new InMemoryKeyValueStorage()); + this.privacyBlockProcessor = new PrivacyBlockProcessor(blockProcessor, privateStateStorage); + } + + @Test + public void mustCopyPreviousPrivacyGroupBlockHeadMap() { + final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); + final Blockchain blockchain = mock(Blockchain.class); + final MutableWorldState mutableWorldState = mock(MutableWorldState.class); + final PrivacyGroupHeadBlockMap expected = + new PrivacyGroupHeadBlockMap(Collections.singletonMap(Bytes32.ZERO, Hash.EMPTY)); + final Block firstBlock = blockDataGenerator.block(); + final Block secondBlock = + blockDataGenerator.block( + BlockDataGenerator.BlockOptions.create().setParentHash(firstBlock.getHash())); + privacyBlockProcessor.processBlock(blockchain, mutableWorldState, firstBlock); + privateStateStorage + .updater() + .putPrivacyGroupHeadBlockMap(firstBlock.getHash(), expected) + .commit(); + privacyBlockProcessor.processBlock(blockchain, mutableWorldState, secondBlock); + assertThat(privateStateStorage.getPrivacyGroupHeadBlockMap(secondBlock.getHash())) + .contains(expected); + verify(blockProcessor) + .processBlock( + blockchain, + mutableWorldState, + firstBlock.getHeader(), + firstBlock.getBody().getTransactions(), + firstBlock.getBody().getOmmers()); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java index f841cb634ff..1acd06d3793 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java @@ -26,6 +26,8 @@ import org.hyperledger.besu.enclave.types.ReceiveResponse; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; import org.hyperledger.besu.ethereum.core.Log; import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; @@ -33,6 +35,7 @@ import org.hyperledger.besu.ethereum.mainnet.SpuriousDragonGasCalculator; import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.vm.BlockHashLookup; import org.hyperledger.besu.ethereum.vm.MessageFrame; @@ -57,6 +60,7 @@ public class PrivacyPrecompiledContractTest { private final String actual = "Test String"; private final Bytes key = Bytes.wrap(actual.getBytes(UTF_8)); private MessageFrame messageFrame; + private Blockchain blockchain; private final String DEFAULT_OUTPUT = "0x01"; private final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class); final PrivateStateStorage privateStateStorage = mock(PrivateStateStorage.class); @@ -107,27 +111,46 @@ public void setUp() { when(worldStateArchive.getMutable(any())).thenReturn(Optional.of(mutableWorldState)); final PrivateStateStorage.Updater storageUpdater = mock(PrivateStateStorage.Updater.class); - when(storageUpdater.putLatestStateRoot(nullable(Bytes32.class), any())) + when(privateStateStorage.getPrivacyGroupHeadBlockMap(any())) + .thenReturn(Optional.of(PrivacyGroupHeadBlockMap.EMPTY)); + when(privateStateStorage.getPrivateBlockMetadata(any(), any())).thenReturn(Optional.empty()); + when(storageUpdater.putPrivateBlockMetadata( + nullable(Bytes32.class), nullable(Bytes32.class), any())) .thenReturn(storageUpdater); - when(storageUpdater.putTransactionLogs(nullable(Bytes32.class), any())) + when(storageUpdater.putPrivacyGroupHeadBlockMap(nullable(Bytes32.class), any())) .thenReturn(storageUpdater); - when(storageUpdater.putTransactionResult(nullable(Bytes32.class), any())) + when(storageUpdater.putTransactionReceipt( + nullable(Bytes32.class), nullable(Bytes32.class), any())) .thenReturn(storageUpdater); when(privateStateStorage.updater()).thenReturn(storageUpdater); messageFrame = mock(MessageFrame.class); + blockchain = mock(Blockchain.class); + final BlockDataGenerator blockGenerator = new BlockDataGenerator(); + final Block genesis = blockGenerator.genesisBlock(); + final Block block = + blockGenerator.block( + new BlockDataGenerator.BlockOptions().setParentHash(genesis.getHeader().getHash())); + when(blockchain.getGenesisBlock()).thenReturn(genesis); + when(blockchain.getBlockByHash(block.getHash())).thenReturn(Optional.of(block)); + when(blockchain.getBlockByHash(genesis.getHash())).thenReturn(Optional.of(genesis)); + when(messageFrame.getBlockchain()).thenReturn(blockchain); + when(messageFrame.getBlockHeader()).thenReturn(block.getHeader()); } @Test - public void testPayloadFoundInEnaclave() { - Enclave enclave = mock(Enclave.class); - PrivacyPrecompiledContract contract = + public void testPayloadFoundInEnclave() { + final Enclave enclave = mock(Enclave.class); + final PrivacyPrecompiledContract contract = new PrivacyPrecompiledContract( new SpuriousDragonGasCalculator(), enclave, worldStateArchive, privateStateStorage); contract.setPrivateTransactionProcessor(mockPrivateTxProcessor()); final ReceiveResponse response = - new ReceiveResponse(VALID_PRIVATE_TRANSACTION_RLP_BASE64, "", null); + new ReceiveResponse( + VALID_PRIVATE_TRANSACTION_RLP_BASE64, + "8lDVI66RZHIrBsolz6Kn88Rd+WsJ4hUjb4hsh29xW/o=", + null); when(enclave.receive(any(String.class))).thenReturn(response); final Bytes actual = contract.compute(key, messageFrame); @@ -137,9 +160,9 @@ public void testPayloadFoundInEnaclave() { @Test public void testPayloadNotFoundInEnclave() { - Enclave enclave = mock(Enclave.class); + final Enclave enclave = mock(Enclave.class); - PrivacyPrecompiledContract contract = + final PrivacyPrecompiledContract contract = new PrivacyPrecompiledContract( new SpuriousDragonGasCalculator(), enclave, worldStateArchive, privateStateStorage); @@ -151,9 +174,9 @@ public void testPayloadNotFoundInEnclave() { @Test(expected = RuntimeException.class) public void testEnclaveDown() { - Enclave enclave = mock(Enclave.class); + final Enclave enclave = mock(Enclave.class); - PrivacyPrecompiledContract contract = + final PrivacyPrecompiledContract contract = new PrivacyPrecompiledContract( new SpuriousDragonGasCalculator(), enclave, worldStateArchive, privateStateStorage); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/ChainHeadPrivateNonceProviderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/ChainHeadPrivateNonceProviderTest.java new file mode 100644 index 00000000000..fb578cb5eab --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/ChainHeadPrivateNonceProviderTest.java @@ -0,0 +1,102 @@ +/* + * 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.privacy; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Account; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.WorldState; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.Before; +import org.junit.Test; + +public class ChainHeadPrivateNonceProviderTest { + private static final Bytes32 PRIVACY_GROUP_ID = + Bytes32.wrap(Bytes.fromBase64String("DyAOiF/ynpc+JXa2YAGB0bCitSlOMNm+ShmB/7M6C4w=")); + private static final Address ADDRESS = Address.fromHexString("55");; + + private Account account; + private WorldState worldState; + private ChainHeadPrivateNonceProvider privateNonceProvider; + private WorldStateArchive privateWorldStateArchive; + private PrivateStateRootResolver privateStateRootResolver; + + @Before + public void setUp() { + final BlockDataGenerator gen = new BlockDataGenerator(); + + final Blockchain blockchain = mock(Blockchain.class); + when(blockchain.getChainHeadHeader()).thenReturn(gen.header()); + + account = mock(Account.class); + worldState = mock(WorldState.class); + + privateStateRootResolver = mock(PrivateStateRootResolver.class); + privateWorldStateArchive = mock(WorldStateArchive.class); + privateNonceProvider = + new ChainHeadPrivateNonceProvider( + blockchain, privateStateRootResolver, privateWorldStateArchive); + } + + @Test + public void determineNonceForPrivacyGroupRequestWhenPrivateStateDoesNotExist() { + when(privateStateRootResolver.resolveLastStateRoot(any(Bytes32.class), any(Hash.class))) + .thenReturn(Hash.ZERO); + when(privateWorldStateArchive.get(any(Hash.class))).thenReturn(Optional.empty()); + + final long nonce = privateNonceProvider.getNonce(ADDRESS, PRIVACY_GROUP_ID); + + assertThat(nonce).isEqualTo(Account.DEFAULT_NONCE); + } + + @Test + public void determineNonceForPrivacyGroupRequestWhenAccountExists() { + when(account.getNonce()).thenReturn(4L); + when(worldState.get(any(Address.class))).thenReturn(account); + when(privateStateRootResolver.resolveLastStateRoot(any(Bytes32.class), any(Hash.class))) + .thenReturn(Hash.ZERO); + when(privateWorldStateArchive.get(any(Hash.class))).thenReturn(Optional.of(worldState)); + + final long nonce = privateNonceProvider.getNonce(ADDRESS, PRIVACY_GROUP_ID); + + assertThat(nonce).isEqualTo(4L); + } + + @Test + public void determineNonceForPrivacyGroupRequestWhenAccountDoesNotExist() { + when(privateStateRootResolver.resolveLastStateRoot(any(Bytes32.class), any(Hash.class))) + .thenReturn(Hash.ZERO); + when(privateWorldStateArchive.get(any(Hash.class))).thenReturn(Optional.of(worldState)); + when(account.getNonce()).thenReturn(4L); + + final long nonce = privateNonceProvider.getNonce(ADDRESS, PRIVACY_GROUP_ID); + + assertThat(nonce).isEqualTo(Account.DEFAULT_NONCE); + verifyNoInteractions(account); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyControllerTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyControllerTest.java index 6c3a7e54aae..3522edc7dd8 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyControllerTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/DefaultPrivacyControllerTest.java @@ -27,7 +27,6 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import org.hyperledger.besu.crypto.SECP256K1; @@ -38,19 +37,14 @@ import org.hyperledger.besu.enclave.types.PrivacyGroup.Type; import org.hyperledger.besu.enclave.types.ReceiveResponse; import org.hyperledger.besu.enclave.types.SendResponse; -import org.hyperledger.besu.ethereum.core.Account; import org.hyperledger.besu.ethereum.core.Address; -import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.core.Log; -import org.hyperledger.besu.ethereum.core.MutableWorldState; import org.hyperledger.besu.ethereum.core.Transaction; import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.mainnet.TransactionValidator.TransactionInvalidReason; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.privacy.markertransaction.FixedKeySigningPrivateMarkerTransactionFactory; -import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; import org.hyperledger.besu.ethereum.transaction.CallParameter; -import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.orion.testutil.OrionKeyUtils; import java.math.BigInteger; @@ -59,6 +53,7 @@ import java.util.Optional; import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.io.Base64; import org.junit.Before; import org.junit.Test; @@ -87,12 +82,8 @@ public class DefaultPrivacyControllerTest { private PrivacyController brokenPrivacyController; private PrivateTransactionValidator privateTransactionValidator; private Enclave enclave; - private Account account; private String enclavePublicKey; - private PrivateStateStorage privateStateStorage; - private WorldStateArchive worldStateArchive; - private MutableWorldState mutableWorldState; - private final Hash hash = mock(Hash.class); + private PrivateNonceProvider privateNonceProvider; private static final Transaction PUBLIC_TRANSACTION = Transaction.builder() @@ -109,7 +100,8 @@ public class DefaultPrivacyControllerTest { private Enclave mockEnclave() { final Enclave mockEnclave = mock(Enclave.class); final SendResponse response = new SendResponse(TRANSACTION_KEY); - final ReceiveResponse receiveResponse = new ReceiveResponse(new byte[0], "mock", null); + final ReceiveResponse receiveResponse = + new ReceiveResponse(new byte[0], PRIVACY_GROUP_ID, null); when(mockEnclave.send(anyString(), anyString(), anyList())).thenReturn(response); when(mockEnclave.send(anyString(), anyString(), anyString())).thenReturn(response); when(mockEnclave.receive(any(), any())).thenReturn(receiveResponse); @@ -141,14 +133,8 @@ private PrivateTransactionSimulator mockPrivateTransactionSimulator() { @Before public void setUp() throws Exception { - privateStateStorage = mock(PrivateStateStorage.class); - when(privateStateStorage.getLatestStateRoot(any(Bytes.class))).thenReturn(Optional.of(hash)); - worldStateArchive = mock(WorldStateArchive.class); - account = mock(Account.class); - when(account.getNonce()).thenReturn(1L); - mutableWorldState = mock(MutableWorldState.class); - when(worldStateArchive.getMutable(any(Hash.class))).thenReturn(Optional.of(mutableWorldState)); - when(mutableWorldState.get(any(Address.class))).thenReturn(account); + privateNonceProvider = mock(ChainHeadPrivateNonceProvider.class); + when(privateNonceProvider.getNonce(any(), any())).thenReturn(1L); enclavePublicKey = OrionKeyUtils.loadKey("orion_key_0.pub"); privateTransactionValidator = mockPrivateTransactionValidator(); @@ -159,26 +145,23 @@ public void setUp() throws Exception { privacyController = new DefaultPrivacyController( enclave, - privateStateStorage, - worldStateArchive, privateTransactionValidator, new FixedKeySigningPrivateMarkerTransactionFactory( Address.DEFAULT_PRIVACY, (address) -> 0, KEY_PAIR), - privateTransactionSimulator); + privateTransactionSimulator, + privateNonceProvider); brokenPrivacyController = new DefaultPrivacyController( brokenMockEnclave(), - privateStateStorage, - worldStateArchive, privateTransactionValidator, new FixedKeySigningPrivateMarkerTransactionFactory( Address.DEFAULT_PRIVACY, (address) -> 0, KEY_PAIR), - privateTransactionSimulator); + privateTransactionSimulator, + privateNonceProvider); } @Test public void sendsValidLegacyTransaction() { - final PrivateTransaction transaction = buildLegacyPrivateTransaction(1); final String enclaveKey = privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY); @@ -201,7 +184,6 @@ public void sendsValidLegacyTransaction() { @Test public void sendValidBesuTransaction() { - final PrivateTransaction transaction = buildBesuPrivateTransaction(1); final String enclaveKey = privacyController.sendTransaction(transaction, ENCLAVE_PUBLIC_KEY); @@ -329,20 +311,21 @@ public void determinesNonceForEeaRequest() { final long reportedNonce = 8L; final PrivacyGroup[] returnedGroups = new PrivacyGroup[] { - new PrivacyGroup("Group1", Type.LEGACY, "Group1_Name", "Group1_Desc", emptyList()), + new PrivacyGroup( + PRIVACY_GROUP_ID, Type.LEGACY, "Group1_Name", "Group1_Desc", emptyList()), }; when(enclave.findPrivacyGroup(any())).thenReturn(returnedGroups); - when(account.getNonce()).thenReturn(8L); + when(privateNonceProvider.getNonce(any(Address.class), any(Bytes32.class))).thenReturn(8L); final long nonce = privacyController.determineEeaNonce( - "privateFrom", new String[] {"first", "second"}, address, ENCLAVE_PUBLIC_KEY); + ENCLAVE_PUBLIC_KEY, new String[] {ENCLAVE_KEY2}, address, ENCLAVE_PUBLIC_KEY); assertThat(nonce).isEqualTo(reportedNonce); verify(enclave) .findPrivacyGroup( - argThat((m) -> m.containsAll(newArrayList("first", "second", "privateFrom")))); + argThat((m) -> m.containsAll(newArrayList(ENCLAVE_PUBLIC_KEY, ENCLAVE_KEY2)))); } @Test @@ -381,62 +364,6 @@ public void determineNonceForEeaRequestWithMoreThanOneMatchingGroupThrowsExcepti "privateFrom", new String[] {"first", "second"}, address, ENCLAVE_PUBLIC_KEY)); } - @Test - public void determineNonceForPrivacyGroupRequestWhenAccountExists() { - final Address address = Address.fromHexString("55"); - - when(account.getNonce()).thenReturn(4L); - - final long nonce = privacyController.determineBesuNonce(address, "Group1", ENCLAVE_PUBLIC_KEY); - - assertThat(nonce).isEqualTo(4L); - verify(privateStateStorage).getLatestStateRoot(Base64.decode("Group1")); - verify(worldStateArchive).getMutable(hash); - verify(mutableWorldState).get(address); - } - - @Test - public void determineNonceForPrivacyGroupRequestWhenPrivateStateDoesNotExist() { - final Address address = Address.fromHexString("55"); - - when(privateStateStorage.getLatestStateRoot(Base64.decode("Group1"))) - .thenReturn(Optional.empty()); - - final long nonce = privacyController.determineBesuNonce(address, "Group1", ENCLAVE_PUBLIC_KEY); - - assertThat(nonce).isEqualTo(Account.DEFAULT_NONCE); - verifyNoInteractions(worldStateArchive, mutableWorldState, account); - } - - @Test - public void determineNonceForPrivacyGroupRequestWhenWorldStateDoesNotExist() { - final Address address = Address.fromHexString("55"); - - when(privateStateStorage.getLatestStateRoot(Base64.decode("Group1"))) - .thenReturn(Optional.of(hash)); - when(worldStateArchive.getMutable(hash)).thenReturn(Optional.empty()); - - final long nonce = privacyController.determineBesuNonce(address, "Group1", ENCLAVE_PUBLIC_KEY); - - assertThat(nonce).isEqualTo(Account.DEFAULT_NONCE); - verifyNoInteractions(mutableWorldState, account); - } - - @Test - public void determineNonceForPrivacyGroupRequestWhenAccountDoesNotExist() { - final Address address = Address.fromHexString("55"); - - when(privateStateStorage.getLatestStateRoot(Base64.decode("Group1"))) - .thenReturn(Optional.of(hash)); - when(worldStateArchive.getMutable(hash)).thenReturn(Optional.of(mutableWorldState)); - when(mutableWorldState.get(address)).thenReturn(null); - - final long nonce = privacyController.determineBesuNonce(address, "Group1", ENCLAVE_PUBLIC_KEY); - - assertThat(nonce).isEqualTo(Account.DEFAULT_NONCE); - verifyNoInteractions(account); - } - @Test public void simulatingPrivateTransactionWorks() { final CallParameter callParameter = mock(CallParameter.class); @@ -460,8 +387,8 @@ private static PrivateTransaction buildLegacyPrivateTransaction(final long nonce private static PrivateTransaction buildBesuPrivateTransaction(final long nonce) { return buildPrivateTransaction(nonce) - .privateFrom(Base64.decode(ENCLAVE_PUBLIC_KEY)) - .privacyGroupId(Base64.decode(PRIVACY_GROUP_ID)) + .privateFrom(Bytes.fromBase64String(ENCLAVE_PUBLIC_KEY)) + .privacyGroupId(Bytes.fromBase64String(PRIVACY_GROUP_ID)) .signAndBuild(KEY_PAIR); } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRootResolverTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRootResolverTest.java new file mode 100644 index 00000000000..ac0e583e33b --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/PrivateStateRootResolverTest.java @@ -0,0 +1,180 @@ +/* + * 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.privacy; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.InMemoryStorageProvider; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateBlockMetadata; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateTransactionMetadata; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +public class PrivateStateRootResolverTest { + + private static final BlockDataGenerator BLOCK_GENERATOR = new BlockDataGenerator(); + private static MutableBlockchain BLOCKCHAIN; + + private static final Hash pmt1StateHash = + Hash.fromHexString("0x37659019840d6e04e740614d1ad93d62f0d9d7cc423b2178189f391db602a6a6"); + private static final Hash pmt2StateHash = + Hash.fromHexString("0x12d390c87b405e91523b5829002bf90095005366eb9aa168ff8a18540902e410"); + private static final Bytes32 privacyGroupId = + Bytes32.wrap(Bytes.fromBase64String("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")); + private static final Bytes32 failingPrivacyGroupId = + Bytes32.wrap(Bytes.fromBase64String("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=")); + + private PrivateStateStorage privateStateStorage; + + @BeforeClass + public static void setupClass() { + BLOCKCHAIN = InMemoryStorageProvider.createInMemoryBlockchain(BLOCK_GENERATOR.genesisBlock()); + for (int i = 1; i <= 69; i++) { + final BlockDataGenerator.BlockOptions options = + new BlockDataGenerator.BlockOptions() + .setBlockNumber(i) + .setParentHash(BLOCKCHAIN.getBlockHashByNumber(i - 1).get()); + final Block block = BLOCK_GENERATOR.block(options); + final List receipts = BLOCK_GENERATOR.receipts(block); + BLOCKCHAIN.appendBlock(block, receipts); + } + } + + @Before + public void setUp() { + privateStateStorage = InMemoryStorageProvider.createInMemoryPrivateStateStorage(); + } + + @Test + public void mustResolveEmptyStateRootWhenChainHeadIsNotCommitted() { + final BlockDataGenerator.BlockOptions options = + new BlockDataGenerator.BlockOptions() + .setBlockNumber(BLOCKCHAIN.getChainHeadBlockNumber()) + .setParentHash(BLOCKCHAIN.getChainHeadHash()); + final Block block = BLOCK_GENERATOR.block(options); + final PrivateStateRootResolver privateStateRootResolver = + new PrivateStateRootResolver(privateStateStorage); + assertThat( + privateStateRootResolver.resolveLastStateRoot( + privacyGroupId, block.getHeader().getHash())) + .isEqualTo(PrivateStateRootResolver.EMPTY_ROOT_HASH); + } + + @Test + public void resolveEmptyRootHashWhenNoCommitmentForPrivacyGroupExists() { + final PrivateStateRootResolver privateStateRootResolver = + new PrivateStateRootResolver(privateStateStorage); + assertThat( + privateStateRootResolver.resolveLastStateRoot( + privacyGroupId, BLOCKCHAIN.getChainHeadHeader().getHash())) + .isEqualTo(PrivateStateRootResolver.EMPTY_ROOT_HASH); + } + + @Test + public void resolveExpectedRootHashWhenCommitmentForPrivacyGroupExists() { + final PrivateStateStorage.Updater updater = privateStateStorage.updater(); + updater.putPrivateBlockMetadata( + BLOCKCHAIN.getBlockByNumber(16).get().getHash(), + Bytes32.wrap(privacyGroupId), + new PrivateBlockMetadata( + Collections.singletonList( + new PrivateTransactionMetadata( + BLOCK_GENERATOR.transaction().getHash(), pmt1StateHash)))); + updater.putPrivacyGroupHeadBlockMap( + BLOCKCHAIN.getChainHeadHash(), + new PrivacyGroupHeadBlockMap( + Collections.singletonMap( + Bytes32.wrap(privacyGroupId), BLOCKCHAIN.getBlockByNumber(16).get().getHash()))); + updater.commit(); + final PrivateStateRootResolver privateStateRootResolver = + new PrivateStateRootResolver(privateStateStorage); + assertThat( + privateStateRootResolver.resolveLastStateRoot( + privacyGroupId, BLOCKCHAIN.getChainHeadHash())) + .isEqualTo(pmt1StateHash); + } + + @Test + public void resolveCorrectRootHashWhenMultipleCommitmentsExistForPrivacyGroup() { + final PrivateStateStorage.Updater updater = privateStateStorage.updater(); + updater.putPrivateBlockMetadata( + BLOCKCHAIN.getBlockByNumber(16).get().getHash(), + Bytes32.wrap(privacyGroupId), + new PrivateBlockMetadata( + Collections.singletonList( + new PrivateTransactionMetadata( + BLOCK_GENERATOR.transaction().getHash(), pmt1StateHash)))); + updater.putPrivateBlockMetadata( + BLOCKCHAIN.getBlockByNumber(16).get().getHash(), + Bytes32.wrap(failingPrivacyGroupId), + new PrivateBlockMetadata( + Collections.singletonList( + new PrivateTransactionMetadata( + BLOCK_GENERATOR.transaction().getHash(), pmt2StateHash)))); + updater.putPrivacyGroupHeadBlockMap( + BLOCKCHAIN.getChainHeadHash(), + new PrivacyGroupHeadBlockMap( + Collections.singletonMap( + Bytes32.wrap(privacyGroupId), BLOCKCHAIN.getBlockByNumber(16).get().getHash()))); + updater.commit(); + final PrivateStateRootResolver privateStateRootResolver = + new PrivateStateRootResolver(privateStateStorage); + assertThat( + privateStateRootResolver.resolveLastStateRoot( + privacyGroupId, BLOCKCHAIN.getChainHeadHash())) + .isEqualTo(pmt1StateHash); + } + + @Test + public void resolveLatestRootHashWhenMultipleCommitmentsForTheSamePrivacyGroupExist() { + final PrivateStateStorage.Updater updater = privateStateStorage.updater(); + updater.putPrivateBlockMetadata( + BLOCKCHAIN.getBlockByNumber(16).get().getHash(), + Bytes32.wrap(privacyGroupId), + new PrivateBlockMetadata( + Arrays.asList( + new PrivateTransactionMetadata( + BLOCK_GENERATOR.transaction().getHash(), pmt1StateHash), + new PrivateTransactionMetadata( + BLOCK_GENERATOR.transaction().getHash(), pmt2StateHash)))); + updater.putPrivacyGroupHeadBlockMap( + BLOCKCHAIN.getChainHeadHash(), + new PrivacyGroupHeadBlockMap( + Collections.singletonMap( + Bytes32.wrap(privacyGroupId), BLOCKCHAIN.getBlockByNumber(16).get().getHash()))); + updater.commit(); + final PrivateStateRootResolver privateStateRootResolver = + new PrivateStateRootResolver(privateStateStorage); + assertThat( + privateStateRootResolver.resolveLastStateRoot( + privacyGroupId, BLOCKCHAIN.getChainHeadHash())) + .isEqualTo(pmt2StateHash); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorageTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorageTest.java new file mode 100644 index 00000000000..ce7fdd12311 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/PrivateStateKeyValueStorageTest.java @@ -0,0 +1,55 @@ +/* + * 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.privacy.storage; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_0_0; +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_4_0; + +import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; + +import org.junit.Before; +import org.junit.Test; + +public class PrivateStateKeyValueStorageTest { + + private PrivateStateKeyValueStorage storage; + + @Before + public void before() { + storage = new PrivateStateKeyValueStorage(new InMemoryKeyValueStorage()); + } + + @Test + public void databaseWithoutVersionShouldReturn1_0_x() { + assertThat(storage.getSchemaVersion()).isEqualTo(SCHEMA_VERSION_1_0_0); + } + + @Test + public void databaseSetVersionShouldSetVersion() { + storage.updater().putDatabaseVersion(123).commit(); + assertThat(storage.getSchemaVersion()).isEqualTo(123); + } + + @Test + public void schemaVersion1_0_xHasCorrectValue() { + assertThat(SCHEMA_VERSION_1_0_0).isEqualTo(1); + } + + @Test + public void schemaVersion1_4_xHasCorrectValue() { + assertThat(SCHEMA_VERSION_1_4_0).isEqualTo(2); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationTest.java new file mode 100644 index 00000000000..51e57a64425 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateStorageMigrationTest.java @@ -0,0 +1,315 @@ +/* + * 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.privacy.storage.migration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMPTY_ROOT_HASH; +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_0_0; +import static org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage.SCHEMA_VERSION_1_4_0; +import static org.hyperledger.besu.ethereum.privacy.storage.migration.PrivateTransactionDataFixture.privacyMarkerTransaction; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.crypto.SECP256K1.KeyPair; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.MutableWorldState; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor.TransactionReceiptFactory; +import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.mainnet.TransactionProcessor; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; +import org.hyperledger.besu.ethereum.privacy.storage.LegacyPrivateStateStorage; +import org.hyperledger.besu.ethereum.privacy.storage.PrivacyGroupHeadBlockMap; +import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateKeyValueStorage; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; +import org.hyperledger.besu.plugin.services.storage.KeyValueStorage; +import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +@SuppressWarnings({"unchecked", "rawtypes"}) +public class PrivateStorageMigrationTest { + + private static final String PRIVACY_GROUP_ID = "tJw12cPM6EZRF5zfHv2zLePL0cqlaDjLn0x1T/V0yzE="; + public static final Bytes32 PRIVACY_GROUP_BYTES = + Bytes32.wrap(Bytes.fromBase64String(PRIVACY_GROUP_ID)); + private static final Address PRIVACY_ADDRESS = Address.DEFAULT_PRIVACY; + private static final String TRANSACTION_KEY = "93Ky7lXwFkMc7+ckoFgUMku5bpr9tz4zhmWmk9RlNng="; + + @Mock private Blockchain blockchain; + @Mock private ProtocolSchedule protocolSchedule; + @Mock private ProtocolSpec protocolSpec; + @Mock private WorldStateArchive publicWorldStateArchive; + @Mock private MutableWorldState publicMutableWorldState; + @Mock private LegacyPrivateStateStorage legacyPrivateStateStorage; + @Mock private TransactionProcessor transactionProcessor; + @Mock private TransactionReceiptFactory transactionReceiptFactory; + @Mock private MiningBeneficiaryCalculator miningBeneficiaryCalculator; + @Mock private PrivateMigrationBlockProcessor privateMigrationBlockProcessor; + + private PrivateStateKeyValueStorage privateStateStorage; + private PrivateStateRootResolver privateStateRootResolver; + private PrivateStorageMigration migration; + + @Before + public void setUp() { + final KeyValueStorage kvStorage = new InMemoryKeyValueStorage(); + + privateStateStorage = new PrivateStateKeyValueStorage(kvStorage); + privateStateRootResolver = new PrivateStateRootResolver(privateStateStorage); + + lenient().when(protocolSchedule.getByBlockNumber(anyLong())).thenReturn(protocolSpec); + lenient().when(protocolSpec.getTransactionProcessor()).thenReturn(transactionProcessor); + lenient() + .when(protocolSpec.getTransactionReceiptFactory()) + .thenReturn(transactionReceiptFactory); + lenient().when(protocolSpec.getBlockReward()).thenReturn(Wei.ZERO); + lenient() + .when(protocolSpec.getMiningBeneficiaryCalculator()) + .thenReturn(miningBeneficiaryCalculator); + lenient().when(protocolSpec.isSkipZeroBlockRewards()).thenReturn(false); + + migration = + new PrivateStorageMigration( + blockchain, + PRIVACY_ADDRESS, + protocolSchedule, + publicWorldStateArchive, + privateStateStorage, + privateStateRootResolver, + legacyPrivateStateStorage, + (protocolSpec) -> privateMigrationBlockProcessor); + } + + @Test + public void privateGroupHeadBlocKMapIsCopiedFromPreviousBlocks() { + mockBlockchainWithZeroTransactions(); + + // create existing map at block hash 'zero' (pre-genesis) + final PrivacyGroupHeadBlockMap existingPgHeadMap = + createPrivacyGroupHeadBlockInitialMap(PRIVACY_GROUP_BYTES); + + migration.migratePrivateStorage(); + + // check that for every block we have the existing mapping + for (long i = 0; i <= blockchain.getChainHeadBlockNumber(); i++) { + final Optional pgHeadMapAfterMigration = + privateStateStorage.getPrivacyGroupHeadBlockMap( + blockchain.getBlockByNumber(i).get().getHash()); + + assertThat(pgHeadMapAfterMigration).isPresent().hasValue(existingPgHeadMap); + } + } + + @Test + public void successfulMigrationBumpsSchemaVersion() { + final Transaction privacyMarkerTransaction = createPrivacyMarkerTransaction(); + mockBlockchainWithPrivacyMarkerTransaction(privacyMarkerTransaction); + assertThat(privateStateStorage.getSchemaVersion()).isEqualTo(SCHEMA_VERSION_1_0_0); + + migration.migratePrivateStorage(); + + assertThat(privateStateStorage.getSchemaVersion()).isEqualTo(SCHEMA_VERSION_1_4_0); + } + + @Test + public void failedMigrationThrowsErrorAndDoesNotBumpSchemaVersion() { + final Transaction privacyMarkerTransaction = createPrivacyMarkerTransaction(); + mockBlockchainWithPrivacyMarkerTransaction(privacyMarkerTransaction); + createPrivacyGroupHeadBlockInitialMap(PRIVACY_GROUP_BYTES); + + // final state root won't match the legacy state root + when(legacyPrivateStateStorage.getLatestStateRoot(any())).thenReturn(Optional.of(Hash.ZERO)); + + assertThat(privateStateStorage.getSchemaVersion()).isEqualTo(SCHEMA_VERSION_1_0_0); + + assertThatThrownBy(() -> migration.migratePrivateStorage()) + .isInstanceOf(PrivateStorageMigrationException.class) + .hasMessageContaining("Inconsistent state root"); + + assertThat(privateStateStorage.getSchemaVersion()).isEqualTo(SCHEMA_VERSION_1_0_0); + } + + @Test + public void migrationInBlockchainWithZeroPMTsDoesNotReprocessAnyBlocks() { + mockBlockchainWithZeroTransactions(); + + migration.migratePrivateStorage(); + + verifyNoInteractions(privateMigrationBlockProcessor); + } + + @Test + public void migrationReprocessBlocksWithPMT() { + final Transaction privacyMarkerTransaction = createPrivacyMarkerTransaction(); + mockBlockchainWithPrivacyMarkerTransaction(privacyMarkerTransaction); + final Block blockWithPMT = blockchain.getBlockByNumber(1L).orElseThrow(); + + migration.migratePrivateStorage(); + + verify(privateMigrationBlockProcessor) + .processBlock( + any(), + any(), + eq(blockWithPMT.getHeader()), + eq(blockWithPMT.getBody().getTransactions()), + eq(blockWithPMT.getBody().getOmmers())); + } + + /* + When processing a block, we only need to process up to the last PTM in the block. + */ + @Test + public void migrationOnlyProcessRequiredTransactions() { + final List transactions = new ArrayList<>(); + transactions.add(publicTransaction()); + transactions.add(createPrivacyMarkerTransaction()); + transactions.add(publicTransaction()); + + mockBlockchainWithTransactionsInABlock(transactions); + + migration.migratePrivateStorage(); + + final ArgumentCaptor txsCaptor = ArgumentCaptor.forClass(List.class); + + verify(privateMigrationBlockProcessor) + .processBlock(any(), any(), any(), txsCaptor.capture(), any()); + + // won't process transaction after PMT, that's why we only process 2 txs + final List processedTxs = txsCaptor.getValue(); + assertThat(processedTxs).hasSize(2); + } + + private PrivacyGroupHeadBlockMap createPrivacyGroupHeadBlockInitialMap( + final Bytes32 privacyGroupBytes) { + final PrivacyGroupHeadBlockMap existingPgHeadMap = + new PrivacyGroupHeadBlockMap(Map.of(privacyGroupBytes, Hash.ZERO)); + privateStateStorage + .updater() + .putPrivacyGroupHeadBlockMap(Hash.ZERO, existingPgHeadMap) + .commit(); + return existingPgHeadMap; + } + + private Transaction createPrivacyMarkerTransaction() { + final Transaction privacyMarkerTransaction = privacyMarkerTransaction(TRANSACTION_KEY); + mockBlockchainWithPrivacyMarkerTransaction(privacyMarkerTransaction); + return privacyMarkerTransaction; + } + + private void mockBlockchainWithZeroTransactions() { + final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); + + final Block genesis = blockDataGenerator.genesisBlock(); + mockBlockInBlockchain(genesis); + + final BlockDataGenerator.BlockOptions options = + BlockDataGenerator.BlockOptions.create() + .setParentHash(genesis.getHash()) + .setBlockNumber(1) + .hasTransactions(false); + final Block block = blockDataGenerator.block(options); + mockBlockInBlockchain(block); + mockChainHeadInBlockchain(block); + + when(legacyPrivateStateStorage.getLatestStateRoot(any())) + .thenReturn(Optional.of(EMPTY_ROOT_HASH)); + } + + private void mockBlockchainWithPrivacyMarkerTransaction(final Transaction transaction) { + final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); + + final Block genesis = blockDataGenerator.genesisBlock(); + mockBlockInBlockchain(genesis); + + final BlockDataGenerator.BlockOptions options = + BlockDataGenerator.BlockOptions.create() + .setParentHash(genesis.getHash()) + .setBlockNumber(1) + .addTransaction(transaction); + final Block block = blockDataGenerator.block(options); + mockBlockInBlockchain(block); + mockChainHeadInBlockchain(block); + } + + private void mockBlockchainWithTransactionsInABlock(final List transactions) { + final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); + + final Block genesis = blockDataGenerator.genesisBlock(); + mockBlockInBlockchain(genesis); + + final Block block = + blockDataGenerator.block( + BlockDataGenerator.BlockOptions.create() + .setParentHash(genesis.getHash()) + .setBlockNumber(1) + .addTransaction(transactions)); + mockBlockInBlockchain(block); + mockChainHeadInBlockchain(block); + } + + private void mockBlockInBlockchain(final Block block) { + when(blockchain.getBlockByNumber(block.getHeader().getNumber())).thenReturn(Optional.of(block)); + when(blockchain.getBlockHeader(block.getHash())).thenReturn(Optional.of(block.getHeader())); + when(blockchain.getBlockBody(block.getHash())).thenReturn(Optional.of(block.getBody())); + + when(publicWorldStateArchive.getMutable(block.getHeader().getStateRoot())) + .thenReturn(Optional.of(publicMutableWorldState)); + } + + private void mockChainHeadInBlockchain(final Block block) { + when(blockchain.getChainHeadBlockNumber()).thenReturn(block.getHeader().getNumber()); + when(blockchain.getChainHeadHash()).thenReturn(block.getHash()); + } + + private Transaction publicTransaction() { + return Transaction.builder() + .nonce(0) + .gasPrice(Wei.of(1000)) + .gasLimit(3000000) + .value(Wei.ZERO) + .payload(Bytes.EMPTY) + .sender(Address.fromHexString("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73")) + .chainId(BigInteger.valueOf(2018)) + .signAndBuild(KeyPair.generate()); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateTransactionDataFixture.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateTransactionDataFixture.java new file mode 100644 index 00000000000..cb15b0ff75b --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateTransactionDataFixture.java @@ -0,0 +1,85 @@ +/* + * 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.privacy.storage.migration; + +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.BlockDataGenerator; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.mainnet.ValidationResult; +import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor.Result; +import org.hyperledger.besu.ethereum.privacy.Restriction; + +import java.math.BigInteger; + +import org.apache.tuweni.bytes.Bytes; + +public class PrivateTransactionDataFixture { + + private static final SECP256K1.KeyPair KEY_PAIR = + SECP256K1.KeyPair.create( + SECP256K1.PrivateKey.create( + new BigInteger( + "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); + + private static final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); + + static Transaction privacyMarkerTransaction(final String transactionKey) { + return Transaction.builder() + .nonce(0) + .gasPrice(Wei.of(1000)) + .gasLimit(3000000) + .to(Address.DEFAULT_PRIVACY) + .value(Wei.ZERO) + .payload(Bytes.fromBase64String(transactionKey)) + .sender(Address.fromHexString("0xfe3b557e8fb62b89f4916b721be55ceb828dbd73")) + .chainId(BigInteger.valueOf(2018)) + .signAndBuild(KEY_PAIR); + } + + static PrivateTransaction privateTransaction(final String privacyGroupId) { + return PrivateTransaction.builder() + .nonce(0) + .gasPrice(Wei.of(1000)) + .gasLimit(3000000) + .to(null) + .value(Wei.ZERO) + .payload( + Bytes.fromHexString( + "0x608060405234801561001057600080fd5b5060d08061001f6000396000" + + "f3fe60806040526004361060485763ffffffff7c010000000000" + + "0000000000000000000000000000000000000000000000600035" + + "04166360fe47b18114604d5780636d4ce63c146075575b600080" + + "fd5b348015605857600080fd5b50607360048036036020811015" + + "606d57600080fd5b50356099565b005b348015608057600080fd" + + "5b506087609e565b60408051918252519081900360200190f35b" + + "600055565b6000549056fea165627a7a72305820cb1d0935d14b" + + "589300b12fcd0ab849a7e9019c81da24d6daa4f6b2f003d1b018" + + "0029")) + .sender(Address.wrap(Bytes.fromHexString("0x1c9a6e1ee3b7ac6028e786d9519ae3d24ee31e79"))) + .chainId(BigInteger.valueOf(4)) + .privateFrom(Bytes.fromBase64String("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")) + .privacyGroupId(Bytes.fromBase64String(privacyGroupId)) + .restriction(Restriction.RESTRICTED) + .signAndBuild(KEY_PAIR); + } + + public static Result successfulPrivateTxProcessingResult() { + return Result.successful( + blockDataGenerator.logs(3, 1), 0, Bytes.EMPTY, ValidationResult.valid()); + } +} From c38152abf0b1ae5573a4221975cb281dbd76fc9c Mon Sep 17 00:00:00 2001 From: Abdelhamid Bakhta <45264458+abdelhamidbakhta@users.noreply.github.com> Date: Mon, 17 Feb 2020 15:15:55 +0100 Subject: [PATCH 23/38] [PIE-2303] Automatic log bloom caching - Remove usage of pending file. (#407) * Don't use pending file. Signed-off-by: Abdelhamid Bakhta * Don't use pending file. Signed-off-by: Abdelhamid Bakhta --- ...AutoTransactionLogBloomCachingService.java | 7 +++- .../api/query/TransactionLogBloomCacher.java | 38 +++++++++---------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java index ed7b84fc655..a500720a32d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/AutoTransactionLogBloomCachingService.java @@ -20,6 +20,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.time.Duration; +import java.util.Optional; import java.util.OptionalLong; import org.apache.logging.log4j.LogManager; @@ -51,13 +52,15 @@ public void start() { (event, __) -> { if (event.isNewCanonicalHead()) { transactionLogBloomCacher.cacheLogsBloomForBlockHeader( - event.getBlock().getHeader()); + event.getBlock().getHeader(), Optional.empty(), true); } })); chainReorgSubscriptionId = OptionalLong.of( blockchain.observeChainReorg( - (header, __) -> transactionLogBloomCacher.cacheLogsBloomForBlockHeader(header))); + (header, __) -> + transactionLogBloomCacher.cacheLogsBloomForBlockHeader( + header, Optional.empty(), true))); transactionLogBloomCacher .getScheduler() diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java index e72d274fca8..4148ea61331 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java @@ -30,7 +30,6 @@ import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.time.Duration; import java.util.Map; import java.util.Optional; @@ -49,7 +48,6 @@ public class TransactionLogBloomCacher { public static final int BLOCKS_PER_BLOOM_CACHE = 100_000; private static final int BLOOM_BITS_LENGTH = 256; - public static final String PENDING = "pending"; private final Map cachedSegments; private final Lock submissionLock = new ReentrantLock(); @@ -95,22 +93,16 @@ public CachingStatus generateLogBloomCache(final long start, final long stop) { LOG.error("Cache directory '{}' does not exist and could not be made.", cacheDir); return cachingStatus; } - - final File pendingFile = calculateCacheFileName(PENDING, cacheDir); for (long blockNum = start; blockNum < stop; blockNum += BLOCKS_PER_BLOOM_CACHE) { LOG.info("Caching segment at {}", blockNum); - try (final FileOutputStream fos = new FileOutputStream(pendingFile)) { - final long blockCount = fillCacheFile(blockNum, blockNum + BLOCKS_PER_BLOOM_CACHE, fos); - if (blockCount == BLOCKS_PER_BLOOM_CACHE) { - Files.move( - pendingFile.toPath(), - calculateCacheFileName(blockNum, cacheDir).toPath(), - StandardCopyOption.REPLACE_EXISTING, - StandardCopyOption.ATOMIC_MOVE); - } else { - LOG.info("Partial segment at {}, only {} blocks cached", blockNum, blockCount); - break; - } + final File cacheFile = calculateCacheFileName(blockNum, cacheDir); + blockchain + .getBlockHeader(blockNum) + .ifPresent( + blockHeader -> + cacheLogsBloomForBlockHeader(blockHeader, Optional.of(cacheFile), false)); + try (final FileOutputStream fos = new FileOutputStream(cacheFile)) { + fillCacheFile(blockNum, blockNum + BLOCKS_PER_BLOOM_CACHE, fos); } } } catch (final Exception e) { @@ -122,7 +114,7 @@ public CachingStatus generateLogBloomCache(final long start, final long stop) { return cachingStatus; } - private long fillCacheFile( + private void fillCacheFile( final long startBlock, final long stopBlock, final FileOutputStream fos) throws IOException { long blockNum = startBlock; while (blockNum < stopBlock) { @@ -134,15 +126,19 @@ private long fillCacheFile( cachingStatus.currentBlock = blockNum; blockNum++; } - return blockNum - startBlock; } - public void cacheLogsBloomForBlockHeader(final BlockHeader blockHeader) { + public void cacheLogsBloomForBlockHeader( + final BlockHeader blockHeader, + final Optional reusedCacheFile, + final boolean ensureChecks) { try { final long blockNumber = blockHeader.getNumber(); LOG.debug("Caching logs bloom for block {}.", "0x" + Long.toHexString(blockNumber)); - ensurePreviousSegmentsArePresent(blockNumber); - final File cacheFile = calculateCacheFileName(blockNumber, cacheDir); + if (ensureChecks) { + ensurePreviousSegmentsArePresent(blockNumber); + } + final File cacheFile = reusedCacheFile.orElse(calculateCacheFileName(blockNumber, cacheDir)); if (!cacheFile.exists()) { Files.createFile(cacheFile.toPath()); } From 8afd6516e1fce466d11147f9a7391542f548f9e0 Mon Sep 17 00:00:00 2001 From: Abdelhamid Bakhta <45264458+abdelhamidbakhta@users.noreply.github.com> Date: Tue, 18 Feb 2020 17:56:15 +0100 Subject: [PATCH 24/38] Create a custom error when plugin is not found. (#409) Signed-off-by: Abdelhamid Bakhta --- .../jsonrpc/internal/methods/PluginsReloadConfiguration.java | 2 +- .../ethereum/api/jsonrpc/internal/response/JsonRpcError.java | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/PluginsReloadConfiguration.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/PluginsReloadConfiguration.java index 5c682b6dad1..8905def5787 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/PluginsReloadConfiguration.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/PluginsReloadConfiguration.java @@ -52,7 +52,7 @@ public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { "Plugin cannot be reloaded because no plugin has been registered with specified name: {}.", pluginName); return new JsonRpcErrorResponse( - requestContext.getRequest().getId(), JsonRpcError.INTERNAL_ERROR); + requestContext.getRequest().getId(), JsonRpcError.PLUGIN_NOT_FOUND); } reloadPluginConfig(namedPlugins.get(pluginName)); return new JsonRpcSuccessResponse(requestContext.getRequest().getId()); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java index 5b61f2cfaa0..a18159dffbc 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/response/JsonRpcError.java @@ -150,7 +150,10 @@ public enum JsonRpcError { ENCLAVE_PRIVACY_GROUP_MISSING(-50200, "PrivacyGroupNotFound"), ENCLAVE_PRIVACY_QUERY_ERROR(-50200, "PrivacyGroupQueryError"), ENCLAVE_KEYS_CANNOT_DECRYPT_PAYLOAD(-50200, "EnclaveKeysCannotDecryptPayload"), - METHOD_UNIMPLEMENTED(-50200, "MethodUnimplemented"); + METHOD_UNIMPLEMENTED(-50200, "MethodUnimplemented"), + + /** Plugins error */ + PLUGIN_NOT_FOUND(-60000, "Plugin not found"); private final int code; private final String message; From 1b0dffcb3072f3eae583099535a3e7838c5707d9 Mon Sep 17 00:00:00 2001 From: pinges <16143240+pinges@users.noreply.github.com> Date: Wed, 19 Feb 2020 16:00:19 +1000 Subject: [PATCH 25/38] Rename method (#412) * rename the method isPersistingState to isPersistingPrivateState because that is what it is used for Signed-off-by: Stefan Pingel * rename the method isPersistingState to isPersistingPrivateState because that is what it is used for Signed-off-by: Stefan Pingel * rename the method isPersistingState to isPersistingPrivateState because that is what it is used for Signed-off-by: Stefan Pingel --- .../mainnet/MainnetTransactionProcessor.java | 6 +++--- .../mainnet/TransactionProcessor.java | 14 ++++++------- .../privacy/PrivacyPrecompiledContract.java | 4 ++-- .../besu/ethereum/vm/MessageFrame.java | 20 +++++++++---------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 7c195da373a..b031c245561 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -172,7 +172,7 @@ public Result processTransaction( final Address miningBeneficiary, final OperationTracer operationTracer, final BlockHashLookup blockHashLookup, - final Boolean isPersistingState, + final Boolean isPersistingPrivateState, final TransactionValidationParams transactionValidationParams) { LOG.trace("Starting execution of {}", transaction); @@ -246,7 +246,7 @@ public Result processTransaction( .completer(c -> {}) .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) - .isPersistingState(isPersistingState) + .isPersistingPrivateState(isPersistingPrivateState) .maxStackSize(maxStackSize) .transactionHash(transaction.getHash()) .build(); @@ -279,7 +279,7 @@ public Result processTransaction( .miningBeneficiary(miningBeneficiary) .blockHashLookup(blockHashLookup) .maxStackSize(maxStackSize) - .isPersistingState(isPersistingState) + .isPersistingPrivateState(isPersistingPrivateState) .transactionHash(transaction.getHash()) .build(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionProcessor.java index 349a9aaf0a6..1c08c2a0e7b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/TransactionProcessor.java @@ -116,7 +116,7 @@ default boolean isSuccessful() { * @param transaction The transaction to process * @param miningBeneficiary The address which is to receive the transaction fee * @param blockHashLookup The {@link BlockHashLookup} to use for BLOCKHASH operations - * @param isPersistingState Whether the state will be modified by this process + * @param isPersistingPrivateState Whether the resulting private state will be persisted * @param transactionValidationParams Validation parameters that will be used by the {@link * TransactionValidator} * @return the transaction result @@ -130,7 +130,7 @@ default Result processTransaction( final Transaction transaction, final Address miningBeneficiary, final BlockHashLookup blockHashLookup, - final Boolean isPersistingState, + final Boolean isPersistingPrivateState, final TransactionValidationParams transactionValidationParams) { return processTransaction( blockchain, @@ -140,7 +140,7 @@ default Result processTransaction( miningBeneficiary, OperationTracer.NO_TRACING, blockHashLookup, - isPersistingState, + isPersistingPrivateState, transactionValidationParams); } @@ -154,7 +154,7 @@ default Result processTransaction( * @param operationTracer The tracer to record results of each EVM operation * @param miningBeneficiary The address which is to receive the transaction fee * @param blockHashLookup The {@link BlockHashLookup} to use for BLOCKHASH operations - * @param isPersistingState Whether the state will be modified by this process + * @param isPersistingPrivateState Whether the resulting private state will be persisted * @return the transaction result */ default Result processTransaction( @@ -165,7 +165,7 @@ default Result processTransaction( final Address miningBeneficiary, final OperationTracer operationTracer, final BlockHashLookup blockHashLookup, - final Boolean isPersistingState) { + final Boolean isPersistingPrivateState) { return processTransaction( blockchain, worldState, @@ -174,7 +174,7 @@ default Result processTransaction( miningBeneficiary, operationTracer, blockHashLookup, - isPersistingState, + isPersistingPrivateState, new TransactionValidationParams.Builder().build()); } @@ -186,6 +186,6 @@ Result processTransaction( Address miningBeneficiary, OperationTracer operationTracer, BlockHashLookup blockHashLookup, - Boolean isPersistingState, + Boolean isPersistingPrivateState, TransactionValidationParams transactionValidationParams); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java index 2a3c21b53d3..23cc3e01448 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java @@ -97,7 +97,7 @@ public Gas gasRequirement(final Bytes input) { public Bytes compute(final Bytes input, final MessageFrame messageFrame) { final ProcessableBlockHeader currentBlockHeader = messageFrame.getBlockHeader(); if (!BlockHeader.class.isAssignableFrom(currentBlockHeader.getClass())) { - if (!messageFrame.isPersistingState()) { + if (!messageFrame.isPersistingPrivateState()) { // We get in here from block mining. return Bytes.EMPTY; } else { @@ -168,7 +168,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) { return Bytes.EMPTY; } - if (messageFrame.isPersistingState()) { + if (messageFrame.isPersistingPrivateState()) { LOG.trace( "Persisting private state {} for privacyGroup {}", disposablePrivateState.rootHash(), diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java index f5e80cb7c73..edf009dff96 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/MessageFrame.java @@ -227,7 +227,7 @@ public enum Type { private final int depth; private final Deque messageFrameStack; private final Address miningBeneficiary; - private final Boolean isPersistingState; + private final Boolean isPersistingPrivateState; private Optional revertReason; // Privacy Execution Environment fields. @@ -267,7 +267,7 @@ private MessageFrame( final Consumer completer, final Address miningBeneficiary, final BlockHashLookup blockHashLookup, - final Boolean isPersistingState, + final Boolean isPersistingPrivateState, final Hash transactionHash, final Optional revertReason, final int maxStackSize) { @@ -303,7 +303,7 @@ private MessageFrame( this.isStatic = isStatic; this.completer = completer; this.miningBeneficiary = miningBeneficiary; - this.isPersistingState = isPersistingState; + this.isPersistingPrivateState = isPersistingPrivateState; this.transactionHash = transactionHash; this.revertReason = revertReason; } @@ -962,8 +962,8 @@ public int getMaxStackSize() { * * @return whether Message calls will be persisted */ - public Boolean isPersistingState() { - return isPersistingState; + public Boolean isPersistingPrivateState() { + return isPersistingPrivateState; } /** @@ -1020,7 +1020,7 @@ public static class Builder { private Consumer completer; private Address miningBeneficiary; private BlockHashLookup blockHashLookup; - private Boolean isPersistingState = false; + private Boolean isPersistingPrivateState = false; private Hash transactionHash; private Optional reason = Optional.empty(); @@ -1135,8 +1135,8 @@ public Builder blockHashLookup(final BlockHashLookup blockHashLookup) { return this; } - public Builder isPersistingState(final Boolean isPersistingState) { - this.isPersistingState = isPersistingState; + public Builder isPersistingPrivateState(final Boolean isPersistingPrivateState) { + this.isPersistingPrivateState = isPersistingPrivateState; return this; } @@ -1170,7 +1170,7 @@ private void validate() { checkState(completer != null, "Missing message frame completer"); checkState(miningBeneficiary != null, "Missing mining beneficiary"); checkState(blockHashLookup != null, "Missing block hash lookup"); - checkState(isPersistingState != null, "Missing isPersistingState"); + checkState(isPersistingPrivateState != null, "Missing isPersistingPrivateState"); checkState(contractAccountVersion != -1, "Missing contractAccountVersion"); } @@ -1199,7 +1199,7 @@ public MessageFrame build() { completer, miningBeneficiary, blockHashLookup, - isPersistingState, + isPersistingPrivateState, transactionHash, reason, maxStackSize); From 8a6840273252af81e44b6b245d9a81d8f11389af Mon Sep 17 00:00:00 2001 From: Karim T Date: Wed, 19 Feb 2020 10:34:36 +0100 Subject: [PATCH 26/38] [BOUNTY-4] Add NAT Kubernetes Support (#410) * add kubernetes support Signed-off-by: Karim TAAM * fix review issues Signed-off-by: Karim TAAM --- .../org/hyperledger/besu/RunnerBuilder.java | 11 +- .../hyperledger/besu/cli/BesuCommandTest.java | 5 +- gradle/versions.gradle | 2 + nat/build.gradle | 1 + .../org/hyperledger/besu/nat/NatMethod.java | 1 + .../nat/kubernetes/KubernetesDetector.java | 45 ++++++ .../nat/kubernetes/KubernetesNatManager.java | 150 +++++++++++++++++ .../kubernetes/KubernetesNatManagerTest.java | 153 ++++++++++++++++++ 8 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 nat/src/main/java/org/hyperledger/besu/nat/kubernetes/KubernetesDetector.java create mode 100644 nat/src/main/java/org/hyperledger/besu/nat/kubernetes/KubernetesNatManager.java create mode 100644 nat/src/test/java/org/hyperledger/besu/nat/kubernetes/KubernetesNatManagerTest.java diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index 243da5cc07e..c7abf140faa 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -93,6 +93,8 @@ import org.hyperledger.besu.nat.core.NatManager; import org.hyperledger.besu.nat.docker.DockerDetector; import org.hyperledger.besu.nat.docker.DockerNatManager; +import org.hyperledger.besu.nat.kubernetes.KubernetesDetector; +import org.hyperledger.besu.nat.kubernetes.KubernetesNatManager; import org.hyperledger.besu.nat.manual.ManualNatManager; import org.hyperledger.besu.nat.upnp.UpnpNatManager; import org.hyperledger.besu.plugin.BesuPlugin; @@ -114,10 +116,14 @@ import com.google.common.base.Preconditions; import graphql.GraphQL; import io.vertx.core.Vertx; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; public class RunnerBuilder { + protected static final Logger LOG = LogManager.getLogger(); + private Vertx vertx; private BesuController besuController; @@ -346,6 +352,7 @@ public Runner build() { .map(nodePerms -> PeerPermissions.combine(nodePerms, bannedNodes)) .orElse(bannedNodes); + LOG.info("Detecting NAT service."); final NatService natService = new NatService(buildNatManager(natMethod)); final NetworkBuilder inactiveNetwork = (caps) -> new NoopP2PNetwork(); final NetworkBuilder activeNetwork = @@ -590,7 +597,7 @@ private Optional buildNatManager(final NatMethod natMethod) { final NatMethod detectedNatMethod = Optional.of(natMethod) .filter(not(isEqual(NatMethod.AUTO))) - .orElse(NatService.autoDetectNatMethod(new DockerDetector())); + .orElse(NatService.autoDetectNatMethod(new DockerDetector(), new KubernetesDetector())); switch (detectedNatMethod) { case UPNP: return Optional.of(new UpnpNatManager()); @@ -600,6 +607,8 @@ private Optional buildNatManager(final NatMethod natMethod) { case DOCKER: return Optional.of( new DockerNatManager(p2pAdvertisedHost, p2pListenPort, jsonRpcConfiguration.getPort())); + case KUBERNETES: + return Optional.of(new KubernetesNatManager()); case NONE: default: return Optional.empty(); diff --git a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java index b65bcfd4185..e794940a4cb 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java @@ -1395,6 +1395,9 @@ public void natMethodOptionIsParsedCorrectly() { parseCommand("--nat-method", "DOCKER"); verify(mockRunnerBuilder).natMethod(eq(NatMethod.DOCKER)); + parseCommand("--nat-method", "KUBERNETES"); + verify(mockRunnerBuilder).natMethod(eq(NatMethod.KUBERNETES)); + assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()).isEmpty(); } @@ -1407,7 +1410,7 @@ public void parsesInvalidNatMethodOptionsShouldFail() { assertThat(commandOutput.toString()).isEmpty(); assertThat(commandErrorOutput.toString()) .contains( - "Invalid value for option '--nat-method': expected one of [UPNP, MANUAL, DOCKER, AUTO, NONE] (case-insensitive) but was 'invalid'"); + "Invalid value for option '--nat-method': expected one of [UPNP, MANUAL, DOCKER, KUBERNETES, AUTO, NONE] (case-insensitive) but was 'invalid'"); } @Test diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 4521ec20311..df1c6865703 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -102,6 +102,8 @@ dependencyManagement { dependency 'org.xerial.snappy:snappy-java:1.1.7.3' + dependency 'io.kubernetes:client-java:5.0.0' + dependency 'tech.pegasys.ethsigner.internal:core:0.4.0' dependency 'tech.pegasys.ethsigner.internal:file-based:0.4.0' dependency 'tech.pegasys.ethsigner.internal:signing-api:0.4.0' diff --git a/nat/build.gradle b/nat/build.gradle index 099fa2721ed..a5967c6374d 100644 --- a/nat/build.gradle +++ b/nat/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-api' implementation 'org.jupnp:org.jupnp' implementation 'org.jupnp:org.jupnp.support' + implementation 'io.kubernetes:client-java' runtimeOnly 'org.apache.logging.log4j:log4j-core' diff --git a/nat/src/main/java/org/hyperledger/besu/nat/NatMethod.java b/nat/src/main/java/org/hyperledger/besu/nat/NatMethod.java index 2b72c68feb3..3877a923e48 100644 --- a/nat/src/main/java/org/hyperledger/besu/nat/NatMethod.java +++ b/nat/src/main/java/org/hyperledger/besu/nat/NatMethod.java @@ -18,6 +18,7 @@ public enum NatMethod { UPNP, MANUAL, DOCKER, + KUBERNETES, AUTO, NONE; diff --git a/nat/src/main/java/org/hyperledger/besu/nat/kubernetes/KubernetesDetector.java b/nat/src/main/java/org/hyperledger/besu/nat/kubernetes/KubernetesDetector.java new file mode 100644 index 00000000000..823e066042e --- /dev/null +++ b/nat/src/main/java/org/hyperledger/besu/nat/kubernetes/KubernetesDetector.java @@ -0,0 +1,45 @@ +/* + * 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.nat.kubernetes; + +import org.hyperledger.besu.nat.NatMethod; +import org.hyperledger.besu.nat.core.NatMethodDetector; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +public class KubernetesDetector implements NatMethodDetector { + + // When a Pod runs on a Node, the kubelet adds a set of environment variables for each active + // Service. + // https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/#environment-variables + private static final Optional KUBERNETES_SERVICE_HOST = + Optional.ofNullable(System.getenv("KUBERNETES_SERVICE_HOST")); + private static final Path KUBERNETES_WATERMARK_FILE = Paths.get("var/run/secrets/kubernetes.io"); + + @Override + public Optional detect() { + return KUBERNETES_SERVICE_HOST + .map(__ -> NatMethod.KUBERNETES) + .or( + () -> + Files.exists(KUBERNETES_WATERMARK_FILE) + ? Optional.of(NatMethod.KUBERNETES) + : Optional.empty()); + } +} diff --git a/nat/src/main/java/org/hyperledger/besu/nat/kubernetes/KubernetesNatManager.java b/nat/src/main/java/org/hyperledger/besu/nat/kubernetes/KubernetesNatManager.java new file mode 100644 index 00000000000..6f5e4f0e70a --- /dev/null +++ b/nat/src/main/java/org/hyperledger/besu/nat/kubernetes/KubernetesNatManager.java @@ -0,0 +1,150 @@ +/* + * 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.nat.kubernetes; + +import org.hyperledger.besu.nat.NatMethod; +import org.hyperledger.besu.nat.core.AbstractNatManager; +import org.hyperledger.besu.nat.core.domain.NatPortMapping; +import org.hyperledger.besu.nat.core.domain.NatServiceType; +import org.hyperledger.besu.nat.core.domain.NetworkProtocol; + +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import com.google.common.annotations.VisibleForTesting; +import io.kubernetes.client.ApiClient; +import io.kubernetes.client.Configuration; +import io.kubernetes.client.apis.CoreV1Api; +import io.kubernetes.client.models.V1Service; +import io.kubernetes.client.util.ClientBuilder; +import io.kubernetes.client.util.KubeConfig; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * This class describes the behaviour of the Kubernetes NAT manager. Kubernetes Nat manager add + * support for Kubernetes’s NAT implementation when Besu is being run from a Kubernetes cluster + */ +public class KubernetesNatManager extends AbstractNatManager { + protected static final Logger LOG = LogManager.getLogger(); + + private static final String KUBE_CONFIG_PATH_ENV = "KUBE_CONFIG_PATH"; + private static final String DEFAULT_KUBE_CONFIG_PATH = "~/.kube/config"; + private static final String DEFAULT_BESU_POD_NAME_FILTER = "besu"; + + private String internalAdvertisedHost; + private final List forwardedPorts = new ArrayList<>(); + + public KubernetesNatManager() { + super(NatMethod.KUBERNETES); + } + + @Override + protected void doStart() { + LOG.info("Starting kubernetes NAT manager."); + update(); + } + + private void update() { + try { + LOG.debug("Trying to update information using Kubernetes client SDK."); + final String kubeConfigPath = + Optional.ofNullable(System.getenv(KUBE_CONFIG_PATH_ENV)).orElse(DEFAULT_KUBE_CONFIG_PATH); + LOG.debug( + "Checking if Kubernetes config file is present on file system: {}.", kubeConfigPath); + if (!Files.exists(Paths.get(kubeConfigPath))) { + throw new IllegalStateException("Cannot locate Kubernetes config file."); + } + // loading the out-of-cluster config, a kubeconfig from file-system + final ApiClient client = + ClientBuilder.kubeconfig( + KubeConfig.loadKubeConfig( + Files.newBufferedReader(Paths.get(kubeConfigPath), Charset.defaultCharset()))) + .build(); + + // set the global default api-client to the in-cluster one from above + Configuration.setDefaultApiClient(client); + + // the CoreV1Api loads default api-client from global configuration. + CoreV1Api api = new CoreV1Api(); + // invokes the CoreV1Api client + api.listServiceForAllNamespaces(null, null, null, null, null, null, null, null, null) + .getItems().stream() + .filter( + v1Service -> v1Service.getMetadata().getName().contains(DEFAULT_BESU_POD_NAME_FILTER)) + .findFirst() + .ifPresent(this::updateUsingBesuService); + + } catch (Exception e) { + LOG.warn("Failed update information using Kubernetes client SDK.", e); + } + } + + @VisibleForTesting + void updateUsingBesuService(final V1Service service) { + try { + LOG.info("Found Besu service: {}", service.getMetadata().getName()); + LOG.info("Setting host IP to: {}.", service.getSpec().getClusterIP()); + internalAdvertisedHost = service.getSpec().getClusterIP(); + final String internalHost = queryLocalIPAddress().get(TIMEOUT_SECONDS, TimeUnit.SECONDS); + service + .getSpec() + .getPorts() + .forEach( + v1ServicePort -> { + try { + final NatServiceType natServiceType = + NatServiceType.fromString(v1ServicePort.getName()); + forwardedPorts.add( + new NatPortMapping( + natServiceType, + natServiceType.equals(NatServiceType.DISCOVERY) + ? NetworkProtocol.UDP + : NetworkProtocol.TCP, + internalHost, + internalAdvertisedHost, + v1ServicePort.getPort(), + v1ServicePort.getTargetPort().getIntValue())); + } catch (IllegalStateException e) { + LOG.warn("Ignored unknown Besu port: {}", e.getMessage()); + } + }); + } catch (Exception e) { + LOG.warn("Failed update information using pod metadata.", e); + } + } + + @Override + protected void doStop() { + LOG.info("Stopping kubernetes NAT manager."); + } + + @Override + protected CompletableFuture retrieveExternalIPAddress() { + return CompletableFuture.completedFuture(internalAdvertisedHost); + } + + @Override + public CompletableFuture> getPortMappings() { + return CompletableFuture.completedFuture(forwardedPorts); + } +} diff --git a/nat/src/test/java/org/hyperledger/besu/nat/kubernetes/KubernetesNatManagerTest.java b/nat/src/test/java/org/hyperledger/besu/nat/kubernetes/KubernetesNatManagerTest.java new file mode 100644 index 00000000000..030256d7e62 --- /dev/null +++ b/nat/src/test/java/org/hyperledger/besu/nat/kubernetes/KubernetesNatManagerTest.java @@ -0,0 +1,153 @@ +/* + * 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.nat.kubernetes; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.nat.core.domain.NatPortMapping; +import org.hyperledger.besu.nat.core.domain.NatServiceType; +import org.hyperledger.besu.nat.core.domain.NetworkProtocol; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.concurrent.ExecutionException; + +import io.kubernetes.client.custom.IntOrString; +import io.kubernetes.client.models.V1ObjectMeta; +import io.kubernetes.client.models.V1Service; +import io.kubernetes.client.models.V1ServicePort; +import io.kubernetes.client.models.V1ServiceSpec; +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 final class KubernetesNatManagerTest { + + private final String detectedAdvertisedHost = "199.45.69.12"; + + private final int p2pPort = 1; + private final int rpcHttpPort = 2; + + @Mock private V1Service v1Service; + + private KubernetesNatManager natManager; + + @Before + public void initialize() throws IOException { + when(v1Service.getSpec()) + .thenReturn( + new V1ServiceSpec() + .clusterIP(detectedAdvertisedHost) + .ports( + Arrays.asList( + new V1ServicePort() + .name(NatServiceType.JSON_RPC.getValue()) + .port(rpcHttpPort) + .targetPort(new IntOrString(rpcHttpPort)), + new V1ServicePort() + .name(NatServiceType.RLPX.getValue()) + .port(p2pPort) + .targetPort(new IntOrString(p2pPort)), + new V1ServicePort() + .name(NatServiceType.DISCOVERY.getValue()) + .port(p2pPort) + .targetPort(new IntOrString(p2pPort))))); + when(v1Service.getMetadata()).thenReturn(new V1ObjectMeta().name("besu")); + natManager = new KubernetesNatManager(); + try { + natManager.start(); + } catch (Exception ignored) { + System.err.println("Ignored missing Kube config file in testing context."); + } + natManager.updateUsingBesuService(v1Service); + } + + @Test + public void assertThatExternalIPIsEqualToRemoteHost() + throws ExecutionException, InterruptedException { + + assertThat(natManager.queryExternalIPAddress().get()).isEqualTo(detectedAdvertisedHost); + } + + @Test + public void assertThatLocalIPIsEqualToLocalHost() + throws ExecutionException, InterruptedException, UnknownHostException { + final String internalHost = InetAddress.getLocalHost().getHostAddress(); + assertThat(natManager.queryLocalIPAddress().get()).isEqualTo(internalHost); + } + + @Test + public void assertThatMappingForDiscoveryWorks() throws UnknownHostException { + final String internalHost = InetAddress.getLocalHost().getHostAddress(); + + final NatPortMapping mapping = + natManager.getPortMapping(NatServiceType.DISCOVERY, NetworkProtocol.UDP); + + final NatPortMapping expectedMapping = + new NatPortMapping( + NatServiceType.DISCOVERY, + NetworkProtocol.UDP, + internalHost, + detectedAdvertisedHost, + p2pPort, + p2pPort); + + assertThat(mapping).isEqualToComparingFieldByField(expectedMapping); + } + + @Test + public void assertThatMappingForJsonRpcWorks() throws UnknownHostException { + final String internalHost = InetAddress.getLocalHost().getHostAddress(); + + final NatPortMapping mapping = + natManager.getPortMapping(NatServiceType.JSON_RPC, NetworkProtocol.TCP); + + final NatPortMapping expectedMapping = + new NatPortMapping( + NatServiceType.JSON_RPC, + NetworkProtocol.TCP, + internalHost, + detectedAdvertisedHost, + rpcHttpPort, + rpcHttpPort); + + assertThat(mapping).isEqualToComparingFieldByField(expectedMapping); + } + + @Test + public void assertThatMappingForRlpxWorks() throws UnknownHostException { + final String internalHost = InetAddress.getLocalHost().getHostAddress(); + + final NatPortMapping mapping = + natManager.getPortMapping(NatServiceType.RLPX, NetworkProtocol.TCP); + + final NatPortMapping expectedMapping = + new NatPortMapping( + NatServiceType.RLPX, + NetworkProtocol.TCP, + internalHost, + detectedAdvertisedHost, + p2pPort, + p2pPort); + + assertThat(mapping).isEqualToComparingFieldByField(expectedMapping); + } +} From 90d46b74458b87e9af09738821b7a9863e546a0c Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 19 Feb 2020 10:46:03 -0700 Subject: [PATCH 27/38] LogBloomCache - make sure the current segment is filled (#411) Make sure we cache the current cache segment with all of the data from the beginning of the segment. Use a flip file approach since it will be a partial file until done. Signed-off-by: Danno Ferrin Co-authored-by: Abdelhamid Bakhta <45264458+abdelhamidbakhta@users.noreply.github.com> --- .../api/query/TransactionLogBloomCacher.java | 80 ++++++++++++++----- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java index 4148ea61331..c8e6cc6286e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java @@ -27,14 +27,17 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.time.Duration; import java.util.Map; import java.util.Optional; import java.util.TreeMap; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -48,6 +51,7 @@ public class TransactionLogBloomCacher { public static final int BLOCKS_PER_BLOOM_CACHE = 100_000; private static final int BLOOM_BITS_LENGTH = 256; + public static final String CURRENT = "current"; private final Map cachedSegments; private final Lock submissionLock = new ReentrantLock(); @@ -67,7 +71,7 @@ public TransactionLogBloomCacher( this.cachedSegments = new TreeMap<>(); } - public void cacheAll() { + void cacheAll() { ensurePreviousSegmentsArePresent(blockchain.getChainHeadBlockNumber()); } @@ -83,7 +87,7 @@ public CachingStatus generateLogBloomCache(final long start, final long stop) { checkArgument( start % BLOCKS_PER_BLOOM_CACHE == 0, "Start block must be at the beginning of a file"); try { - cachingStatus.caching = true; + cachingStatus.cachingCount.incrementAndGet(); LOG.info( "Generating transaction log bloom cache from block {} to block {} in {}", start, @@ -101,21 +105,21 @@ public CachingStatus generateLogBloomCache(final long start, final long stop) { .ifPresent( blockHeader -> cacheLogsBloomForBlockHeader(blockHeader, Optional.of(cacheFile), false)); - try (final FileOutputStream fos = new FileOutputStream(cacheFile)) { - fillCacheFile(blockNum, blockNum + BLOCKS_PER_BLOOM_CACHE, fos); + try (final OutputStream os = new FileOutputStream(cacheFile)) { + fillCacheFile(blockNum, blockNum + BLOCKS_PER_BLOOM_CACHE, os); } } } catch (final Exception e) { LOG.error("Unhandled caching exception", e); } finally { - cachingStatus.caching = false; + cachingStatus.cachingCount.decrementAndGet(); LOG.info("Caching request complete"); } return cachingStatus; } - private void fillCacheFile( - final long startBlock, final long stopBlock, final FileOutputStream fos) throws IOException { + private void fillCacheFile(final long startBlock, final long stopBlock, final OutputStream fos) + throws IOException { long blockNum = startBlock; while (blockNum < stopBlock) { final Optional maybeHeader = blockchain.getBlockHeader(blockNum); @@ -128,27 +132,63 @@ private void fillCacheFile( } } - public void cacheLogsBloomForBlockHeader( + void cacheLogsBloomForBlockHeader( final BlockHeader blockHeader, final Optional reusedCacheFile, final boolean ensureChecks) { try { + if (cachingStatus.cachingCount.incrementAndGet() != 1) { + return; + } final long blockNumber = blockHeader.getNumber(); LOG.debug("Caching logs bloom for block {}.", "0x" + Long.toHexString(blockNumber)); if (ensureChecks) { ensurePreviousSegmentsArePresent(blockNumber); } final File cacheFile = reusedCacheFile.orElse(calculateCacheFileName(blockNumber, cacheDir)); - if (!cacheFile.exists()) { - Files.createFile(cacheFile.toPath()); + if (cacheFile.exists()) { + cacheSingleBlock(blockHeader, cacheFile); + } else { + scheduler.scheduleComputationTask(this::populateLatestSegment); + } + } catch (final IOException e) { + LOG.error("Unhandled caching exception.", e); + } finally { + cachingStatus.cachingCount.decrementAndGet(); + } + } + + private void cacheSingleBlock(final BlockHeader blockHeader, final File cacheFile) + throws IOException { + try (final RandomAccessFile writer = new RandomAccessFile(cacheFile, "rw")) { + final long offset = (blockHeader.getNumber() % BLOCKS_PER_BLOOM_CACHE) * BLOOM_BITS_LENGTH; + writer.seek(offset); + writer.write(ensureBloomBitsAreCorrectLength(blockHeader.getLogsBloom().toArray())); + } + } + + private boolean populateLatestSegment() { + try { + long blockNumber = blockchain.getChainHeadBlockNumber(); + final File currentFile = calculateCacheFileName(CURRENT, cacheDir); + final long segmentNumber = blockNumber / BLOCKS_PER_BLOOM_CACHE; + try (final OutputStream out = new FileOutputStream(currentFile)) { + fillCacheFile(segmentNumber * BLOCKS_PER_BLOOM_CACHE, blockNumber, out); } - try (RandomAccessFile writer = new RandomAccessFile(cacheFile, "rw")) { - final long offset = (blockNumber / BLOCKS_PER_BLOOM_CACHE) * BLOOM_BITS_LENGTH; - writer.seek(offset); - writer.write(ensureBloomBitsAreCorrectLength(blockHeader.getLogsBloom().toArray())); + while (blockNumber <= blockchain.getChainHeadBlockNumber() + && (blockNumber % BLOCKS_PER_BLOOM_CACHE != 0)) { + cacheSingleBlock(blockchain.getBlockHeader(blockNumber).orElseThrow(), currentFile); + blockNumber++; } - } catch (IOException e) { + Files.move( + currentFile.toPath(), + calculateCacheFileName(blockNumber, cacheDir).toPath(), + StandardCopyOption.REPLACE_EXISTING, + StandardCopyOption.ATOMIC_MOVE); + return true; + } catch (final IOException e) { LOG.error("Unhandled caching exception.", e); + return false; } } @@ -173,7 +213,7 @@ private void ensurePreviousSegmentsArePresent(final long blockNumber) { } } - private void fillCacheFileWithBlock(final BlockHeader blockHeader, final FileOutputStream fos) + private void fillCacheFileWithBlock(final BlockHeader blockHeader, final OutputStream fos) throws IOException { fos.write(ensureBloomBitsAreCorrectLength(blockHeader.getLogsBloom().toArray())); } @@ -189,7 +229,7 @@ public CachingStatus requestCaching(final long fromBlock, final long toBlock) { try { if ((fromBlock < toBlock) && submissionLock.tryLock(100, TimeUnit.MILLISECONDS)) { try { - if (!cachingStatus.caching) { + if (!cachingStatus.isCaching()) { requestAccepted = true; cachingStatus.startBlock = fromBlock; cachingStatus.endBlock = toBlock; @@ -209,7 +249,7 @@ public CachingStatus requestCaching(final long fromBlock, final long toBlock) { return cachingStatus; } - public EthScheduler getScheduler() { + EthScheduler getScheduler() { return scheduler; } @@ -221,7 +261,7 @@ public static final class CachingStatus { long startBlock; long endBlock; volatile long currentBlock; - volatile boolean caching; + AtomicInteger cachingCount = new AtomicInteger(0); boolean requestAccepted; @JsonGetter @@ -241,7 +281,7 @@ public String getCurrentBlock() { @JsonGetter public boolean isCaching() { - return caching; + return cachingCount.get() > 0; } @JsonGetter From e28396a5dcb8cff094fc1d5b358cb46d8e8693fe Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 19 Feb 2020 11:22:31 -0700 Subject: [PATCH 28/38] Reduce recaching in Transaction Log Bloom Filter Cache (#415) Do a cursory cache check at start up (file is present and correct size) instead of re-generating the cache at startup. Signed-off-by: Danno Ferrin --- .../besu/ethereum/api/query/TransactionLogBloomCacher.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java index c8e6cc6286e..bafbf05fdf4 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/query/TransactionLogBloomCacher.java @@ -51,6 +51,7 @@ public class TransactionLogBloomCacher { public static final int BLOCKS_PER_BLOOM_CACHE = 100_000; private static final int BLOOM_BITS_LENGTH = 256; + private static final int EXPECTED_BLOOM_FILE_SIZE = BLOCKS_PER_BLOOM_CACHE * BLOOM_BITS_LENGTH; public static final String CURRENT = "current"; private final Map cachedSegments; @@ -201,7 +202,10 @@ private void ensurePreviousSegmentsArePresent(final long blockNumber) { try { if (!cachedSegments.getOrDefault(currentSegment, false)) { final long startBlock = currentSegment * BLOCKS_PER_BLOOM_CACHE; - generateLogBloomCache(startBlock, startBlock + BLOCKS_PER_BLOOM_CACHE); + final File cacheFile = calculateCacheFileName(startBlock, cacheDir); + if (!cacheFile.isFile() || cacheFile.length() != EXPECTED_BLOOM_FILE_SIZE) { + generateLogBloomCache(startBlock, startBlock + BLOCKS_PER_BLOOM_CACHE); + } cachedSegments.put(currentSegment, true); } } finally { From 145710abf89edde2b58590c6dfa0861efd0e3a25 Mon Sep 17 00:00:00 2001 From: Karim T Date: Thu, 20 Feb 2020 09:36:52 +0100 Subject: [PATCH 29/38] fix order of nat detector (#414) Signed-off-by: Karim TAAM --- besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java index c7abf140faa..fed85a89ac3 100644 --- a/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java @@ -597,7 +597,7 @@ private Optional buildNatManager(final NatMethod natMethod) { final NatMethod detectedNatMethod = Optional.of(natMethod) .filter(not(isEqual(NatMethod.AUTO))) - .orElse(NatService.autoDetectNatMethod(new DockerDetector(), new KubernetesDetector())); + .orElse(NatService.autoDetectNatMethod(new KubernetesDetector(), new DockerDetector())); switch (detectedNatMethod) { case UPNP: return Optional.of(new UpnpNatManager()); From 3603f8d14df04434e4477314f51c343d662cc32b Mon Sep 17 00:00:00 2001 From: Edward Mack Date: Thu, 20 Feb 2020 11:09:32 -0500 Subject: [PATCH 30/38] Update SLOAD_GAS cost to 200 in Aztlan Gas Calculator (#23) (#382) * Update SLOAD_GAS cost to 200 in Aztlan Gas Calculator Change SLOAD_GAS cost in Aztlan Gas Calculator from 800 to 200 and update functions that use SLOAD_GAS. Signed-off-by: edwardmack * Update SLOAD_GAS cost to 200 in Aztlan Gas Calculator Change SLOAD_GAS cost in Aztlan Gas Calculator from 800 to 200 and update functions that use SLOAD_GAS. Signed-off-by: edwardmack Signed-off-by: Edward Mack * remove overrides removed overrides of calculateStorageCost and calculateStorageRefundAmount in AztlanGasCalculator because these were causing aztlan fork not to sync with kotti testnet. Signed-off-by: Edward Mack * merge Signed-off-by: Edward Mack --- .../besu/ethereum/mainnet/AztlanGasCalculator.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AztlanGasCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AztlanGasCalculator.java index d5690d4ab23..0c7db03a9dc 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AztlanGasCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AztlanGasCalculator.java @@ -23,6 +23,13 @@ public class AztlanGasCalculator extends IstanbulGasCalculator { private static final Gas BALANCE_OPERATION_GAS_COST = Gas.of(400); private static final Gas EXTCODE_HASH_COST = Gas.of(400); + private static final Gas SLOAD_GAS = Gas.of(200); + + @Override + // As per https://eips.ethereum.org/EIPS/eip-1884 + public Gas getSloadOperationGasCost() { + return SLOAD_GAS; + } @Override public Gas getBalanceOperationGasCost() { From 0a19f4750f5be2ebb81dfd2949993f65cc77fc9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Horacio=20Mijail=20Ant=C3=B3n=20Quiles?= <4139546+hmijail@users.noreply.github.com> Date: Sat, 22 Feb 2020 10:39:59 +0800 Subject: [PATCH 31/38] Remove unused ExecutorService init/termination (#419) Signed-off-by: Horacio Mijail Anton Quiles Co-authored-by: CJ Hare --- .../acceptance/dsl/node/ThreadBesuNodeRunner.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index a73779aee6f..b1445580a88 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -53,9 +53,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import io.vertx.core.Vertx; @@ -68,7 +65,6 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner { private final Logger LOG = LogManager.getLogger(); private final Map besuRunners = new HashMap<>(); - private ExecutorService nodeExecutor = Executors.newCachedThreadPool(); private final Map besuPluginContextMap = new HashMap<>(); @@ -102,9 +98,6 @@ private BesuPluginContextImpl buildPluginContext( @Override public void startNode(final BesuNode node) { - if (nodeExecutor == null || nodeExecutor.isShutdown()) { - nodeExecutor = Executors.newCachedThreadPool(); - } final StorageServiceImpl storageService = new StorageServiceImpl(); final Path dataDir = node.homeDirectory(); @@ -221,14 +214,6 @@ public void shutdown() { // iterate over a copy of the set so that besuRunner can be updated when a runner is killed new HashSet<>(besuRunners.keySet()).forEach(this::killRunner); - try { - nodeExecutor.shutdownNow(); - if (!nodeExecutor.awaitTermination(5, TimeUnit.SECONDS)) { - throw new IllegalStateException("Failed to shut down node executor"); - } - } catch (final InterruptedException e) { - throw new RuntimeException(e); - } } @Override From 7fe1d4796e52fa1ce6099f402cc05f1165607335 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Sat, 22 Feb 2020 09:30:53 -0700 Subject: [PATCH 32/38] Implement Eth/64 (#425) Wire in the fork identifier into the status messages as Eth64. Signed-off-by: Danno Ferrin --- .../besu/ethereum/eth/EthProtocol.java | 2 + .../eth/manager/EthProtocolManager.java | 42 +-- .../ethereum/eth/manager/ForkIdManager.java | 126 ++----- .../ethereum/eth/messages/StatusMessage.java | 11 +- .../eth/manager/EthProtocolManagerTest.java | 2 +- .../manager/EthProtocolManagerTestUtil.java | 2 +- .../eth/manager/ForkIdManagerTest.java | 316 +++++++++++------- .../eth/messages/StatusMessageTest.java | 4 +- 8 files changed, 244 insertions(+), 261 deletions(-) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/EthProtocol.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/EthProtocol.java index 775aafca359..05239e3038f 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/EthProtocol.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/EthProtocol.java @@ -64,6 +64,7 @@ public int messageSpace(final int protocolVersion) { case EthVersion.V62: return 8; case EthVersion.V63: + case EthVersion.V64: return 17; default: return 0; @@ -76,6 +77,7 @@ public boolean isValidMessageCode(final int protocolVersion, final int code) { case EthVersion.V62: return eth62Messages.contains(code); case EthVersion.V63: + case EthVersion.V64: return eth63Messages.contains(code); default: return false; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java index 3f7b9ce1e21..94c35fbbd69 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; +import org.hyperledger.besu.ethereum.eth.manager.ForkIdManager.ForkId; import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.StatusMessage; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; @@ -41,21 +42,21 @@ import java.math.BigInteger; import java.time.Clock; -import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; +import com.google.common.annotations.VisibleForTesting; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class EthProtocolManager implements ProtocolManager, MinedBlockObserver { private static final Logger LOG = LogManager.getLogger(); private static final List FAST_SYNC_CAPS = - Collections.singletonList(EthProtocol.ETH63); + List.of(EthProtocol.ETH63, EthProtocol.ETH64); private static final List FULL_SYNC_CAPS = - Arrays.asList(EthProtocol.ETH62, EthProtocol.ETH63); + List.of(EthProtocol.ETH62, EthProtocol.ETH63, EthProtocol.ETH64); private final EthScheduler scheduler; private final CountDownLatch shutdown; @@ -93,7 +94,7 @@ public EthProtocolManager( this.shutdown = new CountDownLatch(1); genesisHash = blockchain.getBlockHashByNumber(0L).get(); - this.forkIdManager = ForkIdManager.buildCollection(genesisHash); + this.forkIdManager = forkIdManager; ethPeers = new EthPeers(getSupportedProtocol(), clock, metricsSystem); ethMessages = new EthMessages(); @@ -110,30 +111,7 @@ public EthProtocolManager( new EthServer(blockchain, worldStateArchive, ethMessages, ethereumWireProtocolConfiguration); } - public EthProtocolManager( - final Blockchain blockchain, - final WorldStateArchive worldStateArchive, - final BigInteger networkId, - final List peerValidators, - final boolean fastSyncEnabled, - final int syncWorkers, - final int txWorkers, - final int computationWorkers, - final Clock clock, - final MetricsSystem metricsSystem) { - this( - blockchain, - worldStateArchive, - networkId, - peerValidators, - fastSyncEnabled, - new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem), - EthProtocolConfiguration.defaultConfig(), - clock, - metricsSystem, - ForkIdManager.buildCollection(blockchain.getBlockHashByNumber(0L).get())); - } - + @VisibleForTesting public EthProtocolManager( final Blockchain blockchain, final WorldStateArchive worldStateArchive, @@ -156,7 +134,7 @@ public EthProtocolManager( ethereumWireProtocolConfiguration, clock, metricsSystem, - ForkIdManager.buildCollection(blockchain.getBlockHashByNumber(0L).get())); + new ForkIdManager(blockchain, Collections.emptyList())); } public EthProtocolManager( @@ -182,8 +160,7 @@ public EthProtocolManager( ethereumWireProtocolConfiguration, clock, metricsSystem, - ForkIdManager.buildCollection( - blockchain.getBlockHashByNumber(0L).get(), forks, blockchain)); + new ForkIdManager(blockchain, forks)); } public EthContext ethContext() { @@ -274,6 +251,7 @@ public void handleNewConnection(final PeerConnection connection) { } final Capability cap = connection.capability(getSupportedProtocol()); + final ForkId latestForkId = cap.getVersion() >= 64 ? forkIdManager.getLatestForkId() : null; // TODO: look to consolidate code below if possible // making status non-final and implementing it above would be one way. final StatusMessage status = @@ -283,7 +261,7 @@ public void handleNewConnection(final PeerConnection connection) { blockchain.getChainHead().getTotalDifficulty(), blockchain.getChainHeadHash(), genesisHash, - forkIdManager.getLatestForkId()); + latestForkId); try { LOG.debug("Sending status message to {}.", peer); peer.send(status); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManager.java index b1b7b73ff3b..464ec173d0f 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManager.java @@ -14,8 +14,6 @@ */ package org.hyperledger.besu.ethereum.eth.manager; -import static java.util.Collections.emptyList; - import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; @@ -27,52 +25,28 @@ import java.util.stream.Collectors; import java.util.zip.CRC32; -import com.google.common.annotations.VisibleForTesting; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; public class ForkIdManager { + private final Blockchain blockchain; private final Hash genesisHash; - private final Long currentHead; - private Long forkNext; - private final Long highestKnownFork = 0L; - private final List forkAndHashList; - - public ForkIdManager(final Hash genesisHash, final List forks, final Long currentHead) { - this.genesisHash = genesisHash; - this.currentHead = currentHead; - this.forkAndHashList = - createForkIds( - // If there are two forks at the same block height, we only want to add it once to the - // crc checksum - forks.stream().distinct().collect(Collectors.toUnmodifiableList())); - }; - - static ForkIdManager buildCollection( - final Hash genesisHash, final List forks, final Blockchain blockchain) { - return new ForkIdManager(genesisHash, forks, blockchain.getChainHeadBlockNumber()); - }; - - @VisibleForTesting - public static ForkIdManager buildCollection(final Hash genesisHash, final List forks) { - return new ForkIdManager(genesisHash, forks, Long.MAX_VALUE); - }; - - static ForkIdManager buildCollection(final Hash genesisHash) { - return new ForkIdManager(genesisHash, emptyList(), Long.MAX_VALUE); + private final List forks; + private long forkNext; + private final long highestKnownFork; + private List forkAndHashList; + + public ForkIdManager(final Blockchain blockchain, final List forks) { + this.blockchain = blockchain; + this.genesisHash = blockchain.getGenesisBlock().getHash(); + // de-dupe and sanitize forks + this.forks = + forks.stream().filter(fork -> fork > 0).distinct().collect(Collectors.toUnmodifiableList()); + highestKnownFork = forks.size() > 0 ? forks.get(forks.size() - 1) : 0L; + createForkIds(); }; - // Non-generated entry (for tests) - public static ForkId createIdEntry(final String hash, final long next) { - return new ForkId(hash, next); - } - - // Non-generated entry (for tests) - public static ForkId createIdEntry(final String hash, final String next) { - return new ForkId(hash, next); - } - public List getForkAndHashList() { return this.forkAndHashList; } @@ -98,11 +72,11 @@ public static ForkId readFrom(final RLPInput in) { * @param forkId to be validated. * @return boolean (peer valid (true) or invalid (false)) */ - public boolean peerCheck(final ForkId forkId) { + boolean peerCheck(final ForkId forkId) { if (forkId == null) { return true; // Another method must be used to validate (i.e. genesis hash) } - // Run the fork checksum validation ruleset: + // Run the fork checksum validation rule set: // 1. If local and remote FORK_CSUM matches, connect. // The two nodes are in the same fork state currently. They might know // of differing future forks, but that's not relevant until the fork @@ -119,7 +93,7 @@ public boolean peerCheck(final ForkId forkId) { // information. // 4. Reject in all other cases. if (isHashKnown(forkId.getHash())) { - if (currentHead < forkNext) { + if (blockchain.getChainHeadBlockNumber() < forkNext) { return true; } else { if (isForkKnown(forkId.getNext())) { @@ -153,7 +127,7 @@ private boolean isForkKnown(final Long nextFork) { } private boolean isRemoteAwareOfPresent(final Bytes forkHash, final Long nextFork) { - for (ForkId j : forkAndHashList) { + for (final ForkId j : forkAndHashList) { if (forkHash.equals(j.getHash())) { if (nextFork.equals(j.getNext())) { return true; @@ -167,9 +141,9 @@ private boolean isRemoteAwareOfPresent(final Bytes forkHash, final Long nextFork return false; } - private List createForkIds(final List forks) { + private void createForkIds() { final CRC32 crc = new CRC32(); - crc.update(this.genesisHash.toArray()); + crc.update(genesisHash.toArray()); final List forkHashes = new ArrayList<>(List.of(getCurrentCrcHash(crc))); for (final Long fork : forks) { updateCrc(crc, fork); @@ -181,18 +155,14 @@ private List createForkIds(final List forks) { forkIds.add(new ForkId(forkHashes.get(i), forks.get(i))); } if (!forks.isEmpty()) { - this.forkNext = forkIds.get(forkIds.size() - 1).getNext(); - forkIds.add( - new ForkId( - forkHashes.get(forkHashes.size() - 1), - currentHead > forkNext ? 0 : forkNext // Use 0 if there are no known next forks - )); + forkNext = forkIds.get(forkIds.size() - 1).getNext(); + forkIds.add(new ForkId(forkHashes.get(forkHashes.size() - 1), 0)); } - return forkIds; + this.forkAndHashList = forkIds; } private void updateCrc(final CRC32 crc, final Long block) { - byte[] byteRepresentationFork = longToBigEndian(block); + final byte[] byteRepresentationFork = longToBigEndian(block); crc.update(byteRepresentationFork, 0, byteRepresentationFork.length); } @@ -205,35 +175,14 @@ public static class ForkId { final Bytes next; Bytes forkIdRLP; - ForkId(final Bytes hash, final Bytes next) { + private ForkId(final Bytes hash, final Bytes next) { this.hash = hash; this.next = next; createForkIdRLP(); } - ForkId(final String hash, final String next) { - this.hash = padToEightBytes(Bytes.fromHexString((hash.length() % 2 == 0 ? "" : "0") + hash)); - if (next.equals("") || next.equals("0x")) { - this.next = Bytes.EMPTY; - } else if (next.startsWith("0x")) { - long asLong = Long.parseLong(next.replaceFirst("0x", ""), 16); - this.next = Bytes.wrap(longToBigEndian(asLong)).trimLeadingZeros(); - } else { - this.next = Bytes.wrap(longToBigEndian(Long.parseLong(next))); - } - createForkIdRLP(); - } - - ForkId(final String hash, final long next) { - this.hash = Bytes.fromHexString(hash); - this.next = Bytes.wrap(longToBigEndian(next)); - createForkIdRLP(); - } - - ForkId(final Bytes hash, final long next) { - this.hash = hash; - this.next = Bytes.wrap(longToBigEndian(next)); - createForkIdRLP(); + public ForkId(final Bytes hash, final long next) { + this(hash, Bytes.wrap(longToBigEndian(next)).trimLeadingZeros()); } public long getNext() { @@ -245,7 +194,7 @@ public Bytes getHash() { } void createForkIdRLP() { - BytesValueRLPOutput out = new BytesValueRLPOutput(); + final BytesValueRLPOutput out = new BytesValueRLPOutput(); writeTo(out); forkIdRLP = out.encoded(); } @@ -260,26 +209,17 @@ public void writeTo(final RLPOutput out) { public static ForkId readFrom(final RLPInput in) { in.enterList(); final Bytes hash = in.readBytes(); - final long next = in.readLong(); + final long next = in.readLongScalar(); in.leaveList(); return new ForkId(hash, next); } public List asList() { - ArrayList forRLP = new ArrayList<>(); + final ArrayList forRLP = new ArrayList<>(); forRLP.add(this); return forRLP; } - private static Bytes padToEightBytes(final Bytes hash) { - if (hash.size() < 4) { - Bytes padded = Bytes.concatenate(hash, Bytes.fromHexString("0x00")); - return padToEightBytes(padded); - } else { - return hash; - } - } - @Override public String toString() { return "ForkId(hash=" + this.hash + ", next=" + next.toLong() + ")"; @@ -288,8 +228,8 @@ public String toString() { @Override public boolean equals(final Object obj) { if (obj instanceof ForkId) { - ForkId other = (ForkId) obj; - long thisNext = next.toLong(); + final ForkId other = (ForkId) obj; + final long thisNext = next.toLong(); return other.getHash().equals(this.hash) && thisNext == other.getNext(); } return false; @@ -304,7 +244,7 @@ public int hashCode() { // next two methods adopted from: // https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/util/Pack.java private static byte[] longToBigEndian(final long n) { - byte[] bs = new byte[8]; + final byte[] bs = new byte[8]; intToBigEndian((int) (n >>> 32), bs, 0); intToBigEndian((int) (n & 0xffffffffL), bs, 4); return bs; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessage.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessage.java index 80050ecd921..c3847878cff 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessage.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessage.java @@ -182,15 +182,16 @@ public static EthStatus readFrom(final RLPInput in) { final Difficulty totalDifficulty = Difficulty.of(in.readUInt256Scalar()); final Hash bestHash = Hash.wrap(in.readBytes32()); final Hash genesisHash = Hash.wrap(in.readBytes32()); + final ForkIdManager.ForkId forkId; if (in.nextIsList()) { - final ForkIdManager.ForkId forkId = ForkIdManager.ForkId.readFrom(in); - in.leaveList(); - return new EthStatus( - protocolVersion, networkId, totalDifficulty, bestHash, genesisHash, forkId); + forkId = ForkIdManager.ForkId.readFrom(in); + } else { + forkId = null; } in.leaveList(); - return new EthStatus(protocolVersion, networkId, totalDifficulty, bestHash, genesisHash); + return new EthStatus( + protocolVersion, networkId, totalDifficulty, bestHash, genesisHash, forkId); } } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java index b2d70f21cac..e9666a486b8 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java @@ -1094,7 +1094,7 @@ public void transactionMessagesGoToTheCorrectExecutor() { EthProtocolConfiguration.defaultConfig(), TestClock.fixed(), metricsSystem, - ForkIdManager.buildCollection(blockchain.getBlockHashByNumber(0L).get()))) { + new ForkIdManager(blockchain, Collections.emptyList()))) { // Create a transaction pool. This has a side effect of registering a listener for the // transactions message. diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java index 15265a68a07..2ecba9f725f 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java @@ -63,7 +63,7 @@ public static EthProtocolManager create( EthProtocolConfiguration.defaultConfig(), TestClock.fixed(), new NoOpMetricsSystem(), - ForkIdManager.buildCollection(blockchain.getBlockHashByNumber(0L).get())); + new ForkIdManager(blockchain, Collections.emptyList())); } public static EthProtocolManager create( diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManagerTest.java index 13ad8e1c7c9..51c047891b4 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ForkIdManagerTest.java @@ -15,7 +15,12 @@ package org.hyperledger.besu.ethereum.eth.manager; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Hash; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; @@ -32,86 +37,123 @@ public class ForkIdManagerTest { private final String mainnetGenHash = "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"; + private Blockchain mockBlockchain(final String genesisHash, final long chainHeight) { + final Blockchain mockchain = mock(Blockchain.class); + final BlockHeader mockHeader = mock(BlockHeader.class); + final Block block = new Block(mockHeader, null); + when(mockchain.getGenesisBlock()).thenReturn(block); + when(mockchain.getChainHeadBlockNumber()).thenReturn(chainHeight); + when(mockHeader.getHash()).thenReturn(Hash.fromHexString(genesisHash)); + return mockchain; + } + @Test public void checkItFunctionsWithPresentBehavior() { - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), Collections.emptyList(), null); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 0), Collections.emptyList()); assertThat(forkIdManager.peerCheck(Hash.fromHexString(mainnetGenHash))).isFalse(); assertThat(forkIdManager.getLatestForkId()).isNull(); } @Test public void checkCorrectMainnetForkIdHashesGenerated() { - ForkIdManager.ForkId[] checkIds = { - ForkIdManager.createIdEntry("0xfc64ec04", 1150000L), // Unsynced - ForkIdManager.createIdEntry("0x97c2c34c", 1920000L), // First Homestead block - ForkIdManager.createIdEntry("0x91d1f948", 2463000L), // First DAO block - ForkIdManager.createIdEntry("0x7a64da13", 2675000L), // First Tangerine block - ForkIdManager.createIdEntry("0x3edd5b10", 4370000L), // First Spurious block - ForkIdManager.createIdEntry("0xa00bc324", 7280000L), // First Byzantium block - ForkIdManager.createIdEntry("0x668db0af", 0L) // Today Petersburg block + final ForkIdManager.ForkId[] checkIds = { + new ForkIdManager.ForkId(Bytes.fromHexString("0xfc64ec04"), 1150000L), // Unsynced + new ForkIdManager.ForkId( + Bytes.fromHexString("0x97c2c34c"), 1920000L), // First Homestead block + new ForkIdManager.ForkId(Bytes.fromHexString("0x91d1f948"), 2463000L), // First DAO block + new ForkIdManager.ForkId( + Bytes.fromHexString("0x7a64da13"), 2675000L), // First Tangerine block + new ForkIdManager.ForkId(Bytes.fromHexString("0x3edd5b10"), 4370000L), // First Spurious block + new ForkIdManager.ForkId( + Bytes.fromHexString("0xa00bc324"), 7280000L), // First Byzantium block + new ForkIdManager.ForkId(Bytes.fromHexString("0x668db0af"), 0L) // Today Petersburg block }; - List list = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - ForkIdManager.buildCollection(Hash.fromHexString(mainnetGenHash), list); - List entries = forkIdManager.getForkAndHashList(); + final List list = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = new ForkIdManager(mockBlockchain(mainnetGenHash, 0), list); + final List entries = forkIdManager.getForkAndHashList(); assertThat(entries).containsExactly(checkIds); assertThat(forkIdManager.getLatestForkId()).isNotNull(); - assertThat(forkIdManager.getLatestForkId().equals(checkIds[6])).isTrue(); + assertThat(forkIdManager.getLatestForkId()).isEqualTo(checkIds[6]); } @Test public void checkCorrectRopstenForkIdHashesGenerated() { - Long[] forks = {10L, 1700000L, 4230000L, 4939394L}; - String genHash = "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"; - ForkIdManager.ForkId[] checkIds = { - ForkIdManager.createIdEntry( - "0x30c7ddbc", 10L), // Unsynced, last Frontier, Homestead and first Tangerine block - ForkIdManager.createIdEntry("0x63760190", 1700000L), // First Spurious block - ForkIdManager.createIdEntry("0x3ea159c7", 4230000L), // First Byzantium block - ForkIdManager.createIdEntry("0x97b544f3", 4939394L), // First Constantinople block - ForkIdManager.createIdEntry("0xd6e2149b", 0L) // Today Petersburg block + final Long[] forks = {10L, 1700000L, 4230000L, 4939394L}; + final String genHash = "0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"; + final ForkIdManager.ForkId[] checkIds = { + new ForkIdManager.ForkId( + Bytes.fromHexString("0x30c7ddbc"), + 10L), // Unsynced, last Frontier, Homestead and first Tangerine block + new ForkIdManager.ForkId(Bytes.fromHexString("0x63760190"), 1700000L), // First Spurious block + new ForkIdManager.ForkId( + Bytes.fromHexString("0x3ea159c7"), 4230000L), // First Byzantium block + new ForkIdManager.ForkId( + Bytes.fromHexString("0x97b544f3"), 4939394L), // First Constantinople block + new ForkIdManager.ForkId(Bytes.fromHexString("0xd6e2149b"), 0L) // Today Petersburg block }; - List list = Arrays.asList(forks); - ForkIdManager forkIdManager = ForkIdManager.buildCollection(Hash.fromHexString(genHash), list); - List entries = forkIdManager.getForkAndHashList(); + final List list = Arrays.asList(forks); + final ForkIdManager forkIdManager = new ForkIdManager(mockBlockchain(genHash, 0), list); + final List entries = forkIdManager.getForkAndHashList(); assertThat(entries).containsExactly(checkIds); assertThat(forkIdManager.getLatestForkId()).isNotNull(); - assertThat(forkIdManager.getLatestForkId().equals(checkIds[4])).isTrue(); + assertThat(forkIdManager.getLatestForkId()).isEqualTo(checkIds[4]); } @Test public void checkCorrectRinkebyForkIdHashesGenerated() { - Long[] forks = {1L, 2L, 3L, 1035301L, 3660663L, 4321234L}; - String genHash = "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177"; - ForkIdManager.ForkId[] checkIds = { - ForkIdManager.createIdEntry( - "0x3b8e0691", 1L), // Unsynced, last Frontier, Homestead and first Tangerine block - ForkIdManager.createIdEntry("0x60949295", 2L), // Last Tangerine block - ForkIdManager.createIdEntry("0x8bde40dd", 3L), // First Spurious block - ForkIdManager.createIdEntry("0xcb3a64bb", 1035301L), // First Byzantium block - ForkIdManager.createIdEntry("0x8d748b57", 3660663L), // First Constantinople block - ForkIdManager.createIdEntry("0xe49cab14", 4321234L), // First Petersburg block - ForkIdManager.createIdEntry("0xafec6b27", 0L) // Today Petersburg block + final Long[] forks = {1L, 2L, 3L, 1035301L, 3660663L, 4321234L}; + final String genHash = "0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177"; + final ForkIdManager.ForkId[] checkIds = { + new ForkIdManager.ForkId( + Bytes.fromHexString("0x3b8e0691"), + 1L), // Unsynced, last Frontier, Homestead and first Tangerine block + new ForkIdManager.ForkId(Bytes.fromHexString("0x60949295"), 2L), // Last Tangerine block + new ForkIdManager.ForkId(Bytes.fromHexString("0x8bde40dd"), 3L), // First Spurious block + new ForkIdManager.ForkId( + Bytes.fromHexString("0xcb3a64bb"), 1035301L), // First Byzantium block + new ForkIdManager.ForkId( + Bytes.fromHexString("0x8d748b57"), 3660663L), // First Constantinople block + new ForkIdManager.ForkId( + Bytes.fromHexString("0xe49cab14"), 4321234L), // First Petersburg block + new ForkIdManager.ForkId(Bytes.fromHexString("0xafec6b27"), 0L) // Today Petersburg block + }; + final List list = Arrays.asList(forks); + final ForkIdManager forkIdManager = new ForkIdManager(mockBlockchain(genHash, 0), list); + final List entries = forkIdManager.getForkAndHashList(); + + assertThat(entries).containsExactly(checkIds); + assertThat(forkIdManager.getLatestForkId()).isNotNull(); + assertThat(forkIdManager.getLatestForkId()).isEqualTo(checkIds[6]); + } + + @Test + public void checkCorrectGoerliForkIdHashesGenerated() { + final Long[] forks = {1561651L}; + final String genHash = "0xbf7e331f7f7c1dd2e05159666b3bf8bc7a8a3a9eb1d518969eab529dd9b88c1a"; + final ForkIdManager.ForkId[] checkIds = { + new ForkIdManager.ForkId(Bytes.fromHexString("0xa3f5ab08"), 1561651L), // Frontier->Petersburg + new ForkIdManager.ForkId(Bytes.fromHexString("0xc25efa5c"), 0L) // Istanbul }; - List list = Arrays.asList(forks); - ForkIdManager forkIdManager = ForkIdManager.buildCollection(Hash.fromHexString(genHash), list); - List entries = forkIdManager.getForkAndHashList(); + final List list = Arrays.asList(forks); + final ForkIdManager forkIdManager = new ForkIdManager(mockBlockchain(genHash, 0), list); + final List entries = forkIdManager.getForkAndHashList(); assertThat(entries).containsExactly(checkIds); assertThat(forkIdManager.getLatestForkId()).isNotNull(); - assertThat(forkIdManager.getLatestForkId().equals(checkIds[6])).isTrue(); + assertThat(forkIdManager.getLatestForkId()).isEqualTo(checkIds[1]); } @Test public void check1PetersburgWithRemoteAnnouncingTheSame() { // 1 Local is mainnet Petersburg, remote announces the same. No future fork is announced. // {7987396, ID{Hash: 0x668db0af, Next: 0}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7987396L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0x668db0af", 0L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7987396L), forkList); + final Boolean result = + forkIdManager.peerCheck(new ForkIdManager.ForkId(Bytes.fromHexString("0x668db0af"), 0L)); assertThat(result).isTrue(); assertThat(forkIdManager.getLatestForkId()).isNotNull(); } @@ -121,11 +163,12 @@ public void check2PetersburgWithRemoteAnnouncingTheSameAndNextFork() { // 2 Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork // at block 0xffffffff, but that is uncertain. // {7987396, ID{Hash: 0x668db0af, Next: math.MaxUint64}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7987396L); - Boolean result = - forkIdManager.peerCheck(ForkIdManager.createIdEntry("0x668db0af", Long.MAX_VALUE)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7987396L), forkList); + final Boolean result = + forkIdManager.peerCheck( + new ForkIdManager.ForkId(Bytes.fromHexString("0x668db0af"), Long.MAX_VALUE)); assertThat(result).isTrue(); } @@ -136,10 +179,11 @@ public void check3ByzantiumAwareOfPetersburgRemoteUnawareOfPetersburg() { // the fork). // In this case we don't know if Petersburg passed yet or not. // {7279999, ID{Hash: 0xa00bc324, Next: 0}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7279999L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0xa00bc324", 0L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7279999L), forkList); + final Boolean result = + forkIdManager.peerCheck(new ForkIdManager.ForkId(Bytes.fromHexString("0xa00bc324"), 0L)); assertThat(result).isTrue(); } @@ -149,10 +193,12 @@ public void check4ByzantiumAwareOfPetersburgRemoteAwareOfPetersburg() { // announces also Byzantium, and it's also aware of Petersburg (e.g. updated node before the // fork). We don't know if Petersburg passed yet (will pass) or not. // {7279999, ID{Hash: 0xa00bc324, Next: 7280000}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7279999L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0xa00bc324", 7280000L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7987396L), forkList); + final Boolean result = + forkIdManager.peerCheck( + new ForkIdManager.ForkId(Bytes.fromHexString("0xa00bc324"), 7280000L)); assertThat(result).isTrue(); } @@ -163,11 +209,12 @@ public void check5ByzantiumAwareOfPetersburgRemoteAnnouncingUnknownFork() { // Petersburg). // As neither forks passed at neither nodes, they may mismatch, but we still connect for now. // {7279999, ID{Hash: 0xa00bc324, Next: math.MaxUint64}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7279999L); - Boolean result = - forkIdManager.peerCheck(ForkIdManager.createIdEntry("0xa00bc324", Long.MAX_VALUE)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7279999), forkList); + final Boolean result = + forkIdManager.peerCheck( + new ForkIdManager.ForkId(Bytes.fromHexString("0xa00bc324"), Long.MAX_VALUE)); assertThat(result).isTrue(); } @@ -176,10 +223,12 @@ public void check6PetersburgWithRemoteAnnouncingByzantiumAwareOfPetersburg() { // 6 Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. // Remote is simply out of sync, accept. // {7987396, ID{Hash: 0x668db0af, Next: 7280000}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7987396L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0x668db0af", 7280000L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7987396L), forkList); + final Boolean result = + forkIdManager.peerCheck( + new ForkIdManager.ForkId(Bytes.fromHexString("0x668db0af"), 7280000L)); assertThat(result).isTrue(); } @@ -189,10 +238,12 @@ public void check7PetersburgWithRemoteAnnouncingSpuriousAwareOfByzantiumRemoteMa // Remote is definitely out of sync. It may or may not need the Petersburg update, we don't know // yet. // {7987396, ID{Hash: 0x3edd5b10, Next: 4370000}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7987396L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0x3edd5b10", 4370000L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7987396L), forkList); + final Boolean result = + forkIdManager.peerCheck( + new ForkIdManager.ForkId(Bytes.fromHexString("0x3edd5b10"), 4370000L)); assertThat(result).isTrue(); } @@ -200,10 +251,11 @@ public void check7PetersburgWithRemoteAnnouncingSpuriousAwareOfByzantiumRemoteMa public void check8ByzantiumWithRemoteAnnouncingPetersburgLocalOutOfSync() { // 8 Local is mainnet Byzantium, remote announces Petersburg. Local is out of sync, accept. // {7279999, ID{Hash: 0x668db0af, Next: 0}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7279999L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0x668db0af", 0L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 727999L), forkList); + final Boolean result = + forkIdManager.peerCheck(new ForkIdManager.ForkId(Bytes.fromHexString("0x668db0af"), 0L)); assertThat(result).isTrue(); } @@ -212,10 +264,11 @@ public void check9SpuriousWithRemoteAnnouncingByzantiumRemoteUnawareOfPetersburg // 9 Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. // Local out of sync. Local also knows about a future fork, but that is uncertain yet. // {4369999, ID{Hash: 0xa00bc324, Next: 0}, nil}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 4369999L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0xa00bc324", 0L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 4369999L), forkList); + final Boolean result = + forkIdManager.peerCheck(new ForkIdManager.ForkId(Bytes.fromHexString("0xa00bc324"), 0L)); assertThat(result).isTrue(); } @@ -224,10 +277,11 @@ public void check10PetersburgWithRemoteAnnouncingByzantiumRemoteUnawareOfAdditio // 10 Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. // Remote needs software update. // {7987396, ID{Hash: 0xa00bc324, Next: 0}, ErrRemoteStale}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7987396L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0xa00bc324", 0L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7987396L), forkList); + final Boolean result = + forkIdManager.peerCheck(new ForkIdManager.ForkId(Bytes.fromHexString("0xa00bc324"), 0L)); assertThat(result).isFalse(); } @@ -236,10 +290,11 @@ public void check11PetersburgWithRemoteAnnouncingPetersburgAndFutureForkLocalNee // 11 Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + // 0xffffffff. Local needs software update, reject. // {7987396, ID{Hash: 0x5cddc0e1, Next: 0}, ErrLocalIncompatibleOrStale}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7987396L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0x5cddc0e1", 0L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7987396L), forkList); + final Boolean result = + forkIdManager.peerCheck(new ForkIdManager.ForkId(Bytes.fromHexString("0x5cddc0e1"), 0L)); assertThat(result).isFalse(); } @@ -248,10 +303,11 @@ public void check12ByzantiumWithRemoteAnnouncingPetersburgAndFutureForkLocalNeed // 12 Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + // 0xffffffff. Local needs software update, reject. // {7279999, ID{Hash: 0x5cddc0e1, Next: 0}, ErrLocalIncompatibleOrStale}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7279999L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0x5cddc0e1", 0L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7279999L), forkList); + final Boolean result = + forkIdManager.peerCheck(new ForkIdManager.ForkId(Bytes.fromHexString("0x5cddc0e1"), 0L)); assertThat(result).isFalse(); } @@ -259,62 +315,66 @@ public void check12ByzantiumWithRemoteAnnouncingPetersburgAndFutureForkLocalNeed public void check13ByzantiumWithRemoteAnnouncingRinkebyPetersburg() { // 13 Local is mainnet Petersburg, remote is Rinkeby Petersburg. // {7987396, ID{Hash: 0xafec6b27, Next: 0}, ErrLocalIncompatibleOrStale}, - List forkList = Arrays.asList(forksMainnet); - ForkIdManager forkIdManager = - new ForkIdManager(Hash.fromHexString(mainnetGenHash), forkList, 7987396L); - Boolean result = forkIdManager.peerCheck(ForkIdManager.createIdEntry("0xafec6b27", 0L)); + final List forkList = Arrays.asList(forksMainnet); + final ForkIdManager forkIdManager = + new ForkIdManager(mockBlockchain(mainnetGenHash, 7987396L), forkList); + final Boolean result = + forkIdManager.peerCheck(new ForkIdManager.ForkId(Bytes.fromHexString("0xafec6b27"), 0L)); assertThat(result).isFalse(); } @Test public void createAndDecodeRLP() { - ForkIdManager.ForkId forkIdEntry = ForkIdManager.createIdEntry("0xa00bc324", 7280000L); - BytesValueRLPOutput out = new BytesValueRLPOutput(); + final ForkIdManager.ForkId forkIdEntry = + new ForkIdManager.ForkId(Bytes.fromHexString("0xa00bc324"), 7280000L); + final BytesValueRLPOutput out = new BytesValueRLPOutput(); forkIdEntry.writeTo(out); - Bytes bytesValue = out.encoded(); - BytesValueRLPInput in = new BytesValueRLPInput(bytesValue, false); - ForkIdManager.ForkId decodedEntry = ForkIdManager.readFrom(in); - assertThat(forkIdEntry.equals(decodedEntry)).isTrue(); + final Bytes bytesValue = out.encoded(); + final BytesValueRLPInput in = new BytesValueRLPInput(bytesValue, false); + final ForkIdManager.ForkId decodedEntry = ForkIdManager.readFrom(in); + assertThat(forkIdEntry).isEqualTo(decodedEntry); } @Test public void check1ZeroZeroProperRLPEncoding() { - ForkIdManager.ForkId forkIdEntry = ForkIdManager.createIdEntry("0", "0x"); - BytesValueRLPOutput out = new BytesValueRLPOutput(); + final ForkIdManager.ForkId forkIdEntry = + new ForkIdManager.ForkId(Bytes.fromHexString("0x00000000"), 0); + final BytesValueRLPOutput out = new BytesValueRLPOutput(); forkIdEntry.writeTo(out); - String str1 = "0xc6840000000080"; - Bytes bytesValue = out.encoded(); - assertThat(str1.equals(bytesValue.toString())).isTrue(); - BytesValueRLPInput in = new BytesValueRLPInput(bytesValue, false); - ForkIdManager.ForkId decodedEntry = ForkIdManager.readFrom(in); - assertThat(forkIdEntry.equals(decodedEntry)).isTrue(); + final String str1 = "0xc6840000000080"; + final Bytes bytesValue = out.encoded(); + assertThat(str1).isEqualTo(bytesValue.toString()); + final BytesValueRLPInput in = new BytesValueRLPInput(bytesValue, false); + final ForkIdManager.ForkId decodedEntry = ForkIdManager.readFrom(in); + assertThat(forkIdEntry).isEqualTo(decodedEntry); } @Test public void check2ArbitraryProperRLPEncoding() { - ForkIdManager.ForkId forkIdEntry = ForkIdManager.createIdEntry("0xdeadbeef", "0xBADDCAFE"); - BytesValueRLPOutput out = new BytesValueRLPOutput(); + final ForkIdManager.ForkId forkIdEntry = + new ForkIdManager.ForkId(Bytes.fromHexString("0xdeadbeef"), 0xbaddcafeL); + final BytesValueRLPOutput out = new BytesValueRLPOutput(); forkIdEntry.writeTo(out); - String str1 = "0xca84deadbeef84baddcafe"; - Bytes bytesValue = out.encoded(); - assertThat(str1.equals(bytesValue.toString())).isTrue(); - BytesValueRLPInput in = new BytesValueRLPInput(bytesValue, false); - ForkIdManager.ForkId decodedEntry = ForkIdManager.readFrom(in); - assertThat(forkIdEntry.equals(decodedEntry)).isTrue(); + final String str1 = "0xca84deadbeef84baddcafe"; + final Bytes bytesValue = out.encoded(); + assertThat(str1).isEqualTo(bytesValue.toString()); + final BytesValueRLPInput in = new BytesValueRLPInput(bytesValue, false); + final ForkIdManager.ForkId decodedEntry = ForkIdManager.readFrom(in); + assertThat(forkIdEntry).isEqualTo(decodedEntry); } @Test public void check3MaximumsProperRLPEncoding() { - ForkIdManager.ForkId forkIdEntry = - ForkIdManager.createIdEntry("0xffffffff", Long.parseUnsignedLong("ffffffffffffffff", 16)); - BytesValueRLPOutput out = new BytesValueRLPOutput(); + final ForkIdManager.ForkId forkIdEntry = + new ForkIdManager.ForkId(Bytes.fromHexString("0xffffffff"), 0xffffffffffffffffL); + final BytesValueRLPOutput out = new BytesValueRLPOutput(); forkIdEntry.writeTo(out); - String str1 = + final String str1 = "0xce84ffffffff88ffffffffffffffff"; // Check value supplied in EIP-2124 spec via GO lang - Bytes bytesValue = out.encoded(); - assertThat(str1.equals(bytesValue.toString())).isTrue(); - BytesValueRLPInput in = new BytesValueRLPInput(bytesValue, false); - ForkIdManager.ForkId decodedEntry = ForkIdManager.readFrom(in); - assertThat(forkIdEntry.equals(decodedEntry)).isTrue(); + final Bytes bytesValue = out.encoded(); + assertThat(str1).isEqualTo(bytesValue.toString()); + final BytesValueRLPInput in = new BytesValueRLPInput(bytesValue, false); + final ForkIdManager.ForkId decodedEntry = ForkIdManager.readFrom(in); + assertThat(forkIdEntry).isEqualTo(decodedEntry); } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessageTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessageTest.java index ce6ad7d4da3..1bdf3dc543c 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessageTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/StatusMessageTest.java @@ -25,6 +25,7 @@ import java.math.BigInteger; import java.util.Random; +import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.junit.Test; @@ -74,7 +75,8 @@ public void serializeDeserializeWithForkId() { final Difficulty td = Difficulty.of(1000L); final Hash bestHash = randHash(1L); final Hash genesisHash = randHash(2L); - final ForkIdManager.ForkId forkId = ForkIdManager.createIdEntry("0xa00bc334", 0L); + final ForkIdManager.ForkId forkId = + new ForkIdManager.ForkId(Bytes.fromHexString("0xa00bc334"), 0L); final MessageData msg = StatusMessage.create(version, networkId, td, bestHash, genesisHash, forkId); From bfc40be32e6a8ba7918aec69415d6a0ce12d30c0 Mon Sep 17 00:00:00 2001 From: Lucas Saldanha Date: Tue, 25 Feb 2020 11:16:58 +1300 Subject: [PATCH 33/38] Adds priv_getcode (#428) * Adds priv_getcode Signed-off-by: Joshua Richardson --- CHANGELOG.md | 1 + .../condition/ExpectValidContractCode.java | 29 ++++ .../condition/PrivateContractCondition.java | 4 +- .../condition/PrivateContractVerifier.java | 4 + .../transaction/net/CustomRequestFactory.java | 2 + ...loyPrivateSmartContractAcceptanceTest.java | 4 +- .../besu/ethereum/api/jsonrpc/RpcMethod.java | 1 + .../privacy/methods/priv/PrivGetCode.java | 85 ++++++++++ .../jsonrpc/methods/PrivJsonRpcMethods.java | 8 +- .../privacy/methods/priv/PrivGetCodeTest.java | 148 ++++++++++++++++++ gradle/versions.gradle | 8 +- 11 files changed, 287 insertions(+), 7 deletions(-) create mode 100644 acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/ExpectValidContractCode.java create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetCode.java create mode 100644 ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetCodeTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 72837156174..918a80d177f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## 1.4.1 +- Added priv_getCode [\#250](https://github.com/hyperledger/besu/pull/408). Gets the bytecode associated with a private address. ### Bug Fixes diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/ExpectValidContractCode.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/ExpectValidContractCode.java new file mode 100644 index 00000000000..574ace120da --- /dev/null +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/ExpectValidContractCode.java @@ -0,0 +1,29 @@ +/* + * 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.tests.acceptance.dsl.privacy.condition; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; + +import org.web3j.tx.Contract; + +public class ExpectValidContractCode implements PrivateContractCondition { + @Override + public void verify(final Contract contract) throws IOException { + assertThat(contract).isNotNull(); + assertThat(contract.isValid()).isEqualTo(true); + } +} diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/PrivateContractCondition.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/PrivateContractCondition.java index b48a9fca049..7969e2d3576 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/PrivateContractCondition.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/PrivateContractCondition.java @@ -14,8 +14,10 @@ */ package org.hyperledger.besu.tests.acceptance.dsl.privacy.condition; +import java.io.IOException; + import org.web3j.tx.Contract; public interface PrivateContractCondition { - void verify(final Contract contract); + void verify(final Contract contract) throws IOException; } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/PrivateContractVerifier.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/PrivateContractVerifier.java index 755b1339040..de9f36d08d7 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/PrivateContractVerifier.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/condition/PrivateContractVerifier.java @@ -22,4 +22,8 @@ public ExpectValidPrivateContractDeployedReceipt validPrivateContractDeployed( final String contractAddress, final String senderAddress) { return new ExpectValidPrivateContractDeployedReceipt(contractAddress, senderAddress); } + + public ExpectValidContractCode validContractCodeProvided() { + return new ExpectValidContractCode(); + } } diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/net/CustomRequestFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/net/CustomRequestFactory.java index 32c7b46da2e..adaa6b4951c 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/net/CustomRequestFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/net/CustomRequestFactory.java @@ -32,10 +32,12 @@ public static class TransactionReceiptWithRevertReason extends TransactionReceip public TransactionReceiptWithRevertReason() {} + @Override public void setRevertReason(final String revertReason) { this.revertReason = revertReason; } + @Override public String getRevertReason() { return revertReason; } diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java index edbcc275b8e..57858c423d9 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/DeployPrivateSmartContractAcceptanceTest.java @@ -36,7 +36,7 @@ public void setUp() throws Exception { } @Test - public void deployingMustGiveValidReceipt() { + public void deployingMustGiveValidReceiptAndCode() throws Exception { final String contractAddress = "0x89ce396d0f9f937ddfa71113e29b2081c4869555"; final EventEmitter eventEmitter = @@ -50,5 +50,7 @@ public void deployingMustGiveValidReceipt() { privateContractVerifier .validPrivateContractDeployed(contractAddress, minerNode.getAddress().toString()) .verify(eventEmitter); + + privateContractVerifier.validContractCodeProvided().verify(eventEmitter); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java index ac115342ab7..acce6a1e31d 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/RpcMethod.java @@ -46,6 +46,7 @@ public enum RpcMethod { PRIV_FIND_PRIVACY_GROUP("priv_findPrivacyGroup"), PRIV_DISTRIBUTE_RAW_TRANSACTION("priv_distributeRawTransaction"), PRIV_GET_EEA_TRANSACTION_COUNT("priv_getEeaTransactionCount"), + PRIV_GET_CODE("priv_getCode"), EEA_SEND_RAW_TRANSACTION("eea_sendRawTransaction"), ETH_ACCOUNTS("eth_accounts"), ETH_BLOCK_NUMBER("eth_blockNumber"), diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetCode.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetCode.java new file mode 100644 index 00000000000..8c1a7a946aa --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetCode.java @@ -0,0 +1,85 @@ +/* + * 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.privacy.methods.priv; + +import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.AbstractBlockParameterMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameter; +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.BlockchainQueries; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; + +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; + +public class PrivGetCode extends AbstractBlockParameterMethod { + + private final BlockchainQueries blockchain; + private final PrivateStateRootResolver privateStateRootResolver; + private final WorldStateArchive privateWorldStateArchive; + + public PrivGetCode( + final BlockchainQueries blockchainQueries, + final WorldStateArchive privateWorldStateArchive, + final PrivateStateRootResolver privateStateRootResolver) { + super(blockchainQueries); + this.privateWorldStateArchive = privateWorldStateArchive; + this.blockchain = blockchainQueries; + this.privateStateRootResolver = privateStateRootResolver; + } + + @Override + public String getName() { + return RpcMethod.PRIV_GET_CODE.getMethodName(); + } + + @Override + protected BlockParameter blockParameter(final JsonRpcRequestContext request) { + return request.getRequiredParameter(2, BlockParameter.class); + } + + @Override + protected Object resultByBlockNumber( + final JsonRpcRequestContext request, final long blockNumber) { + final String privacyGroupId = request.getRequiredParameter(0, String.class); + + final Address address = Address.fromHexString(request.getRequiredParameter(1, String.class)); + + final Hash latestStateRoot = + privateStateRootResolver.resolveLastStateRoot( + Bytes32.wrap(Bytes.fromBase64String(privacyGroupId)), + blockchain.getBlockchain().getBlockByNumber(blockNumber).orElseThrow().getHash()); + + return privateWorldStateArchive + .get(latestStateRoot) + .flatMap( + pws -> + Optional.ofNullable(pws.get(address)).map(account -> account.getCode().toString())) + .map(c -> new JsonRpcSuccessResponse(request.getRequest().getId(), c)) + .orElse(new JsonRpcSuccessResponse(request.getRequest().getId(), null)); + } + + @Override + public JsonRpcResponse response(final JsonRpcRequestContext requestContext) { + return (JsonRpcResponse) findResultByParamType(requestContext); + } +} diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivJsonRpcMethods.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivJsonRpcMethods.java index ab9bac5462d..7cfdada586e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivJsonRpcMethods.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/PrivJsonRpcMethods.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivDeletePrivacyGroup; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivDistributeRawTransaction; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivFindPrivacyGroup; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivGetCode; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivGetPrivacyPrecompileAddress; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivGetPrivateTransaction; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv.PrivGetTransactionCount; @@ -32,6 +33,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.privacy.PrivacyController; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; import java.util.Map; @@ -68,6 +70,10 @@ protected Map create( new PrivGetPrivateTransaction( getBlockchainQueries(), privacyController, enclavePublicKeyProvider), new PrivDistributeRawTransaction(privacyController, enclavePublicKeyProvider), - new PrivCall(getBlockchainQueries(), privacyController, enclavePublicKeyProvider)); + new PrivCall(getBlockchainQueries(), privacyController, enclavePublicKeyProvider), + new PrivGetCode( + getBlockchainQueries(), + getPrivacyParameters().getPrivateWorldStateArchive(), + new PrivateStateRootResolver(getPrivacyParameters().getPrivateStateStorage()))); } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetCodeTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetCodeTest.java new file mode 100644 index 00000000000..45b42b85dc4 --- /dev/null +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetCodeTest.java @@ -0,0 +1,148 @@ +/* + * 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.privacy.methods.priv; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.crypto.SECP256K1; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; +import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.Account; +import org.hyperledger.besu.ethereum.core.Address; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.Hash; +import org.hyperledger.besu.ethereum.core.Wei; +import org.hyperledger.besu.ethereum.core.WorldState; +import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver; +import org.hyperledger.besu.ethereum.privacy.PrivateTransaction; +import org.hyperledger.besu.ethereum.privacy.Restriction; +import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; + +import java.math.BigInteger; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +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 PrivGetCodeTest { + + private final Address sender = + Address.fromHexString("0x0000000000000000000000000000000000000003"); + private static final SECP256K1.KeyPair KEY_PAIR = + SECP256K1.KeyPair.create( + SECP256K1.PrivateKey.create( + new BigInteger( + "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 16))); + + private final Bytes privacyGroupId = + Bytes.fromBase64String("Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs="); + + private final PrivateTransaction.Builder privateTransactionBuilder = + PrivateTransaction.builder() + .nonce(0) + .gasPrice(Wei.of(1000)) + .gasLimit(3000000) + .to(null) + .value(Wei.ZERO) + .payload( + Bytes.fromBase64String( + "0x608060405234801561001057600080fd5b5060d08061001f60003960" + + "00f3fe60806040526004361060485763ffffffff7c01000000" + + "00000000000000000000000000000000000000000000000000" + + "60003504166360fe47b18114604d5780636d4ce63c14607557" + + "5b600080fd5b348015605857600080fd5b5060736004803603" + + "6020811015606d57600080fd5b50356099565b005b34801560" + + "8057600080fd5b506087609e565b6040805191825251908190" + + "0360200190f35b600055565b6000549056fea165627a7a7230" + + "5820cb1d0935d14b589300b12fcd0ab849a7e9019c81da24d6" + + "daa4f6b2f003d1b0180029")) + .sender(sender) + .chainId(BigInteger.valueOf(2018)) + .privateFrom(Bytes.fromBase64String("A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=")) + .restriction(Restriction.RESTRICTED); + + private PrivGetCode method; + + @Mock private BlockchainQueries mockBlockchainQueries; + @Mock private Blockchain mockBlockchain; + @Mock private Block mockBlock; + @Mock private Hash mockHash; + @Mock private PrivateStateRootResolver mockResolver; + @Mock private WorldStateArchive mockPrivateWorldStateArchive; + @Mock private WorldState mockWorldState; + @Mock private Account mockAccount; + + private final Hash lastStateRoot = + Hash.fromHexString("0x2121b68f1333e93bae8cd717a3ca68c9d7e7003f6b288c36dfc59b0f87be9590"); + + private final Bytes contractAccountCode = Bytes.fromBase64String("ZXhhbXBsZQ=="); + + @Before + public void before() { + method = new PrivGetCode(mockBlockchainQueries, mockPrivateWorldStateArchive, mockResolver); + } + + @Test + public void returnValidCodeWhenCalledOnValidContract() { + final PrivateTransaction transaction = + privateTransactionBuilder.privacyGroupId(privacyGroupId).signAndBuild(KEY_PAIR); + final Address contractAddress = + Address.privateContractAddress( + transaction.getSender(), transaction.getNonce(), privacyGroupId); + + mockBlockchainWithContractCode(contractAddress); + + final JsonRpcRequestContext request = + new JsonRpcRequestContext( + new JsonRpcRequest( + "2.0", + "priv_getCode", + new Object[] { + "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=", + contractAddress.toHexString(), + "latest" + })); + + final JsonRpcResponse response = method.response(request); + + assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class); + assertThat(((JsonRpcSuccessResponse) response).getResult()) + .isEqualTo(contractAccountCode.toString()); + } + + private void mockBlockchainWithContractCode(final Address resultantContractAddress) { + when(mockBlockchainQueries.getBlockchain()).thenReturn(mockBlockchain); + when(mockBlockchain.getBlockByNumber(anyLong())).thenReturn(Optional.of(mockBlock)); + when(mockBlock.getHash()).thenReturn(mockHash); + when(mockResolver.resolveLastStateRoot(any(Bytes32.class), any(Hash.class))) + .thenReturn(lastStateRoot); + when(mockPrivateWorldStateArchive.get(lastStateRoot)).thenReturn(Optional.of(mockWorldState)); + when(mockWorldState.get(resultantContractAddress)).thenReturn(mockAccount); + when(mockAccount.getCode()).thenReturn(contractAccountCode); + } +} diff --git a/gradle/versions.gradle b/gradle/versions.gradle index df1c6865703..3b971e2eef0 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -95,10 +95,10 @@ dependencyManagement { dependency 'org.springframework.security:spring-security-crypto:5.2.1.RELEASE' - dependency 'org.web3j:abi:4.5.11' - dependency 'org.web3j:besu:4.5.11' - dependency 'org.web3j:core:4.5.11' - dependency 'org.web3j:crypto:4.5.11' + dependency 'org.web3j:abi:4.5.15' + dependency 'org.web3j:besu:4.5.15' + dependency 'org.web3j:core:4.5.15' + dependency 'org.web3j:crypto:4.5.15' dependency 'org.xerial.snappy:snappy-java:1.1.7.3' From 30fa565dca87f0021b4e223cb4279c619a4cf032 Mon Sep 17 00:00:00 2001 From: anthonybuckle Date: Wed, 26 Feb 2020 01:14:18 -0330 Subject: [PATCH 34/38] BESU-146 - check if success and return errorResponse otherwise (#424) Signed-off-by: Anthony Buckle Co-authored-by: CJ Hare --- .../internal/methods/EthEstimateGas.java | 6 ++++-- .../internal/methods/EthEstimateGasTest.java | 20 ++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java index ff4c15c034a..fb9a7d4e632 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java @@ -84,8 +84,10 @@ private JsonCallParameter overrideGasLimitAndPrice( private Function gasEstimateResponse( final JsonRpcRequestContext request) { return result -> - new JsonRpcSuccessResponse( - request.getRequest().getId(), Quantity.create(result.getGasEstimate())); + result.isSuccessful() + ? new JsonRpcSuccessResponse( + request.getRequest().getId(), Quantity.create(result.getGasEstimate())) + : null; } private JsonRpcErrorResponse errorResponse(final JsonRpcRequestContext request) { diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java index 37c7eb94504..c1669723977 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java @@ -83,9 +83,9 @@ public void shouldReturnErrorWhenTransientTransactionProcessorReturnsEmpty() { } @Test - public void shouldReturnGasEstimateWhenTransientTransactionProcessorReturnsResult() { + public void shouldReturnGasEstimateWhenTransientTransactionProcessorReturnsResultSuccess() { final JsonRpcRequestContext request = ethEstimateGasRequest(callParameter()); - mockTransientProcessorResultGasEstimate(1L); + mockTransientProcessorResultGasEstimate(1L, true); final JsonRpcResponse expectedResponse = new JsonRpcSuccessResponse(null, Quantity.create(1L)); @@ -93,11 +93,25 @@ public void shouldReturnGasEstimateWhenTransientTransactionProcessorReturnsResul .isEqualToComparingFieldByField(expectedResponse); } - private void mockTransientProcessorResultGasEstimate(final long gasEstimate) { + @Test + public void shouldReturnGasEstimateErrorWhenTransientTransactionProcessorReturnsResultFailure() { + final JsonRpcRequestContext request = ethEstimateGasRequest(callParameter()); + mockTransientProcessorResultGasEstimate(1L, false); + + final JsonRpcResponse expectedResponse = + new JsonRpcErrorResponse(null, JsonRpcError.INTERNAL_ERROR); + + Assertions.assertThat(method.response(request)) + .isEqualToComparingFieldByField(expectedResponse); + } + + private void mockTransientProcessorResultGasEstimate( + final long gasEstimate, final boolean isSuccessful) { final TransactionSimulatorResult result = mock(TransactionSimulatorResult.class); when(result.getGasEstimate()).thenReturn(gasEstimate); when(transactionSimulator.process(eq(modifiedCallParameter()), eq(1L))) .thenReturn(Optional.of(result)); + when(result.isSuccessful()).thenReturn(isSuccessful); } private JsonCallParameter callParameter() { From 9a088f7b2d29e5064fbe4f5a69343135f5d33b96 Mon Sep 17 00:00:00 2001 From: Sally MacFarlane Date: Wed, 26 Feb 2020 17:15:29 +1000 Subject: [PATCH 35/38] fixed typos (#429) Signed-off-by: Sally MacFarlane Co-authored-by: CJ Hare --- .../main/java/org/hyperledger/besu/ethereum/core/Address.java | 2 +- .../test/java/org/hyperledger/besu/ethereum/vm/AddressTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Address.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Address.java index 83b2d20aaf2..092c02b6c1e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Address.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Address.java @@ -55,7 +55,7 @@ protected Address(final Bytes bytes) { public static Address wrap(final Bytes value) { checkArgument( value.size() == SIZE, - "An account address must be be %s bytes long, got %s", + "An account address must be %s bytes long, got %s", SIZE, value.size()); return new Address(value); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/AddressTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/AddressTest.java index af8dc4d2bf0..df3d86f2869 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/AddressTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/AddressTest.java @@ -41,7 +41,7 @@ public void accountAddressEquals() { } @Test - public void accountAddresHashCode() { + public void accountAddressHashCode() { final Address addr = Address.wrap(Bytes.fromHexString("0x0000000000000000000000000000000000101010")); final Address addr2 = From 28205875c5683cce832450f05898ee5316139176 Mon Sep 17 00:00:00 2001 From: Felipe Faraggi Date: Wed, 26 Feb 2020 13:39:26 +0100 Subject: [PATCH 36/38] roll back on ALL-CAPS.md files for TSC proposal (#376) Signed-off-by: Felipe Faraggi --- CODEOWNERS.md | 0 CODE_OF_CONDUCT.md | 74 ++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 4 +-- ISSUE_TEMPLATE.md | 0 NOTICE.md | 0 PULL_REQUEST_TEMPLATE.md | 39 +++++++++++++++++++++ ROADMAP.md | 66 +++++++++++++++++++++++++++++++++++ SECURITY.md | 11 ++++++ SUPPORT.md | 24 +++++++++++++ 9 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 CODEOWNERS.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 ISSUE_TEMPLATE.md create mode 100644 NOTICE.md create mode 100644 PULL_REQUEST_TEMPLATE.md create mode 100644 ROADMAP.md create mode 100644 SECURITY.md create mode 100644 SUPPORT.md diff --git a/CODEOWNERS.md b/CODEOWNERS.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..dfd1c60cceb --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at [private@pegasys.tech]. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[Contributor Covenant]: https://www.contributor-covenant.org +[private@pegasys.tech]: mailto:private@pegasys.tech \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2e0428356bf..9529e4f7a1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,9 +6,9 @@ Welcome to the Besu repository! The following links are a set of guidelines for ### LF / Jira Accounts Having the following accounts is necessary for contributing code/issues to Besu. -* If you want to contribute code, you can make a [Linux Foundation (LF) account] here. +* If you want to contribute code, you can make a [github account here](https://github.com). * If you want to raise an issue, you can login to your [Atlassian account here](https://id.atlassian.com/). -* Our Rocket Chat also requires a [Linux Foundation (LF) account]. +* Our Rocket Chat requires a [Linux Foundation (LF) account]. ### Useful contributing links diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000000..edfac2bb41a --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,39 @@ +## Status +**READY/IN DEVELOPMENT/HOLD** + +## Description +A few sentences describing the overall goals of the pull request's commits. + +## Related PRs +List related PRs against other branches: + +branch | PR +------ | ------ +other_pr_production | [link]() +other_pr_master | [link]() + + +## Todos +- [ ] Tests +- [ ] Documentation + + +## Deploy Notes +Notes regarding deployment the contained body of work. These should note any +db migrations, etc. + +## Steps to Test or Reproduce +Outline the steps to test or reproduce the PR here. + +```sh +git pull --prune +git checkout +bundle; script/server +``` + +1. + +## Impacted Areas in Application +List general components of the application that this PR will affect: + +* \ No newline at end of file diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 00000000000..7adb9304157 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,66 @@ +# Roadmap +This document represents the current working roadmap for Besu. It is a living document, which will +evolve and change over time. In particular the features in later versions are likely to be refined and change. + +We use the approach of `#now`, `#next`, `#later` [used by foursquare](https://medium.com/@noah_weiss/now-next-later-roadmaps-without-the-drudgery-1cfe65656645), with a slightly different time horizon. +Our `#now` scale is about 3 months, `#next` about 6 months, and `+later` is 12+ months. + +## Now (up to v1.3) +Our key areas for now are: +* Making Besu a First Class Client +* Istanbul Support +* State Pruning +* Disaster recovery + +### Making Besu a First Class Client + +First and foremost, we want Besu to be a first class client for usage on both mainnet and permissioned networks. +This entails maintaining compatibility with mainnet, providing permissioning features and constantly improving Besu's performance. +Some recent additions to the client have been [UPnP Support](https://docs.besu.pegasys.tech/en/latest/Configuring-Besu/Networking/Using-UPnP/) +and a [GraphQL interface](https://docs.besu.pegasys.tech/en/latest/Besu-API/GraphQL/). + +### Istanbul Support + +Besu will support the upcoming Istanbul network upgrade and implement all required EIPs as per the [Hardfork Meta: Istanbul EIP](https://eips.ethereum.org/EIPS/eip-1679). + +### State Pruning + +State pruning will be implemented. State pruning reduces the disk space required for the Besu database by discarding outdated world state data. + +### Disaster Recovery + +Support key-value storage in relational databases to solidify a robust Disaster Recovery process. Note: Orion to support Oracle and Postgres in 1.3. + +## Next (v1.4) +The key areas for next are: +* Tracing APIs +* Enhancing key management capabilities +* Migration tools +* Ethereum 1.x + +### Tracing APIs + +Additional tracing APIs to be added. + +### Enhancing Key Management + +Enhancing key management capabilities by supporting secure storage of keys. + +### Migration Tools + +Adding tools to enable migration across consensus algorithms. + +### Ethereum 1.x + +The Besu team will help spearhead the Ethereum 1.x initiative by contributing to EIPs, community discussions, roadmap specificaton and eventually implementing the resulting features from the initiative. More information on the 1.x initiative can be found [here](https://docs.ethhub.io/ethereum-roadmap/ethereum-1.x/). + +## Future (v1.5+) +In addition to making incremental improvements to the above features, there will be some bigger pieces of work. +These are deliberately kept vague at this time, and will be elaborated upon when they move up to the now and next levels of work. + +* Ethereum 2.0 +* Alternate Consensus Mechanisms +* Sidechains +* Privacy group consensus +* Cross privacy group communication +* On-chain privacy \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..73255812096 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,11 @@ +# Hyperledger Security Policy + +## Reporting a Security Bug + +If you think you have discovered a security issue in any of the Hyperledger projects, we'd love to hear from you. We will take all security bugs seriously and if confirmed upon investigation we will patch it within a reasonable amount of time and release a public security bulletin discussing the impact and credit the discoverer. + +There are two ways to report a security bug. The easiest is to email a description of the flaw and any related information (e.g. reproduction steps, version) to [security at hyperledger dot org](mailto:security@hyperledger.org). + +The other way is to file a confidential security bug in our [JIRA bug tracking system](https://jira.hyperledger.org). Be sure to set the “Security Level” to “Security issue”. + +The process by which the Hyperledger Security Team handles security bugs is documented further in our [Defect Response page](https://wiki.hyperledger.org/display/HYP/Defect+Response) on our [wiki](https://wiki.hyperledger.org). diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 00000000000..a28b0076e7c --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,24 @@ +# Hyperledger Besu Support + +Welcome to the Besu repository! The following links are a set of guidelines for contributing to this repo and its packages. These are mostly guidelines, not rules. Use your best judgement, and feel free to propose changes to this document in a pull request. Contributions come in the form of code submissions, writing documentation, raising issues, helping others in chat, and any other actions that help develop Besu. + +### LF / Jira Accounts + +Having the following accounts is necessary for obtaining support for Besu. +* If you want to raise an issue, you can login to your [Atlassian account here](https://id.atlassian.com/). +* Our Rocket Chat requires a [Linux Foundation (LF) account]. +* The Hyperlegder wiki also requires a [Linux Foundation (LF) account] in order to edit pages. + +### Useful support links + +* [Besu User Documentation] +* [RocketChat] +* [I just have a quick question](https://wiki.hyperledger.org/display/BESU/I+just+have+a+quick+question) +* [Did you find a bug?](https://wiki.hyperledger.org/display/BESU/Reporting+Bugs) +* [Issues](https://wiki.hyperledger.org/display/BESU/Issues) +* [Contributing Guidelines] + + +[Besu User Documentation]: https://besu.hyperledger.org +[RocketChat]: https://chat.hyperledger.org/ +[Contributing Guidelines]: CONTRIBUTING.md \ No newline at end of file From 2f6c58e63b93294a2240e8ae10052efb23609032 Mon Sep 17 00:00:00 2001 From: Joshua Fernandes Date: Thu, 27 Feb 2020 11:42:56 +1000 Subject: [PATCH 37/38] updating the changelog with 1.4.0 details (#431) Signed-off-by: Joshua Fernandes --- CHANGELOG.md | 1996 ++++++++++++++++++++++++++------------------------ 1 file changed, 1035 insertions(+), 961 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 918a80d177f..e6de68bd86e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,17 +7,92 @@ - [BESU-25](https://jira.hyperledger.org/browse/BESU-25) Use v5 Devp2p when pinging [\#392](https://github.com/hyperledger/besu/pull/392) -## 1.4.0 RC-2 -### Private State Migration -Hyperledger Besu v1.4 implements a new data structure for private state storage that is not backwards compatible. -A migration will be performed when starting v1.4 for the first time to reprocess existing private transactions -and re-create the private state data in the v1.4 format. +## 1.4.0 + +### Private State Migration + +Hyperledger Besu v1.4 implements a new data structure for private state storage that is not backwards compatible. +A migration will be performed when starting v1.4 for the first time to reprocess existing private transactions +and re-create the private state data in the v1.4 format. + +If you have existing private transactions, see [migration details](docs/Private-Txns-Migration.md). + +### Additions and Improvements + +* [TLS support](https://besu.hyperledger.org/en/latest/Concepts/TLS/) to secure client and server communication. + +* [Multi-tenancy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Multi-Tenancy/) to enable multiple participants to use the same Besu and Orion node. + +* [Plugin APIs](https://besu.hyperledger.org/en/latest/Concepts/Plugins/) to enable building of Java plugins to extend Hyperledger Besu. + +* Support for additional [NAT methods](https://besu.hyperledger.org/en/latest/HowTo/Find-and-Connect/Specifying-NAT/). + +* Added [`priv_call`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#priv_call) which invokes +a private contract function locally and does not change the private state. + +* Besu has moved from an internal Bytes library to the [Apache Tuweni](https://tuweni.apache.org/) Bytes library. +This includes using the library in the Plugins API interfaces. [#295](https://github.com/hyperledger/besu/pull/295) and [#215](https://github.com/hyperledger/besu/pull/215) + +### Early Access Features + +Early access features are available features that are not recommended for production networks and may +have unstable interfaces. + +* [Reorg compatible privacy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Privacy-Overview/#reorg-compatible-privacy) +to enable private transactions on networks using consensus mechanisms that fork. + +* [Tracing API](https://besu.hyperledger.org/en/latest/Concepts/Transactions/Trace-Types) to obtain detailed information about transaction processing. + +### Bug Fixes + +See RC and Beta sections below. + +### Known Issues + +#### Fast sync defaulting to full sync + +- When fast sync cannot find enough valid peers rapidly enough, Besu defaults to full sync. + +Workarounds: +1. To re-attempt fast syncing rather than continue full syncing, stop Besu, delete your database, +and start again. +2. When fast syncing, explicitly disable pruning using `--pruning-enabled=false` to reduce the likelihood +of encountering the pruning bug. + +A fix to remove the default to full sync is [in progress](https://github.com/hyperledger/besu/pull/427) +and is planned for inclusion in v1.4.1. + +#### Error full syncing with pruning + +- Error syncing with mainnet on Besu 1.3.7 node - MerkleTrieException [\#BESU-160](https://jira.hyperledger.org/browse/BESU-160) +The associated error is `Unable to load trie node value for hash` and is caused by the combination of +full sync and pruning. + +Workarounds: +1. Explicitly disable pruning using `--pruning-enabled=false` when using fast sync. +2. If the `MerkleTrieException` occurs, delete the database and resync. + +Investigation of this issue is in progress and a fix is targeted for v1.4.1. + +#### Bootnodes must be validators when using onchain permissioning + +- Onchain permissioning nodes can't peer when using a non-validator bootnode [\#BESU-181](https://jira.hyperledger.org/browse/BESU-181) + +Workaround -> When using onchain permissioning, ensure bootnodes are also validators. + + +## 1.4.0 RC-2 + +### Private State Migration +Hyperledger Besu v1.4 implements a new data structure for private state storage that is not backwards compatible. +A migration will be performed when starting v1.4 for the first time to reprocess existing private transactions +and re-create the private state data in the v1.4 format. If you have existing private transactions, see [migration details](docs/Private-Txns-Migration.md). -## 1.4.0 RC-1 +## 1.4.0 RC-1 -### Additions and Improvements +### Additions and Improvements - New`trace_replayBlockTransactions` JSON-RPC API @@ -35,34 +110,34 @@ The default NAT method (AUTO) can detect this so no user intervention is require - Private transactions are now validated before sent to the enclave [\#356](https://github.com/hyperledger/besu/pull/356) -### Known Bugs +### Known Bugs - Error syncing with mainnet on Besu 1.3.7 node - MerkleTrieException [\#BESU-160](https://jira.hyperledger.org/browse/BESU-160) -Workaround -> Don't enable pruning when syncing to mainnet. +Workaround -> Don't enable pruning when syncing to mainnet. - Onchain permissioning nodes can't peer when using a non-validator bootnode [\#BESU-181](https://jira.hyperledger.org/browse/BESU-181) -Workaround -> When using onchain permissioning, ensure bootnodes are also validators. +Workaround -> When using onchain permissioning, ensure bootnodes are also validators. -## 1.4 Beta 3 +## 1.4 Beta 3 -### Additions and Improvements +### Additions and Improvements - CLI option to enable TLS client auth for JSON-RPC HTTP [\#340](https://github.com/hyperledger/besu/pull/340) -Added CLI options to enable TLS client authentication and trusting client certificates: +Added CLI options to enable TLS client authentication and trusting client certificates: ~~~ --rpc-http-tls-client-auth-enabled - Enable TLS client authentication for the JSON-RPC HTTP service (default: false) --rpc-http-tls-known-clients-file - Path to file containing client's certificate common name and fingerprint for client authentication. --rpc-http-tls-ca-clients-enabled - Enable to accept clients certificate signed by a valid CA for client authentication (default: false) ~~~ -If client-auth is enabled, user must either enable CA signed clients OR provide a known-clients file. An error is reported +If client-auth is enabled, user must either enable CA signed clients OR provide a known-clients file. An error is reported if both CA signed clients is disabled and known-clients file is not specified. - Stable Plugins APIs [\#346](https://github.com/hyperledger/besu/pull/346) -The `BesuEvents` service and related `data` package have been marked as a stable plugin API. +The `BesuEvents` service and related `data` package have been marked as a stable plugin API. ### Bug Fixes @@ -70,22 +145,22 @@ The `BesuEvents` service and related `data` package have been marked as a stable ### Experimental Features -- Experimental support for `trace_replayBlockTransactions` - multiple PRs +- Experimental support for `trace_replayBlockTransactions` - multiple PRs -Added support for the `trace_replayBlockTransactions` JSON-RPC call. To enable this API add -`TRACE` to the `rpc-http-api` options (for example, `--rpc-http-api TRACE` on the command line). +Added support for the `trace_replayBlockTransactions` JSON-RPC call. To enable this API add +`TRACE` to the `rpc-http-api` options (for example, `--rpc-http-api TRACE` on the command line). -This is not a production ready API. There are known bugs relating to traced memory from calls and -returns, and the gas calculation reported in the flat traces does not always match up with the +This is not a production ready API. There are known bugs relating to traced memory from calls and +returns, and the gas calculation reported in the flat traces does not always match up with the correct gas calculated for consensus. -## 1.4 Beta 2 +## 1.4 Beta 2 -### Additions and Improvements +### Additions and Improvements - Enable TLS for JSON-RPC HTTP Service [\#253](https://github.com/hyperledger/besu/pull/253) -Exposes new command line parameters to enable TLS on Ethereum JSON-RPC HTTP interface to allow clients like EthSigner to connect via TLS: +Exposes new command line parameters to enable TLS on Ethereum JSON-RPC HTTP interface to allow clients like EthSigner to connect via TLS: `--rpc-http-tls-enabled=true` (Optional - Only required if `--rpc-http-enabled` is set to true) Set to `true` to enable TLS. False by default. `--rpc-http-tls-keystore-file="/path/to/cert.pfx"` @@ -93,40 +168,40 @@ Exposes new command line parameters to enable TLS on Ethereum JSON-RPC HTTP inte `--rpc-http-tls-keystore-password-file="/path/to/cert.passwd"` (Must be specified if TLS is enabled) Path to the text file containing password for unlocking key store. `--rpc-http-tls-known-clients-file="/path/to/rpc_tls_clients.txt"` -(Optional) Path to a plain text file containing space separated client’s certificate’s common name and its sha-256 fingerprints when -they are not signed by a known CA. The presence of this file (even empty) enables TLS client authentication. That is, the client -presents the certificate to server on TLS handshake and server establishes that the client certificate is either signed by a -proper/known CA. Otherwise, server trusts client certificate by reading the sha-256 fingerprint from known clients file specified above. +(Optional) Path to a plain text file containing space separated client’s certificate’s common name and its sha-256 fingerprints when +they are not signed by a known CA. The presence of this file (even empty) enables TLS client authentication. That is, the client +presents the certificate to server on TLS handshake and server establishes that the client certificate is either signed by a +proper/known CA. Otherwise, server trusts client certificate by reading the sha-256 fingerprint from known clients file specified above. The format of the file is (as an example): `localhost DF:65:B8:02:08:5E:91:82:0F:91:F5:1C:96:56:92:C4:1A:F6:C6:27:FD:6C:FC:31:F2:BB:90:17:22:59:5B:50` -### Bug Fixes +### Bug Fixes -- TotalDifficulty is a BigInteger [\#253](https://github.com/hyperledger/besu/pull/253). +- TotalDifficulty is a BigInteger [\#253](https://github.com/hyperledger/besu/pull/253). Don't try and cast total difficulty down to a long because it will overflow long in a reasonable timeframe. -## 1.4 Beta 1 +## 1.4 Beta 1 -### Additions and Improvements +### Additions and Improvements - Besu has moved from an internal Bytes library to the [Apache Tuweni](https://tuweni.apache.org/) Bytes library. This includes using the library in the Plugins API interfaces. [#295](https://github.com/hyperledger/besu/pull/295) and [#215](https://github.com/hyperledger/besu/pull/215) - Besu stops processing blocks if Orion is unavailable [\#253](https://github.com/hyperledger/besu/pull/253) - Added priv_call [\#250](https://github.com/hyperledger/besu/pull/250). Invokes a private contract function locally and does not change the private state. - Support for [EIP-2124](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2124.md), which results in faster peer discovery [\#156](https://github.com/hyperledger/besu/pull/156) -## 1.3.8 +## 1.3.8 -### Additions and Improvements +### Additions and Improvements - `admin_generateLogBloomCache` JSON-RPC API to generate a cache of the block bloombits that improves performance for log queries [\#262](https://github.com/hyperledger/besu/pull/262) -## Critical Fix in 1.3.7 +## Critical Fix in 1.3.7 -1.3.7 includes a critical fix for Ethereum MainNet users and the Muir Glacier upgrade. We recommend users of Ethereum public networks +1.3.7 includes a critical fix for Ethereum MainNet users and the Muir Glacier upgrade. We recommend users of Ethereum public networks (MainNet, Ropsten, Rinkeby, and Goerli) upgrade immediately. This upgrade is also strongly recommended for users of private networks. -For more details, see [Hyperledger Besu Wiki](https://wiki.hyperledger.org/display/BESU/Mainnet+Consensus+Bug+Identified+and+Resolved+in+Hyperledger+Besu). +For more details, see [Hyperledger Besu Wiki](https://wiki.hyperledger.org/display/BESU/Mainnet+Consensus+Bug+Identified+and+Resolved+in+Hyperledger+Besu). ## Muir Glacier Compatibility @@ -143,230 +218,230 @@ For compatibility with ETC Agharta upgrade, use 1.3.7 or later. - Hard Fork Support: Configures the Agharta activation block for the ETC MainNet configuration [\#251](https://github.com/hyperledger/besu/pull/251) (thanks to [soc1c](https://github.com/soc1c)) - `operator generate-log-bloom-cache` command line option to generate a cache of the block bloombits that improves performance for log queries [\#245](https://github.com/hyperledger/besu/pull/245) -### Bug Fixes +### Bug Fixes -- Resolves a Mainnet consensus issue [\#254](https://github.com/hyperledger/besu/pull/254) +- Resolves a Mainnet consensus issue [\#254](https://github.com/hyperledger/besu/pull/254) ### New Maintainer -[Edward Mack](https://github.com/hyperledger/besu/commits?author=edwardmack) added as a [new maintainer](https://github.com/hyperledger/besu/pull/219). +[Edward Mack](https://github.com/hyperledger/besu/commits?author=edwardmack) added as a [new maintainer](https://github.com/hyperledger/besu/pull/219). -### 1.3.6 +### 1.3.6 -### Additions and Improvements +### Additions and Improvements -- Performance improvements: - * Multithread Websockets to increase throughput [\#231](https://github.com/hyperledger/besu/pull/231) +- Performance improvements: + * Multithread Websockets to increase throughput [\#231](https://github.com/hyperledger/besu/pull/231) * NewBlockHeaders performance improvement [\#230](https://github.com/hyperledger/besu/pull/230) - EIP2384 - Ice Age Adustment around Istanbul [\#211](https://github.com/hyperledger/besu/pull/211) -- Documentation updates include: +- Documentation updates include: * [Configuring mining using the Stratum protocol](https://besu.hyperledger.org/en/latest/HowTo/Configure/Configure-Mining/) * [ETC network command line options](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#network) - Hard Fork Support: * MuirGlacier for Ethereum Mainnet and Ropsten Testnet * Agharta for Kotti and Mordor Testnets -### Bug Fixes +### Bug Fixes + +- [\#210](https://github.com/hyperledger/besu/pull/210) fixes WebSocket frames handling + User impact: PING/PONG frames handling in Websocket services was not implemented -- [\#210](https://github.com/hyperledger/besu/pull/210) fixes WebSocket frames handling - User impact: PING/PONG frames handling in Websocket services was not implemented - ### 1.3.5 ### Additions and Improvements -- Log Event Streaming for Plugin API [\#186](https://github.com/hyperledger/besu/pull/186) -- Allow use a external JWT public key in authenticated APIs [\#183](https://github.com/hyperledger/besu/pull/183) -- ETC Configuration, classic fork peer validator [\#176](https://github.com/hyperledger/besu/pull/176) (thanks to [edwardmack](https://github.com/edwardmack)) -- Allow IBFT validators to be changed at a given block [\#173](https://github.com/hyperledger/besu/pull/173) +- Log Event Streaming for Plugin API [\#186](https://github.com/hyperledger/besu/pull/186) +- Allow use a external JWT public key in authenticated APIs [\#183](https://github.com/hyperledger/besu/pull/183) +- ETC Configuration, classic fork peer validator [\#176](https://github.com/hyperledger/besu/pull/176) (thanks to [edwardmack](https://github.com/edwardmack)) +- Allow IBFT validators to be changed at a given block [\#173](https://github.com/hyperledger/besu/pull/173) - Support external mining using Stratum [\#140](https://github.com/hyperledger/besu/pull/140) (thanks to [atoulme](https://github.com/atoulme)) - Add more fields to private transaction receipt [\#85](https://github.com/hyperledger/besu/pull/85) (thanks to [josh-richardson](https://github.com/josh-richardson)) -- [Pruning documentation](https://besu.hyperledger.org/en/latest/Concepts/Pruning/) +- [Pruning documentation](https://besu.hyperledger.org/en/latest/Concepts/Pruning/) ### Technical Improvements - ETC - Cleanup [\#201](https://github.com/hyperledger/besu/pull/201) (thanks to [GregTheGreek](https://github.com/GregTheGreek)) -- User specific enclave public key configuration in auth file [\#196](https://github.com/hyperledger/besu/pull/196) -- Change CustomForks -\> Transitions [\#193](https://github.com/hyperledger/besu/pull/193) -- Pass identity information into RpcMethod from Http Service [\#189](https://github.com/hyperledger/besu/pull/189) -- Remove the use of JsonRpcParameters from RpcMethods [\#188](https://github.com/hyperledger/besu/pull/188) -- Repaired Metrics name collision between Privacy and RocksDB [\#187](https://github.com/hyperledger/besu/pull/187) -- Multi-Tenancy: Do not specify a public key anymore when requesting a … [\#185](https://github.com/hyperledger/besu/pull/185) -- Updates to circle building acceptance tests [\#184](https://github.com/hyperledger/besu/pull/184) +- User specific enclave public key configuration in auth file [\#196](https://github.com/hyperledger/besu/pull/196) +- Change CustomForks -\> Transitions [\#193](https://github.com/hyperledger/besu/pull/193) +- Pass identity information into RpcMethod from Http Service [\#189](https://github.com/hyperledger/besu/pull/189) +- Remove the use of JsonRpcParameters from RpcMethods [\#188](https://github.com/hyperledger/besu/pull/188) +- Repaired Metrics name collision between Privacy and RocksDB [\#187](https://github.com/hyperledger/besu/pull/187) +- Multi-Tenancy: Do not specify a public key anymore when requesting a … [\#185](https://github.com/hyperledger/besu/pull/185) +- Updates to circle building acceptance tests [\#184](https://github.com/hyperledger/besu/pull/184) - Move Apache Tuweni dependency to official release [\#181](https://github.com/hyperledger/besu/pull/181) (thanks to [atoulme](https://github.com/atoulme)) -- Update Gradle to 6.0, support Java 13 [\#180](https://github.com/hyperledger/besu/pull/180) +- Update Gradle to 6.0, support Java 13 [\#180](https://github.com/hyperledger/besu/pull/180) - ETC Atlantis fork [\#179](https://github.com/hyperledger/besu/pull/179) (thanks to [edwardmack](https://github.com/edwardmack)) - ETC Gotham Fork [\#178](https://github.com/hyperledger/besu/pull/178) (thanks to [edwardmack](https://github.com/edwardmack)) - ETC DieHard fork support [\#177](https://github.com/hyperledger/besu/pull/177) (thanks to [edwardmack](https://github.com/edwardmack)) - Remove 'parentHash', 'number' and 'gasUsed' fields from the genesis d… [\#175](https://github.com/hyperledger/besu/pull/175) (thanks to [SweeXordious](https://github.com/SweeXordious)) -- Enable pruning by default for fast sync and validate conflicts with privacy [\#172](https://github.com/hyperledger/besu/pull/172) -- Update RocksDB [\#170](https://github.com/hyperledger/besu/pull/170) -- Vpdate ver to 1.3.5-snapshot [\#169](https://github.com/hyperledger/besu/pull/169) -- Added PoaQueryService method that returns local node signer… [\#163](https://github.com/hyperledger/besu/pull/163) -- Add versioning to privacy storage [\#149](https://github.com/hyperledger/besu/pull/149) -- Update reference tests [\#139](https://github.com/hyperledger/besu/pull/139) +- Enable pruning by default for fast sync and validate conflicts with privacy [\#172](https://github.com/hyperledger/besu/pull/172) +- Update RocksDB [\#170](https://github.com/hyperledger/besu/pull/170) +- Vpdate ver to 1.3.5-snapshot [\#169](https://github.com/hyperledger/besu/pull/169) +- Added PoaQueryService method that returns local node signer… [\#163](https://github.com/hyperledger/besu/pull/163) +- Add versioning to privacy storage [\#149](https://github.com/hyperledger/besu/pull/149) +- Update reference tests [\#139](https://github.com/hyperledger/besu/pull/139) -### 1.3.4 +### 1.3.4 - Reverted _Enable pruning by default for fast sync (#135)_ [\#164](https://github.com/hyperledger/besu/pull/164) ### 1.3.3 -### Technical Improvements +### Technical Improvements -- Add --identity flag for client identification in node browsers [\#150](https://github.com/hyperledger/besu/pull/150) -- Istanbul Mainnet Block [\#145](https://github.com/hyperledger/besu/pull/150) -- Add priv\_getEeaTransactionCount [\#110](https://github.com/hyperledger/besu/pull/110) +- Add --identity flag for client identification in node browsers [\#150](https://github.com/hyperledger/besu/pull/150) +- Istanbul Mainnet Block [\#145](https://github.com/hyperledger/besu/pull/150) +- Add priv\_getEeaTransactionCount [\#110](https://github.com/hyperledger/besu/pull/110) ### Additions and Improvements -- Redesign of how JsonRpcMethods are created [\#159](https://github.com/hyperledger/besu/pull/159) -- Moving JsonRpcMethods classes into the same package, prior to refactor [\#154](https://github.com/hyperledger/besu/pull/154) -- Reflect default logging in CLI help [\#148](https://github.com/hyperledger/besu/pull/148) -- Handle zero port better in NAT [\#147](https://github.com/hyperledger/besu/pull/147) -- Rework how filter and log query parameters are created/used [\#146](https://github.com/hyperledger/besu/pull/146) -- Don't generate shutdown tasks in controller [\#141](https://github.com/hyperledger/besu/pull/141) -- Ibft queries [\#138](https://github.com/hyperledger/besu/pull/138) -- Enable pruning by default for fast sync [\#135](https://github.com/hyperledger/besu/pull/135) -- Ensure spotless runs in CI [\#132](https://github.com/hyperledger/besu/pull/132) -- Add more logging around peer disconnects [\#131](https://github.com/hyperledger/besu/pull/131) -- Repair EthGetLogs returning incorrect results [\#128](https://github.com/hyperledger/besu/pull/128) +- Redesign of how JsonRpcMethods are created [\#159](https://github.com/hyperledger/besu/pull/159) +- Moving JsonRpcMethods classes into the same package, prior to refactor [\#154](https://github.com/hyperledger/besu/pull/154) +- Reflect default logging in CLI help [\#148](https://github.com/hyperledger/besu/pull/148) +- Handle zero port better in NAT [\#147](https://github.com/hyperledger/besu/pull/147) +- Rework how filter and log query parameters are created/used [\#146](https://github.com/hyperledger/besu/pull/146) +- Don't generate shutdown tasks in controller [\#141](https://github.com/hyperledger/besu/pull/141) +- Ibft queries [\#138](https://github.com/hyperledger/besu/pull/138) +- Enable pruning by default for fast sync [\#135](https://github.com/hyperledger/besu/pull/135) +- Ensure spotless runs in CI [\#132](https://github.com/hyperledger/besu/pull/132) +- Add more logging around peer disconnects [\#131](https://github.com/hyperledger/besu/pull/131) +- Repair EthGetLogs returning incorrect results [\#128](https://github.com/hyperledger/besu/pull/128) - Use Bloombits for Logs queries [\#127](https://github.com/hyperledger/besu/pull/127) -- Improve message when extraData missing [\#121](https://github.com/hyperledger/besu/pull/121) -- Fix miner startup logic [\#104](https://github.com/hyperledger/besu/pull/104) -- Support log reordring from reorgs in `LogSubscriptionService` [\#86](https://github.com/hyperledger/besu/pull/86) +- Improve message when extraData missing [\#121](https://github.com/hyperledger/besu/pull/121) +- Fix miner startup logic [\#104](https://github.com/hyperledger/besu/pull/104) +- Support log reordring from reorgs in `LogSubscriptionService` [\#86](https://github.com/hyperledger/besu/pull/86) -### 1.3.2 +### 1.3.2 -### Additions and Improvements +### Additions and Improvements -- besu -v to print plugin versions[\#123](https://github.com/hyperledger/besu/pull/123) +- besu -v to print plugin versions[\#123](https://github.com/hyperledger/besu/pull/123) -### Technical Improvements +### Technical Improvements - Update Governance and Code of Conduct verbiage [\#120](https://github.com/hyperledger/besu/pull/120) -- Fix private transaction root mismatch [\#118](https://github.com/hyperledger/besu/pull/118) -- Programatically enforce plugin CLI variable names [\#117](https://github.com/hyperledger/besu/pull/117) -- Additional unit test for selecting replaced pending transactions [\#116](https://github.com/hyperledger/besu/pull/116) +- Fix private transaction root mismatch [\#118](https://github.com/hyperledger/besu/pull/118) +- Programatically enforce plugin CLI variable names [\#117](https://github.com/hyperledger/besu/pull/117) +- Additional unit test for selecting replaced pending transactions [\#116](https://github.com/hyperledger/besu/pull/116) - Only set sync targets that have an estimated height value [\#115](https://github.com/hyperledger/besu/pull/115) -- Fix rlpx startup [\#114](https://github.com/hyperledger/besu/pull/114) -- Expose getPayload in Transaction plugin-api interface. [\#113](https://github.com/hyperledger/besu/pull/113) -- Dependency Version Upgrades [\#112](https://github.com/hyperledger/besu/pull/112) -- Add hash field in Transaction plugin interface. [\#111](https://github.com/hyperledger/besu/pull/111) -- Rework sync status events [\#106](https://github.com/hyperledger/besu/pull/106) +- Fix rlpx startup [\#114](https://github.com/hyperledger/besu/pull/114) +- Expose getPayload in Transaction plugin-api interface. [\#113](https://github.com/hyperledger/besu/pull/113) +- Dependency Version Upgrades [\#112](https://github.com/hyperledger/besu/pull/112) +- Add hash field in Transaction plugin interface. [\#111](https://github.com/hyperledger/besu/pull/111) +- Rework sync status events [\#106](https://github.com/hyperledger/besu/pull/106) -### 1.3.1 +### 1.3.1 -### Additions and Improvements +### Additions and Improvements - Added GraphQL query/logs support [\#94](https://github.com/hyperledger/besu/pull/94) -### Technical Improvements +### Technical Improvements -- Add totalDiffculty to BlockPropagated events. [\#97](https://github.com/hyperledger/besu/pull/97) -- Merge BlockchainQueries classes [\#101](https://github.com/hyperledger/besu/pull/101) -- Fixed casing of dynamic MetricCategorys [\#99](https://github.com/hyperledger/besu/pull/99) -- Fix private transactions breaking evm [\#96](https://github.com/hyperledger/besu/pull/96) -- Make SyncState variables thread-safe [\#95](https://github.com/hyperledger/besu/pull/95) -- Fix transaction tracking by sender [\#93](https://github.com/hyperledger/besu/pull/93) -- Make logic in PersistBlockTask more explicit to fix a LGTM warning [\#92](https://github.com/hyperledger/besu/pull/92) -- Removed Unused methods in the transaction simulator. [\#91](https://github.com/hyperledger/besu/pull/91) -- Fix ThreadBesuNodeRunner BesuConfiguration setup [\#90](https://github.com/hyperledger/besu/pull/90) -- JsonRpc method disabled error condition rewrite and unit test [\#80](https://github.com/hyperledger/besu/pull/80) -- Round trip testing of state trie account values [\#31](https://github.com/hyperledger/besu/pull/31) +- Add totalDiffculty to BlockPropagated events. [\#97](https://github.com/hyperledger/besu/pull/97) +- Merge BlockchainQueries classes [\#101](https://github.com/hyperledger/besu/pull/101) +- Fixed casing of dynamic MetricCategorys [\#99](https://github.com/hyperledger/besu/pull/99) +- Fix private transactions breaking evm [\#96](https://github.com/hyperledger/besu/pull/96) +- Make SyncState variables thread-safe [\#95](https://github.com/hyperledger/besu/pull/95) +- Fix transaction tracking by sender [\#93](https://github.com/hyperledger/besu/pull/93) +- Make logic in PersistBlockTask more explicit to fix a LGTM warning [\#92](https://github.com/hyperledger/besu/pull/92) +- Removed Unused methods in the transaction simulator. [\#91](https://github.com/hyperledger/besu/pull/91) +- Fix ThreadBesuNodeRunner BesuConfiguration setup [\#90](https://github.com/hyperledger/besu/pull/90) +- JsonRpc method disabled error condition rewrite and unit test [\#80](https://github.com/hyperledger/besu/pull/80) +- Round trip testing of state trie account values [\#31](https://github.com/hyperledger/besu/pull/31) -### 1.3 +### 1.3 -### Breaking Change +### Breaking Change -- Disallow comments in Genesis JSON file. [\#49](https://github.com/hyperledger/besu/pull/49) +- Disallow comments in Genesis JSON file. [\#49](https://github.com/hyperledger/besu/pull/49) ### Additions and Improvements - Add `--required-block` command line option to deal with chain splits [\#79](https://github.com/hyperledger/besu/pull/79) -- Store db metadata file in the root data directory. [\#46](https://github.com/hyperledger/besu/pull/46) +- Store db metadata file in the root data directory. [\#46](https://github.com/hyperledger/besu/pull/46) - Add `--target-gas-limit` command line option. [\#24](https://github.com/hyperledger/besu/pull/24)(thanks to new contributor [cfelde](https://github.com/cfelde)) -- Allow private contracts to access public state. [\#9](https://github.com/hyperledger/besu/pull/9) -- Documentation updates include: +- Allow private contracts to access public state. [\#9](https://github.com/hyperledger/besu/pull/9) +- Documentation updates include: - Added [sample load balancer configurations](https://besu.hyperledger.org/en/latest/HowTo/Configure/Configure-HA/Sample-Configuration/) - - Added [`retesteth`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Subcommands/#retesteth) subcommand - - Added [`debug_accountRange`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#debug_accountrange) JSON-RPC API method - - Clarified purpose of [static nodes](https://besu.hyperledger.org/en/latest/HowTo/Find-and-Connect/Managing-Peers/#static-nodes) + - Added [`retesteth`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Subcommands/#retesteth) subcommand + - Added [`debug_accountRange`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#debug_accountrange) JSON-RPC API method + - Clarified purpose of [static nodes](https://besu.hyperledger.org/en/latest/HowTo/Find-and-Connect/Managing-Peers/#static-nodes) - Added links [Kubernetes reference implementations](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Kubernetes/) - Added content about [access between private and public states](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Privacy-Groups/#access-between-states) - Added restriction that [account permissioning cannot be used with random key signing](https://besu.hyperledger.org/en/latest/HowTo/Use-Privacy/Sign-Privacy-Marker-Transactions/). - Added high availability requirement for [private transaction manager](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Privacy-Overview/#availability) (ie, Orion) - Added [genesis file reference](https://besu.hyperledger.org/en/latest/Reference/Config-Items/) -### Technical Improvements +### Technical Improvements - Less verbose synching subscriptions [\#59](https://github.com/hyperledger/besu/pull/59) -- Return enclave key instead of private transaction hash [\#53](https://github.com/hyperledger/besu/pull/53) -- Fix mark sweep pruner bugs where nodes that should be kept were being swept [\#50](https://github.com/hyperledger/besu/pull/50) -- Clean up BesuConfiguration construction [\#51](https://github.com/hyperledger/besu/pull/51) -- Private tx nonce errors return same msg as any tx [\#48](https://github.com/hyperledger/besu/pull/48) -- Fix default logging [\#47](https://github.com/hyperledger/besu/pull/47) -- Introduce virtual operation. [\#45](https://github.com/hyperledger/besu/pull/45) +- Return enclave key instead of private transaction hash [\#53](https://github.com/hyperledger/besu/pull/53) +- Fix mark sweep pruner bugs where nodes that should be kept were being swept [\#50](https://github.com/hyperledger/besu/pull/50) +- Clean up BesuConfiguration construction [\#51](https://github.com/hyperledger/besu/pull/51) +- Private tx nonce errors return same msg as any tx [\#48](https://github.com/hyperledger/besu/pull/48) +- Fix default logging [\#47](https://github.com/hyperledger/besu/pull/47) +- Introduce virtual operation. [\#45](https://github.com/hyperledger/besu/pull/45) - Downgrade RocksDBPlugin Logging Levels [\#44](https://github.com/hyperledger/besu/pull/44) - Infrastructure for exposing PoA metrics for plugins. [\#37](https://github.com/hyperledger/besu/pull/37) - Refactor privacy storage. [\#7](https://github.com/hyperledger/besu/pull/7) -## 1.2.4 +## 1.2.4 -### Additions and Improvements +### Additions and Improvements - Add Istanbul block (5435345) for Rinkeby [\#35](https://github.com/hyperledger/besu/pull/35) -- Add Istanbul block (1561651) for Goerli [\#27](https://github.com/hyperledger/besu/pull/27) -- Add Istanbul block (6485846) for Ropsten [\#26](https://github.com/hyperledger/besu/pull/26) +- Add Istanbul block (1561651) for Goerli [\#27](https://github.com/hyperledger/besu/pull/27) +- Add Istanbul block (6485846) for Ropsten [\#26](https://github.com/hyperledger/besu/pull/26) - Add privDistributeRawTransaction endpoint [\#23](https://github.com/hyperledger/besu/pull/23) (thanks to [josh-richardson](https://github.com/josh-richardson)) -### Technical Improvements +### Technical Improvements - Refactors pantheon private key to signing private key [\#34](https://github.com/hyperledger/besu/pull/34) (thanks to [josh-richardson](https://github.com/josh-richardson)) -- Support both BESU\_ and PANTHEON\_ env var prefixes [\#32](https://github.com/hyperledger/besu/pull/32) -- Use only fully validated peers for fast sync pivot selection [\#21](https://github.com/hyperledger/besu/pull/21) +- Support both BESU\_ and PANTHEON\_ env var prefixes [\#32](https://github.com/hyperledger/besu/pull/32) +- Use only fully validated peers for fast sync pivot selection [\#21](https://github.com/hyperledger/besu/pull/21) - Support Version Rollbacks for RocksDB \(\#6\) [\#19](https://github.com/hyperledger/besu/pull/19) - Update Cava library to Tuweni Library [\#18](https://github.com/hyperledger/besu/pull/18) -- StateTrieAccountValue:Version should be written as an int, not a long [\#17](https://github.com/hyperledger/besu/pull/17) -- Handle discovery peers with updated endpoints [\#12](https://github.com/hyperledger/besu/pull/12) -- Change retesteth port [\#11](https://github.com/hyperledger/besu/pull/11) +- StateTrieAccountValue:Version should be written as an int, not a long [\#17](https://github.com/hyperledger/besu/pull/17) +- Handle discovery peers with updated endpoints [\#12](https://github.com/hyperledger/besu/pull/12) +- Change retesteth port [\#11](https://github.com/hyperledger/besu/pull/11) - Renames eea\_getTransactionReceipt to priv\_getTransactionReceipt [\#10](https://github.com/hyperledger/besu/pull/10) (thanks to [josh-richardson](https://github.com/josh-richardson)) -- Support Version Rollbacks for RocksDB [\#6](https://github.com/hyperledger/besu/pull/6) -- Moving AT DSL into its own module [\#3](https://github.com/hyperledger/besu/pull/3) +- Support Version Rollbacks for RocksDB [\#6](https://github.com/hyperledger/besu/pull/6) +- Moving AT DSL into its own module [\#3](https://github.com/hyperledger/besu/pull/3) -## 1.2.3 +## 1.2.3 ### Additions and Improvements -- Added an override facility for genesis configs [\#1915](https://github.com/PegaSysEng/pantheon/pull/1915) +- Added an override facility for genesis configs [\#1915](https://github.com/PegaSysEng/pantheon/pull/1915) - Finer grained logging configuration [\#1895](https://github.com/PegaSysEng/pantheon/pull/1895) (thanks to [matkt](https://github.com/matkt)) ### Technical Improvements -- Add archiving of docker test reports [\#1921](https://github.com/PegaSysEng/pantheon/pull/1921) -- Events API: Transaction dropped, sync status, and renames [\#1919](https://github.com/PegaSysEng/pantheon/pull/1919) +- Add archiving of docker test reports [\#1921](https://github.com/PegaSysEng/pantheon/pull/1921) +- Events API: Transaction dropped, sync status, and renames [\#1919](https://github.com/PegaSysEng/pantheon/pull/1919) - Remove metrics from plugin registration [\#1918](https://github.com/PegaSysEng/pantheon/pull/1918) -- Replace uses of Instant.now from within the IBFT module [\#1911](https://github.com/PegaSysEng/pantheon/pull/1911) -- Update plugins-api build script [\#1908](https://github.com/PegaSysEng/pantheon/pull/1908) -- Ignore flaky tracing tests [\#1907](https://github.com/PegaSysEng/pantheon/pull/1907) -- Ensure plugin-api module gets published at the correct maven path [\#1905](https://github.com/PegaSysEng/pantheon/pull/1905) +- Replace uses of Instant.now from within the IBFT module [\#1911](https://github.com/PegaSysEng/pantheon/pull/1911) +- Update plugins-api build script [\#1908](https://github.com/PegaSysEng/pantheon/pull/1908) +- Ignore flaky tracing tests [\#1907](https://github.com/PegaSysEng/pantheon/pull/1907) +- Ensure plugin-api module gets published at the correct maven path [\#1905](https://github.com/PegaSysEng/pantheon/pull/1905) - Return the plugin-apis to this repo [\#1900](https://github.com/PegaSysEng/pantheon/pull/1900) -- Stop autogenerating BesuInfo.java [\#1899](https://github.com/PegaSysEng/pantheon/pull/1899) -- Extracted Metrics interfaces to plugins-api. [\#1898](https://github.com/PegaSysEng/pantheon/pull/1898) -- Fix key value storage clear so it removes all values [\#1894](https://github.com/PegaSysEng/pantheon/pull/1894) +- Stop autogenerating BesuInfo.java [\#1899](https://github.com/PegaSysEng/pantheon/pull/1899) +- Extracted Metrics interfaces to plugins-api. [\#1898](https://github.com/PegaSysEng/pantheon/pull/1898) +- Fix key value storage clear so it removes all values [\#1894](https://github.com/PegaSysEng/pantheon/pull/1894) - Ethsigner test [\#1892](https://github.com/PegaSysEng/pantheon/pull/1892) (thanks to [iikirilov](https://github.com/iikirilov)) - Return null private transaction receipt instead of error [\#1872](https://github.com/PegaSysEng/pantheon/pull/1872) (thanks to [iikirilov](https://github.com/iikirilov)) -- Implement trace replay block transactions trace option [\#1886](https://github.com/PegaSysEng/pantheon/pull/1886) +- Implement trace replay block transactions trace option [\#1886](https://github.com/PegaSysEng/pantheon/pull/1886) - Use object parameter instead of list of parameters for priv\_createPrivacyGroup [\#1868](https://github.com/PegaSysEng/pantheon/pull/1868) (thanks to [iikirilov](https://github.com/iikirilov)) - Refactor privacy acceptance tests [\#1864](https://github.com/PegaSysEng/pantheon/pull/1864) (thanks to [iikirilov](https://github.com/iikirilov)) -## 1.2.2 +## 1.2.2 ### Additions and Improvements - Support large numbers for the `--network-id` option [\#1891](https://github.com/PegaSysEng/pantheon/pull/1891) -- Added eea\_getTransactionCount Json Rpc [\#1861](https://github.com/PegaSysEng/pantheon/pull/1861) -- PrivacyMarkerTransaction to be signed with a randomly generated key [\#1844](https://github.com/PegaSysEng/pantheon/pull/1844) +- Added eea\_getTransactionCount Json Rpc [\#1861](https://github.com/PegaSysEng/pantheon/pull/1861) +- PrivacyMarkerTransaction to be signed with a randomly generated key [\#1844](https://github.com/PegaSysEng/pantheon/pull/1844) - Implement eth\_getproof JSON RPC API [\#1824](https://github.com/PegaSysEng/pantheon/pull/1824) (thanks to [matkt](https://github.com/matkt)) -- Documentation updates include: +- Documentation updates include: - [Improved navigation](https://docs.pantheon.pegasys.tech/en/latest/) - [Added permissioning diagram](https://docs.pantheon.pegasys.tech/en/latest/Concepts/Permissioning/Permissioning-Overview/#onchain) - [Added Responsible Disclosure policy](https://docs.pantheon.pegasys.tech/en/latest/Reference/Responsible-Disclosure/) @@ -374,33 +449,33 @@ For compatibility with ETC Agharta upgrade, use 1.3.7 or later. ### Technical Improvements - Update the `pantheon blocks export` command usage [\#1887](https://github.com/PegaSysEng/pantheon/pull/1887) (thanks to [matkt](https://github.com/matkt)) -- Stop Returning null for 'pending' RPC calls [\#1883](https://github.com/PegaSysEng/pantheon/pull/1883) -- Blake validation errors are hard errors [\#1882](https://github.com/PegaSysEng/pantheon/pull/1882) -- Add test cases for trace\_replayBlockTransactions [\#1881](https://github.com/PegaSysEng/pantheon/pull/1881) -- Simplify json rpc spec test setup [\#1880](https://github.com/PegaSysEng/pantheon/pull/1880) -- Tweak JSON import format [\#1878](https://github.com/PegaSysEng/pantheon/pull/1878) -- Transactions listeners should use the subscriber pattern [\#1877](https://github.com/PegaSysEng/pantheon/pull/1877) -- Maven spotless [\#1876](https://github.com/PegaSysEng/pantheon/pull/1876) -- Don't cache for localbalance [\#1875](https://github.com/PegaSysEng/pantheon/pull/1875) -- EIP-1108 - Reprice alt\_bn128 [\#1874](https://github.com/PegaSysEng/pantheon/pull/1874) -- Create stub trace\_replayBlockTransactions json-rpc method [\#1873](https://github.com/PegaSysEng/pantheon/pull/1873) +- Stop Returning null for 'pending' RPC calls [\#1883](https://github.com/PegaSysEng/pantheon/pull/1883) +- Blake validation errors are hard errors [\#1882](https://github.com/PegaSysEng/pantheon/pull/1882) +- Add test cases for trace\_replayBlockTransactions [\#1881](https://github.com/PegaSysEng/pantheon/pull/1881) +- Simplify json rpc spec test setup [\#1880](https://github.com/PegaSysEng/pantheon/pull/1880) +- Tweak JSON import format [\#1878](https://github.com/PegaSysEng/pantheon/pull/1878) +- Transactions listeners should use the subscriber pattern [\#1877](https://github.com/PegaSysEng/pantheon/pull/1877) +- Maven spotless [\#1876](https://github.com/PegaSysEng/pantheon/pull/1876) +- Don't cache for localbalance [\#1875](https://github.com/PegaSysEng/pantheon/pull/1875) +- EIP-1108 - Reprice alt\_bn128 [\#1874](https://github.com/PegaSysEng/pantheon/pull/1874) +- Create stub trace\_replayBlockTransactions json-rpc method [\#1873](https://github.com/PegaSysEng/pantheon/pull/1873) - Improve trace log [\#1870](https://github.com/PegaSysEng/pantheon/pull/1870) -- Pruning Command Line Flags [\#1869](https://github.com/PegaSysEng/pantheon/pull/1869) -- Re-enable istanbul [\#1865](https://github.com/PegaSysEng/pantheon/pull/1865) -- Fix logic to disconnect from peers on fork [\#1863](https://github.com/PegaSysEng/pantheon/pull/1863) -- Blake 2b tweaks [\#1862](https://github.com/PegaSysEng/pantheon/pull/1862) -- Sweep state roots before child nodes [\#1854](https://github.com/PegaSysEng/pantheon/pull/1854) +- Pruning Command Line Flags [\#1869](https://github.com/PegaSysEng/pantheon/pull/1869) +- Re-enable istanbul [\#1865](https://github.com/PegaSysEng/pantheon/pull/1865) +- Fix logic to disconnect from peers on fork [\#1863](https://github.com/PegaSysEng/pantheon/pull/1863) +- Blake 2b tweaks [\#1862](https://github.com/PegaSysEng/pantheon/pull/1862) +- Sweep state roots before child nodes [\#1854](https://github.com/PegaSysEng/pantheon/pull/1854) - Update export subcommand to export blocks in rlp format [\#1852](https://github.com/PegaSysEng/pantheon/pull/1852) -- Updating docker tests to make it easier to follow & ensure it listens on the right interface on docker [\#1851](https://github.com/PegaSysEng/pantheon/pull/1851) -- Disable Istanbul block [\#1849](https://github.com/PegaSysEng/pantheon/pull/1849) -- Add read-only blockchain factory method [\#1845](https://github.com/PegaSysEng/pantheon/pull/1845) -- Removing the release plugin in favour of the new process with branches [\#1843](https://github.com/PegaSysEng/pantheon/pull/1843) -- Update Görli bootnodes [\#1842](https://github.com/PegaSysEng/pantheon/pull/1842) -- Upgrade graphql library to version 13.0 [\#1834](https://github.com/PegaSysEng/pantheon/pull/1834) -- Database versioning and enable multi-column database [\#1830](https://github.com/PegaSysEng/pantheon/pull/1830) +- Updating docker tests to make it easier to follow & ensure it listens on the right interface on docker [\#1851](https://github.com/PegaSysEng/pantheon/pull/1851) +- Disable Istanbul block [\#1849](https://github.com/PegaSysEng/pantheon/pull/1849) +- Add read-only blockchain factory method [\#1845](https://github.com/PegaSysEng/pantheon/pull/1845) +- Removing the release plugin in favour of the new process with branches [\#1843](https://github.com/PegaSysEng/pantheon/pull/1843) +- Update Görli bootnodes [\#1842](https://github.com/PegaSysEng/pantheon/pull/1842) +- Upgrade graphql library to version 13.0 [\#1834](https://github.com/PegaSysEng/pantheon/pull/1834) +- Database versioning and enable multi-column database [\#1830](https://github.com/PegaSysEng/pantheon/pull/1830) - Fixes invalid JsonGetter, comment [\#1811](https://github.com/PegaSysEng/pantheon/pull/1811) (thanks to [josh-richardson](https://github.com/josh-richardson)) - Add EthSigner acceptance test [\#1655](https://github.com/PegaSysEng/pantheon/pull/1655) (thanks to [iikirilov](https://github.com/iikirilov)) -- Support plugin Richdata APIs via implementation [\#1581](https://github.com/PegaSysEng/pantheon/pull/1581) +- Support plugin Richdata APIs via implementation [\#1581](https://github.com/PegaSysEng/pantheon/pull/1581) ## 1.2.1 @@ -489,7 +564,7 @@ For compatibility with ETC Agharta upgrade, use 1.3.7 or later. [#1652](https://github.com/PegaSysEng/pantheon/pull/1652) - Added permissioning webinar in the resources [#1717](https://github.com/PegaSysEng/pantheon/pull/1717) - Add web3.js-eea reference doc [#1617](https://github.com/PegaSysEng/pantheon/pull/1617) - - Updated privacy documentation + - Updated privacy documentation [#1650](https://github.com/PegaSysEng/pantheon/pull/1650) [#1721](https://github.com/PegaSysEng/pantheon/pull/1721) [#1722](https://github.com/PegaSysEng/pantheon/pull/1722) @@ -529,42 +604,42 @@ For compatibility with ETC Agharta upgrade, use 1.3.7 or later. - Disable smoke tests on windows [#1847](https://github.com/PegaSysEng/pantheon/pull/1847) - Add read-only blockchain factory method [#1845](https://github.com/PegaSysEng/pantheon/pull/1845) -## 1.2 +## 1.2 -### Additions and Improvements +### Additions and Improvements - Add UPnP Support [\#1334](https://github.com/PegaSysEng/pantheon/pull/1334) (thanks to [notlesh](https://github.com/notlesh)) -- Limit the fraction of wire connections initiated by peers [\#1665](https://github.com/PegaSysEng/pantheon/pull/1665) -- EIP-1706 - Disable SSTORE with gasleft lt call stipend [\#1706](https://github.com/PegaSysEng/pantheon/pull/1706) -- EIP-1108 - Reprice alt\_bn128 [\#1704](https://github.com/PegaSysEng/pantheon/pull/1704) +- Limit the fraction of wire connections initiated by peers [\#1665](https://github.com/PegaSysEng/pantheon/pull/1665) +- EIP-1706 - Disable SSTORE with gasleft lt call stipend [\#1706](https://github.com/PegaSysEng/pantheon/pull/1706) +- EIP-1108 - Reprice alt\_bn128 [\#1704](https://github.com/PegaSysEng/pantheon/pull/1704) - EIP-1344 ChainID Opcode [\#1690](https://github.com/PegaSysEng/pantheon/pull/1690) -- New release docker image [\#1664](https://github.com/PegaSysEng/pantheon/pull/1664) +- New release docker image [\#1664](https://github.com/PegaSysEng/pantheon/pull/1664) - Support changing log level at runtime [\#1656](https://github.com/PegaSysEng/pantheon/pull/1656) (thanks to [matkt](https://github.com/matkt)) - Implement dump command to dump a specific block from storage [\#1641](https://github.com/PegaSysEng/pantheon/pull/1641) (thanks to [matkt](https://github.com/matkt)) - Add eea\_findPrivacyGroup endpoint to Besu [\#1635](https://github.com/PegaSysEng/pantheon/pull/1635) (thanks to [Puneetha17](https://github.com/Puneetha17)) - Updated eea send raw transaction with privacy group ID [\#1611](https://github.com/PegaSysEng/pantheon/pull/1611) (thanks to [iikirilov](https://github.com/iikirilov)) - Added Revert Reason [\#1603](https://github.com/PegaSysEng/pantheon/pull/1603) -- Documentation updates include: +- Documentation updates include: - Added [UPnP content](https://besu.hyperledger.org/en/latest/HowTo/Find-and-Connect/Using-UPnP/) - - Added [load balancer image](https://besu.hyperledger.org/en/stable/) - - Added [revert reason](https://besu.hyperledger.org/en/latest/HowTo/Send-Transactions/Revert-Reason/) + - Added [load balancer image](https://besu.hyperledger.org/en/stable/) + - Added [revert reason](https://besu.hyperledger.org/en/latest/HowTo/Send-Transactions/Revert-Reason/) - Added [admin\_changeLogLevel](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#admin_changeloglevel) JSON RPC API (thanks to [matkt](https://github.com/matkt)) - - Updated for [new Docker image](https://besu.hyperledger.org/en/stable/) - - Added [Docker image migration content](https://besu.hyperledger.org/en/latest/HowTo/Get-Started/Migration-Docker/) - - Added [transaction validation content](https://besu.hyperledger.org/en/latest/Concepts/Transactions/Transaction-Validation/) - - Updated [permissioning overview](https://besu.hyperledger.org/en/stable/) for onchain account permissioning - - Updated [quickstart](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Monitoring-Performance/#monitor-node-performance-using-prometheus) to include Prometheus and Grafana + - Updated for [new Docker image](https://besu.hyperledger.org/en/stable/) + - Added [Docker image migration content](https://besu.hyperledger.org/en/latest/HowTo/Get-Started/Migration-Docker/) + - Added [transaction validation content](https://besu.hyperledger.org/en/latest/Concepts/Transactions/Transaction-Validation/) + - Updated [permissioning overview](https://besu.hyperledger.org/en/stable/) for onchain account permissioning + - Updated [quickstart](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Monitoring-Performance/#monitor-node-performance-using-prometheus) to include Prometheus and Grafana - Added [remote connections limits options](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#remote-connections-limit-enabled) - - Updated [web3.js-eea reference](https://docs.pantheon.pegasys.tech/en/latest/Reference/web3js-eea-Methods/) to include privacy group methods + - Updated [web3.js-eea reference](https://docs.pantheon.pegasys.tech/en/latest/Reference/web3js-eea-Methods/) to include privacy group methods - Updated [onchain permissioning to include account permissioning](hhttps://besu.hyperledger.org/en/latest/Concepts/Permissioning/Onchain-Permissioning/) and [Permissioning Management Dapp](https://besu.hyperledger.org/en/latest/Tutorials/Permissioning/Getting-Started-Onchain-Permissioning/#start-the-development-server-for-the-permissioning-management-dapp) - - Added [deployment procedure for Permissioning Management Dapp](https://besu.hyperledger.org/en/stable/) - - Added privacy content for [EEA-compliant and Besu-extended privacy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Privacy-Groups/) + - Added [deployment procedure for Permissioning Management Dapp](https://besu.hyperledger.org/en/stable/) + - Added privacy content for [EEA-compliant and Besu-extended privacy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Privacy-Groups/) - Added content on [creating and managing privacy groups](https://besu.hyperledger.org/en/latest/Reference/web3js-eea-Methods/#createprivacygroup) - Added content on [accessing private and privacy marker transactions](https://besu.hyperledger.org/en/latest/HowTo/Use-Privacy/Access-Private-Transactions/) - Added content on [system requirements](https://besu.hyperledger.org/en/latest/HowTo/Get-Started/System-Requirements/) - Added reference to [Besu role on Galaxy to deploy using Ansible](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Ansible/). -### Technical Improvements +### Technical Improvements - Remove enclave public key from parameter [\#1789](https://github.com/PegaSysEng/pantheon/pull/1789) - Update defaults host interfaces [\#1782](https://github.com/PegaSysEng/pantheon/pull/1782) @@ -579,65 +654,65 @@ For compatibility with ETC Agharta upgrade, use 1.3.7 or later. - Renames various eea methods to priv methods [\#1736](https://github.com/PegaSysEng/pantheon/pull/1736) - Update Orion version [\#1716](https://github.com/PegaSysEng/pantheon/pull/1716) - Rename CLI flag for better ordering of options [\#1715](https://github.com/PegaSysEng/pantheon/pull/1715) -- Routine dependency updates [\#1712](https://github.com/PegaSysEng/pantheon/pull/1712) -- Fix spelling error in getApplicationPrefix method name [\#1711](https://github.com/PegaSysEng/pantheon/pull/1711) -- Wait and retry if best peer's chain is too short for fast sync [\#1708](https://github.com/PegaSysEng/pantheon/pull/1708) +- Routine dependency updates [\#1712](https://github.com/PegaSysEng/pantheon/pull/1712) +- Fix spelling error in getApplicationPrefix method name [\#1711](https://github.com/PegaSysEng/pantheon/pull/1711) +- Wait and retry if best peer's chain is too short for fast sync [\#1708](https://github.com/PegaSysEng/pantheon/pull/1708) - Eea get private transaction fix [\#1707](https://github.com/PegaSysEng/pantheon/pull/1707) (thanks to [iikirilov](https://github.com/iikirilov)) - Rework remote connection limit flag defaults [\#1705](https://github.com/PegaSysEng/pantheon/pull/1705) -- Report invalid options from config file [\#1703](https://github.com/PegaSysEng/pantheon/pull/1703) -- Add ERROR to list of CLI log level options [\#1699](https://github.com/PegaSysEng/pantheon/pull/1699) +- Report invalid options from config file [\#1703](https://github.com/PegaSysEng/pantheon/pull/1703) +- Add ERROR to list of CLI log level options [\#1699](https://github.com/PegaSysEng/pantheon/pull/1699) - Enable onchain account permissioning CLI option [\#1686](https://github.com/PegaSysEng/pantheon/pull/1686) - Exempt static nodes from all connection limits [\#1685](https://github.com/PegaSysEng/pantheon/pull/1685) -- Enclave refactoring [\#1684](https://github.com/PegaSysEng/pantheon/pull/1684) -- Add opcode and precompiled support for versioning [\#1683](https://github.com/PegaSysEng/pantheon/pull/1683) -- Use a percentage instead of fraction for the remote connections percentage CLI option. [\#1682](https://github.com/PegaSysEng/pantheon/pull/1682) -- Added error msg for calling eth\_sendTransaction [\#1681](https://github.com/PegaSysEng/pantheon/pull/1681) -- Remove instructions for installing with Chocolatey [\#1680](https://github.com/PegaSysEng/pantheon/pull/1680) +- Enclave refactoring [\#1684](https://github.com/PegaSysEng/pantheon/pull/1684) +- Add opcode and precompiled support for versioning [\#1683](https://github.com/PegaSysEng/pantheon/pull/1683) +- Use a percentage instead of fraction for the remote connections percentage CLI option. [\#1682](https://github.com/PegaSysEng/pantheon/pull/1682) +- Added error msg for calling eth\_sendTransaction [\#1681](https://github.com/PegaSysEng/pantheon/pull/1681) +- Remove instructions for installing with Chocolatey [\#1680](https://github.com/PegaSysEng/pantheon/pull/1680) - remove zulu-jdk8 from smoke tests [\#1679](https://github.com/PegaSysEng/pantheon/pull/1679) -- Add new MainNet bootnodes [\#1678](https://github.com/PegaSysEng/pantheon/pull/1678) -- updating smoke tests to use \>= jdk11 [\#1677](https://github.com/PegaSysEng/pantheon/pull/1677) -- Fix handling of remote connection limit [\#1676](https://github.com/PegaSysEng/pantheon/pull/1676) -- Add accountVersion to MessageFrame [\#1675](https://github.com/PegaSysEng/pantheon/pull/1675) -- Change getChildren return type [\#1674](https://github.com/PegaSysEng/pantheon/pull/1674) -- Use Log4J message template instead of String.format [\#1673](https://github.com/PegaSysEng/pantheon/pull/1673) +- Add new MainNet bootnodes [\#1678](https://github.com/PegaSysEng/pantheon/pull/1678) +- updating smoke tests to use \>= jdk11 [\#1677](https://github.com/PegaSysEng/pantheon/pull/1677) +- Fix handling of remote connection limit [\#1676](https://github.com/PegaSysEng/pantheon/pull/1676) +- Add accountVersion to MessageFrame [\#1675](https://github.com/PegaSysEng/pantheon/pull/1675) +- Change getChildren return type [\#1674](https://github.com/PegaSysEng/pantheon/pull/1674) +- Use Log4J message template instead of String.format [\#1673](https://github.com/PegaSysEng/pantheon/pull/1673) - Return hashrate of 0 when not mining. [\#1672](https://github.com/PegaSysEng/pantheon/pull/1672) -- Add hooks for validation [\#1671](https://github.com/PegaSysEng/pantheon/pull/1671) -- Upgrade to pantheon-build:0.0.6-jdk11 which really does include jdk11 [\#1670](https://github.com/PegaSysEng/pantheon/pull/1670) -- Onchain permissioning startup check [\#1669](https://github.com/PegaSysEng/pantheon/pull/1669) +- Add hooks for validation [\#1671](https://github.com/PegaSysEng/pantheon/pull/1671) +- Upgrade to pantheon-build:0.0.6-jdk11 which really does include jdk11 [\#1670](https://github.com/PegaSysEng/pantheon/pull/1670) +- Onchain permissioning startup check [\#1669](https://github.com/PegaSysEng/pantheon/pull/1669) - Update BesuCommand to accept minTransactionGasPriceWei as an integer [\#1668](https://github.com/PegaSysEng/pantheon/pull/1668) (thanks to [matkt](https://github.com/matkt)) - Privacy group id consistent [\#1667](https://github.com/PegaSysEng/pantheon/pull/1667) (thanks to [iikirilov](https://github.com/iikirilov)) - Change eea\_getPrivateTransaction endpoint to accept hex [\#1666](https://github.com/PegaSysEng/pantheon/pull/1666) (thanks to [Puneetha17](https://github.com/Puneetha17)) - Factorise metrics code for KeyValueStorage database [\#1663](https://github.com/PegaSysEng/pantheon/pull/1663)) -- Create a metric tracking DB size [\#1662](https://github.com/PegaSysEng/pantheon/pull/1662) -- AT- Removing unused methods on KeyValueStorage [\#1661](https://github.com/PegaSysEng/pantheon/pull/1661) +- Create a metric tracking DB size [\#1662](https://github.com/PegaSysEng/pantheon/pull/1662) +- AT- Removing unused methods on KeyValueStorage [\#1661](https://github.com/PegaSysEng/pantheon/pull/1661) - Add Prerequisites and Quick-Start [\#1660](https://github.com/PegaSysEng/pantheon/pull/1660) (thanks to [lazaridiscom](https://github.com/lazaridiscom)) - Java 11 updates [\#1658](https://github.com/PegaSysEng/pantheon/pull/1658) - Make test generated keys deterministic w/in block generator [\#1657](https://github.com/PegaSysEng/pantheon/pull/1657) - Rename privacyGroupId to createPrivacyGroupId [\#1654](https://github.com/PegaSysEng/pantheon/pull/1654) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Intermittent Test Failures in TransactionsMessageSenderTest [\#1653](https://github.com/PegaSysEng/pantheon/pull/1653) +- Intermittent Test Failures in TransactionsMessageSenderTest [\#1653](https://github.com/PegaSysEng/pantheon/pull/1653) - Sanity check the generated distribution files before upload [\#1648](https://github.com/PegaSysEng/pantheon/pull/1648) - Use JDK 11 for release builds [\#1647](https://github.com/PegaSysEng/pantheon/pull/1647) -- Support multiple private marker transactions in a block [\#1646](https://github.com/PegaSysEng/pantheon/pull/1646) -- Display World State Sync Progress in Logs [\#1645](https://github.com/PegaSysEng/pantheon/pull/1645) -- Remove the docker gradle plugin, handle building docker with shell now [\#1644](https://github.com/PegaSysEng/pantheon/pull/1644) -- Switch to using metric names from EIP-2159 [\#1634](https://github.com/PegaSysEng/pantheon/pull/1634) -- Account versioning [\#1612](https://github.com/PegaSysEng/pantheon/pull/1612) +- Support multiple private marker transactions in a block [\#1646](https://github.com/PegaSysEng/pantheon/pull/1646) +- Display World State Sync Progress in Logs [\#1645](https://github.com/PegaSysEng/pantheon/pull/1645) +- Remove the docker gradle plugin, handle building docker with shell now [\#1644](https://github.com/PegaSysEng/pantheon/pull/1644) +- Switch to using metric names from EIP-2159 [\#1634](https://github.com/PegaSysEng/pantheon/pull/1634) +- Account versioning [\#1612](https://github.com/PegaSysEng/pantheon/pull/1612) -## 1.1.4 +## 1.1.4 ### Additions and Improvements -- \[PAN-2832\] Support setting config options via environment variables [\#1597](https://github.com/PegaSysEng/pantheon/pull/1597) -- Print Besu version when starting [\#1593](https://github.com/PegaSysEng/pantheon/pull/1593) +- \[PAN-2832\] Support setting config options via environment variables [\#1597](https://github.com/PegaSysEng/pantheon/pull/1597) +- Print Besu version when starting [\#1593](https://github.com/PegaSysEng/pantheon/pull/1593) - \[PAN-2746\] Add eea\_createPrivacyGroup & eea\_deletePrivacyGroup endpoint [\#1560](https://github.com/PegaSysEng/pantheon/pull/1560) (thanks to [Puneetha17](https://github.com/Puneetha17)) -Documentation updates include: -- Added [readiness and liveness endpoints](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/Using-JSON-RPC-API/#readiness-and-liveness-endpoints) -- Added [high availability content](https://besu.hyperledger.org/en/latest/HowTo/Configure/Configure-HA/High-Availability/) -- Added [web3js-eea client library](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Privacy-Quickstart/#clone-eeajs-libraries) +Documentation updates include: +- Added [readiness and liveness endpoints](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/Using-JSON-RPC-API/#readiness-and-liveness-endpoints) +- Added [high availability content](https://besu.hyperledger.org/en/latest/HowTo/Configure/Configure-HA/High-Availability/) +- Added [web3js-eea client library](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Privacy-Quickstart/#clone-eeajs-libraries) - Added content on [setting CLI options using environment variables](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#specifying-options) -### Technical Improvements +### Technical Improvements - Read config from env vars when no config file specified [\#1639](https://github.com/PegaSysEng/pantheon/pull/1639) - Upgrade jackson-databind to 2.9.9.1 [\#1636](https://github.com/PegaSysEng/pantheon/pull/1636) @@ -646,269 +721,269 @@ Documentation updates include: - Check connections more frequently during acceptance tests [\#1630](https://github.com/PegaSysEng/pantheon/pull/1630) - Refactor experimental CLI options [\#1629](https://github.com/PegaSysEng/pantheon/pull/1629) - JSON-RPC api net_services should display the actual ports [\#1628](https://github.com/PegaSysEng/pantheon/pull/1628) -- Refactor CLI [\#1627](https://github.com/PegaSysEng/pantheon/pull/1627) -- Simplify BesuCommand `run` and `parse` methods. [\#1626](https://github.com/PegaSysEng/pantheon/pull/1626) +- Refactor CLI [\#1627](https://github.com/PegaSysEng/pantheon/pull/1627) +- Simplify BesuCommand `run` and `parse` methods. [\#1626](https://github.com/PegaSysEng/pantheon/pull/1626) - PAN-2860: Ignore discport during startup whitelist validation [\#1625](https://github.com/PegaSysEng/pantheon/pull/1625) -- Freeze plugin api version [\#1624](https://github.com/PegaSysEng/pantheon/pull/1624) -- Implement incoming transaction messages CLI option as an unstable command. [\#1622](https://github.com/PegaSysEng/pantheon/pull/1622) -- Update smoke tests docker images for zulu and openjdk to private ones [\#1620](https://github.com/PegaSysEng/pantheon/pull/1620) +- Freeze plugin api version [\#1624](https://github.com/PegaSysEng/pantheon/pull/1624) +- Implement incoming transaction messages CLI option as an unstable command. [\#1622](https://github.com/PegaSysEng/pantheon/pull/1622) +- Update smoke tests docker images for zulu and openjdk to private ones [\#1620](https://github.com/PegaSysEng/pantheon/pull/1620) - Remove duplication between EeaTransactionCountRpc & PrivateTransactionHandler [\#1619](https://github.com/PegaSysEng/pantheon/pull/1619) -- \[PAN-2709\] - nonce too low error [\#1618](https://github.com/PegaSysEng/pantheon/pull/1618) -- Cache TransactionValidationParams instead of creating new object for each call [\#1616](https://github.com/PegaSysEng/pantheon/pull/1616) -- \[PAN-2850\] Create a transaction pool configuration object [\#1615](https://github.com/PegaSysEng/pantheon/pull/1615) -- Add TransactionValidationParam to TxProcessor [\#1613](https://github.com/PegaSysEng/pantheon/pull/1613) -- Expose a CLI option to configure the life time of transaction messages. [\#1610](https://github.com/PegaSysEng/pantheon/pull/1610) -- Implement Prometheus metric counter for skipped expired transaction messages. [\#1609](https://github.com/PegaSysEng/pantheon/pull/1609) -- Upload jars to bintray as part of releases [\#1608](https://github.com/PegaSysEng/pantheon/pull/1608) -- Avoid publishing docker-pantheon directory to bintray during a release [\#1606](https://github.com/PegaSysEng/pantheon/pull/1606) -- \[PAN-2756\] Istanbul scaffolding [\#1605](https://github.com/PegaSysEng/pantheon/pull/1605) -- Implement a timeout in TransactionMessageProcessor [\#1604](https://github.com/PegaSysEng/pantheon/pull/1604) -- Reject transactions with gas price below the configured minimum [\#1602](https://github.com/PegaSysEng/pantheon/pull/1602) -- Always build the k8s image, only push to dockerhub for master branch [\#1601](https://github.com/PegaSysEng/pantheon/pull/1601) -- Properly validate AltBN128 pairing precompile input [\#1600](https://github.com/PegaSysEng/pantheon/pull/1600) -- \[PAN-2871\] Columnar rocksdb [\#1599](https://github.com/PegaSysEng/pantheon/pull/1599) -- Reverting change to dockerfile [\#1594](https://github.com/PegaSysEng/pantheon/pull/1594) -- Update dependency versions [\#1592](https://github.com/PegaSysEng/pantheon/pull/1592) -- \[PAN-2797\] Clean up failed connections [\#1591](https://github.com/PegaSysEng/pantheon/pull/1591) -- Cleaning up the build process for docker [\#1590](https://github.com/PegaSysEng/pantheon/pull/1590) -- \[PAN-2786\] Stop Transaction Pool Queue from Growing Unbounded [\#1586](https://github.com/PegaSysEng/pantheon/pull/1586) +- \[PAN-2709\] - nonce too low error [\#1618](https://github.com/PegaSysEng/pantheon/pull/1618) +- Cache TransactionValidationParams instead of creating new object for each call [\#1616](https://github.com/PegaSysEng/pantheon/pull/1616) +- \[PAN-2850\] Create a transaction pool configuration object [\#1615](https://github.com/PegaSysEng/pantheon/pull/1615) +- Add TransactionValidationParam to TxProcessor [\#1613](https://github.com/PegaSysEng/pantheon/pull/1613) +- Expose a CLI option to configure the life time of transaction messages. [\#1610](https://github.com/PegaSysEng/pantheon/pull/1610) +- Implement Prometheus metric counter for skipped expired transaction messages. [\#1609](https://github.com/PegaSysEng/pantheon/pull/1609) +- Upload jars to bintray as part of releases [\#1608](https://github.com/PegaSysEng/pantheon/pull/1608) +- Avoid publishing docker-pantheon directory to bintray during a release [\#1606](https://github.com/PegaSysEng/pantheon/pull/1606) +- \[PAN-2756\] Istanbul scaffolding [\#1605](https://github.com/PegaSysEng/pantheon/pull/1605) +- Implement a timeout in TransactionMessageProcessor [\#1604](https://github.com/PegaSysEng/pantheon/pull/1604) +- Reject transactions with gas price below the configured minimum [\#1602](https://github.com/PegaSysEng/pantheon/pull/1602) +- Always build the k8s image, only push to dockerhub for master branch [\#1601](https://github.com/PegaSysEng/pantheon/pull/1601) +- Properly validate AltBN128 pairing precompile input [\#1600](https://github.com/PegaSysEng/pantheon/pull/1600) +- \[PAN-2871\] Columnar rocksdb [\#1599](https://github.com/PegaSysEng/pantheon/pull/1599) +- Reverting change to dockerfile [\#1594](https://github.com/PegaSysEng/pantheon/pull/1594) +- Update dependency versions [\#1592](https://github.com/PegaSysEng/pantheon/pull/1592) +- \[PAN-2797\] Clean up failed connections [\#1591](https://github.com/PegaSysEng/pantheon/pull/1591) +- Cleaning up the build process for docker [\#1590](https://github.com/PegaSysEng/pantheon/pull/1590) +- \[PAN-2786\] Stop Transaction Pool Queue from Growing Unbounded [\#1586](https://github.com/PegaSysEng/pantheon/pull/1586) ## 1.1.3 ### Additions and Improvements -- \[PAN-2811\] Be more lenient with discovery message deserialization. Completes our support for EIP-8 and enables Besu to work on Rinkeby again. [\#1580](https://github.com/PegaSysEng/pantheon/pull/1580) -- Added liveness and readiness probe stub endpoints [\#1553](https://github.com/PegaSysEng/pantheon/pull/1553) -- Implemented operator tool. \(blockchain network configuration for permissioned networks\) [\#1511](https://github.com/PegaSysEng/pantheon/pull/1511) +- \[PAN-2811\] Be more lenient with discovery message deserialization. Completes our support for EIP-8 and enables Besu to work on Rinkeby again. [\#1580](https://github.com/PegaSysEng/pantheon/pull/1580) +- Added liveness and readiness probe stub endpoints [\#1553](https://github.com/PegaSysEng/pantheon/pull/1553) +- Implemented operator tool. \(blockchain network configuration for permissioned networks\) [\#1511](https://github.com/PegaSysEng/pantheon/pull/1511) - \[PAN-2754\] Added eea\_getPrivacyPrecompileAddress [\#1579](https://github.com/PegaSysEng/pantheon/pull/1579) (thanks to [Puneetha17](https://github.com/Puneetha17)) - Publish the chain head gas used, gas limit, transaction count and ommer metrics [\#1551](https://github.com/PegaSysEng/pantheon/pull/1551) - Add subscribe and unsubscribe count metrics [\#1541](https://github.com/PegaSysEng/pantheon/pull/1541) - Add pivot block metrics [\#1537](https://github.com/PegaSysEng/pantheon/pull/1537) -Documentation updates include: +Documentation updates include: - Updated [IBFT 2.0 tutorial](https://besu.hyperledger.org/en/latest/Tutorials/Private-Network/Create-IBFT-Network/) to use network configuration tool -- Added [debug\_traceBlock\* methods](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#debug_traceblock) +- Added [debug\_traceBlock\* methods](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#debug_traceblock) - Reorganised [monitoring documentation](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Monitoring-Performance/) -- Added [link to sample Grafana dashboard](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Monitoring-Performance/#monitor-node-performance-using-prometheus) +- Added [link to sample Grafana dashboard](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Monitoring-Performance/#monitor-node-performance-using-prometheus) - Added [note about replacing transactions in transaction pool](https://besu.hyperledger.org/en/latest/Concepts/Transactions/Transaction-Pool/#replacing-transactions-with-same-nonce) - Updated [example transaction scripts](https://besu.hyperledger.org/en/latest/HowTo/Send-Transactions/Transactions/#example-javascript-scripts) - Updated [Alethio Ethstats and Explorer documentation](https://besu.hyperledger.org/en/latest/Concepts/AlethioOverview/) -### Technical Improvements +### Technical Improvements - PAN-2816: Hiding experimental account permissioning cli options [\#1584](https://github.com/PegaSysEng/pantheon/pull/1584) -- \[PAN-2630\] Synchronizer should disconnect the sync target peer on invalid block data [\#1578](https://github.com/PegaSysEng/pantheon/pull/1578) -- Rename MetricCategory to BesuMetricCategory [\#1574](https://github.com/PegaSysEng/pantheon/pull/1574) -- Convert MetricsConfigiguration to use a builder [\#1572](https://github.com/PegaSysEng/pantheon/pull/1572) -- PAN-2794: Including flag for onchain permissioning check on tx processor [\#1571](https://github.com/PegaSysEng/pantheon/pull/1571) -- Fix behaviour for absent account permissiong smart contract [\#1569](https://github.com/PegaSysEng/pantheon/pull/1569) -- Expand readiness check to check peer count and sync state [\#1568](https://github.com/PegaSysEng/pantheon/pull/1568) -- \[PAN-2798\] Reorganize p2p classes [\#1567](https://github.com/PegaSysEng/pantheon/pull/1567) -- PAN-2729: Account Smart Contract Permissioning ATs [\#1565](https://github.com/PegaSysEng/pantheon/pull/1565) -- Timeout build after 1 hour to prevent it hanging forever. [\#1564](https://github.com/PegaSysEng/pantheon/pull/1564) -- \[PAN-2791\] Make permissions checks for ongoing connections more granular [\#1563](https://github.com/PegaSysEng/pantheon/pull/1563) -- \[PAN-2721\] Fix TopicParameter deserialization [\#1562](https://github.com/PegaSysEng/pantheon/pull/1562) +- \[PAN-2630\] Synchronizer should disconnect the sync target peer on invalid block data [\#1578](https://github.com/PegaSysEng/pantheon/pull/1578) +- Rename MetricCategory to BesuMetricCategory [\#1574](https://github.com/PegaSysEng/pantheon/pull/1574) +- Convert MetricsConfigiguration to use a builder [\#1572](https://github.com/PegaSysEng/pantheon/pull/1572) +- PAN-2794: Including flag for onchain permissioning check on tx processor [\#1571](https://github.com/PegaSysEng/pantheon/pull/1571) +- Fix behaviour for absent account permissiong smart contract [\#1569](https://github.com/PegaSysEng/pantheon/pull/1569) +- Expand readiness check to check peer count and sync state [\#1568](https://github.com/PegaSysEng/pantheon/pull/1568) +- \[PAN-2798\] Reorganize p2p classes [\#1567](https://github.com/PegaSysEng/pantheon/pull/1567) +- PAN-2729: Account Smart Contract Permissioning ATs [\#1565](https://github.com/PegaSysEng/pantheon/pull/1565) +- Timeout build after 1 hour to prevent it hanging forever. [\#1564](https://github.com/PegaSysEng/pantheon/pull/1564) +- \[PAN-2791\] Make permissions checks for ongoing connections more granular [\#1563](https://github.com/PegaSysEng/pantheon/pull/1563) +- \[PAN-2721\] Fix TopicParameter deserialization [\#1562](https://github.com/PegaSysEng/pantheon/pull/1562) - \[PAN-2779\] Allow signing private transaction with any key [\#1561](https://github.com/PegaSysEng/pantheon/pull/1561) (thanks to [iikirilov](https://github.com/iikirilov)) -- \[PAN-2783\] Invert dependency between permissioning and p2p [\#1557](https://github.com/PegaSysEng/pantheon/pull/1557) -- Removing account filter from TransactionPool [\#1556](https://github.com/PegaSysEng/pantheon/pull/1556) -- \[PAN-1952\] - Remove ignored pending transaction event publish acceptance test [\#1552](https://github.com/PegaSysEng/pantheon/pull/1552) -- Make MetricCategories more flexible [\#1550](https://github.com/PegaSysEng/pantheon/pull/1550) -- Fix encoding for account permissioning check call [\#1549](https://github.com/PegaSysEng/pantheon/pull/1549) +- \[PAN-2783\] Invert dependency between permissioning and p2p [\#1557](https://github.com/PegaSysEng/pantheon/pull/1557) +- Removing account filter from TransactionPool [\#1556](https://github.com/PegaSysEng/pantheon/pull/1556) +- \[PAN-1952\] - Remove ignored pending transaction event publish acceptance test [\#1552](https://github.com/PegaSysEng/pantheon/pull/1552) +- Make MetricCategories more flexible [\#1550](https://github.com/PegaSysEng/pantheon/pull/1550) +- Fix encoding for account permissioning check call [\#1549](https://github.com/PegaSysEng/pantheon/pull/1549) - Discard known remote transactions prior to validation [\#1548](https://github.com/PegaSysEng/pantheon/pull/1548) -- \[PAN-2009\] - Fix cluster clean start after stop in Acceptance tests [\#1546](https://github.com/PegaSysEng/pantheon/pull/1546) -- FilterIdGenerator fixes [\#1544](https://github.com/PegaSysEng/pantheon/pull/1544) -- Only increment the added transaction counter if we actually added the transaction [\#1543](https://github.com/PegaSysEng/pantheon/pull/1543) -- When retrieving transactions by hash, check the pending transactions first [\#1542](https://github.com/PegaSysEng/pantheon/pull/1542) +- \[PAN-2009\] - Fix cluster clean start after stop in Acceptance tests [\#1546](https://github.com/PegaSysEng/pantheon/pull/1546) +- FilterIdGenerator fixes [\#1544](https://github.com/PegaSysEng/pantheon/pull/1544) +- Only increment the added transaction counter if we actually added the transaction [\#1543](https://github.com/PegaSysEng/pantheon/pull/1543) +- When retrieving transactions by hash, check the pending transactions first [\#1542](https://github.com/PegaSysEng/pantheon/pull/1542) - Fix thread safety in SubscriptionManager [\#1540](https://github.com/PegaSysEng/pantheon/pull/1540) -- \[PAN-2731\] Extract connection management from P2PNetwork [\#1538](https://github.com/PegaSysEng/pantheon/pull/1538) +- \[PAN-2731\] Extract connection management from P2PNetwork [\#1538](https://github.com/PegaSysEng/pantheon/pull/1538) - \[PAN-2010\] format filter id as quantity [\#1534](https://github.com/PegaSysEng/pantheon/pull/1534) -- PAN-2445: Onchain account permissioning [\#1507](https://github.com/PegaSysEng/pantheon/pull/1507) +- PAN-2445: Onchain account permissioning [\#1507](https://github.com/PegaSysEng/pantheon/pull/1507) - \[PAN-2672\] Return specific and useful error for enclave issues [\#1455](https://github.com/PegaSysEng/pantheon/pull/1455) (thanks to [Puneetha17](https://github.com/Puneetha17)) ## 1.1.2 ### Additions and Improvements -Documentation updates include: +Documentation updates include: -- Added [GraphQL options](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#graphql-http-cors-origins) +- Added [GraphQL options](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#graphql-http-cors-origins) - Added [troubleshooting point about illegal reflective access error](https://besu.hyperledger.org/en/latest/HowTo/Troubleshoot/Troubleshooting/#illegal-reflective-access-error-on-startup) - Added [trusted bootnode behaviour for permissioning](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Onchain-Permissioning/#bootnodes) - Added [how to obtain a WS authentication token](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/Authentication/#obtaining-an-authentication-token) - Updated [example scripts and added package.json file for creating signed transactions](https://besu.hyperledger.org/en/latest/HowTo/Send-Transactions/Transactions/) -### Technical Improvements +### Technical Improvements -- Replaced Void datatype with void [\#1530](https://github.com/PegaSysEng/pantheon/pull/1530) -- Fix estimate gas RPC failing for clique when no blocks have been created [\#1528](https://github.com/PegaSysEng/pantheon/pull/1528) +- Replaced Void datatype with void [\#1530](https://github.com/PegaSysEng/pantheon/pull/1530) +- Fix estimate gas RPC failing for clique when no blocks have been created [\#1528](https://github.com/PegaSysEng/pantheon/pull/1528) - Avoid auto-boxing for gauge metrics [\#1526](https://github.com/PegaSysEng/pantheon/pull/1526) -- Add AT to ensure 0-miner Clique/IBFT are valid [\#1525](https://github.com/PegaSysEng/pantheon/pull/1525) -- AT DSL - renaming to suffix of Conditions and co-locating with Conditions [\#1524](https://github.com/PegaSysEng/pantheon/pull/1524) -- Set disconnect flag immediately when disconnecting a peer [\#1521](https://github.com/PegaSysEng/pantheon/pull/1521) -- \[PAN-2547\] Modified JSON-RPC subscription processing to avoid blocking [\#1519](https://github.com/PegaSysEng/pantheon/pull/1519) -- Dependency Version Updates [\#1517](https://github.com/PegaSysEng/pantheon/pull/1517) +- Add AT to ensure 0-miner Clique/IBFT are valid [\#1525](https://github.com/PegaSysEng/pantheon/pull/1525) +- AT DSL - renaming to suffix of Conditions and co-locating with Conditions [\#1524](https://github.com/PegaSysEng/pantheon/pull/1524) +- Set disconnect flag immediately when disconnecting a peer [\#1521](https://github.com/PegaSysEng/pantheon/pull/1521) +- \[PAN-2547\] Modified JSON-RPC subscription processing to avoid blocking [\#1519](https://github.com/PegaSysEng/pantheon/pull/1519) +- Dependency Version Updates [\#1517](https://github.com/PegaSysEng/pantheon/pull/1517) - AT DSL - renaming ibft to ibft2 [\#1516](https://github.com/PegaSysEng/pantheon/pull/1516) -- \[PIE-1578\] Added local transaction permissioning metrics [\#1515](https://github.com/PegaSysEng/pantheon/pull/1515) -- \[PIE-1577\] Added node local metrics [\#1514](https://github.com/PegaSysEng/pantheon/pull/1514) -- AT DSL - Removing WaitCondition, consistently applying Condition instead [\#1513](https://github.com/PegaSysEng/pantheon/pull/1513) -- Remove usage of deprecated ConcurrentSet [\#1512](https://github.com/PegaSysEng/pantheon/pull/1512) -- Log error if clique or ibft have 0 validators in genesis [\#1509](https://github.com/PegaSysEng/pantheon/pull/1509) -- GraphQL library upgrade changes. [\#1508](https://github.com/PegaSysEng/pantheon/pull/1508) -- Add metrics to assist monitoring and alerting [\#1506](https://github.com/PegaSysEng/pantheon/pull/1506) -- Use external pantheon-plugin-api library [\#1505](https://github.com/PegaSysEng/pantheon/pull/1505) -- Tilde [\#1504](https://github.com/PegaSysEng/pantheon/pull/1504) -- Dependency version updates [\#1503](https://github.com/PegaSysEng/pantheon/pull/1503) +- \[PIE-1578\] Added local transaction permissioning metrics [\#1515](https://github.com/PegaSysEng/pantheon/pull/1515) +- \[PIE-1577\] Added node local metrics [\#1514](https://github.com/PegaSysEng/pantheon/pull/1514) +- AT DSL - Removing WaitCondition, consistently applying Condition instead [\#1513](https://github.com/PegaSysEng/pantheon/pull/1513) +- Remove usage of deprecated ConcurrentSet [\#1512](https://github.com/PegaSysEng/pantheon/pull/1512) +- Log error if clique or ibft have 0 validators in genesis [\#1509](https://github.com/PegaSysEng/pantheon/pull/1509) +- GraphQL library upgrade changes. [\#1508](https://github.com/PegaSysEng/pantheon/pull/1508) +- Add metrics to assist monitoring and alerting [\#1506](https://github.com/PegaSysEng/pantheon/pull/1506) +- Use external pantheon-plugin-api library [\#1505](https://github.com/PegaSysEng/pantheon/pull/1505) +- Tilde [\#1504](https://github.com/PegaSysEng/pantheon/pull/1504) +- Dependency version updates [\#1503](https://github.com/PegaSysEng/pantheon/pull/1503) - Simplify text [\#1501](https://github.com/PegaSysEng/pantheon/pull/1501) (thanks to [bgravenorst](https://github.com/bgravenorst)) -- \[PAN-1625\] Clique AT mining continues if validator offline [\#1500](https://github.com/PegaSysEng/pantheon/pull/1500) -- Acceptance Test DSL Node refactoring [\#1498](https://github.com/PegaSysEng/pantheon/pull/1498) +- \[PAN-1625\] Clique AT mining continues if validator offline [\#1500](https://github.com/PegaSysEng/pantheon/pull/1500) +- Acceptance Test DSL Node refactoring [\#1498](https://github.com/PegaSysEng/pantheon/pull/1498) - Updated an incorrect command [\#1497](https://github.com/PegaSysEng/pantheon/pull/1497) (thanks to [bgravenorst](https://github.com/bgravenorst)) -- Acceptance Test and DSL rename for IBFT2 [\#1493](https://github.com/PegaSysEng/pantheon/pull/1493) -- \[PIE-1580\] Metrics for smart contract permissioning actions [\#1492](https://github.com/PegaSysEng/pantheon/pull/1492) +- Acceptance Test and DSL rename for IBFT2 [\#1493](https://github.com/PegaSysEng/pantheon/pull/1493) +- \[PIE-1580\] Metrics for smart contract permissioning actions [\#1492](https://github.com/PegaSysEng/pantheon/pull/1492) - Handle RLPException when processing incoming DevP2P messages [\#1491](https://github.com/PegaSysEng/pantheon/pull/1491) -- Limit spotless checks to java classes in expected java dirs [\#1490](https://github.com/PegaSysEng/pantheon/pull/1490) -- \[PAN-2560\] Add LocalNode class [\#1489](https://github.com/PegaSysEng/pantheon/pull/1489) +- Limit spotless checks to java classes in expected java dirs [\#1490](https://github.com/PegaSysEng/pantheon/pull/1490) +- \[PAN-2560\] Add LocalNode class [\#1489](https://github.com/PegaSysEng/pantheon/pull/1489) - Changed Enode length error String implementation. [\#1486](https://github.com/PegaSysEng/pantheon/pull/1486) - PAN-2715 - return block not found reasons in error [\#1485](https://github.com/PegaSysEng/pantheon/pull/1485) - \[PAN-2652\] Refactor Privacy acceptance test and add Privacy Ibft test [\#1483](https://github.com/PegaSysEng/pantheon/pull/1483) (thanks to [iikirilov](https://github.com/iikirilov)) -- \[PAN-2603\] Onchain account permissioning support [\#1475](https://github.com/PegaSysEng/pantheon/pull/1475) +- \[PAN-2603\] Onchain account permissioning support [\#1475](https://github.com/PegaSysEng/pantheon/pull/1475) - Make CLI options names with hyphen-minus searchable and reduce index size [\#1476](https://github.com/PegaSysEng/pantheon/pull/1476) - Added warning banner when using latest version [\#1454](https://github.com/PegaSysEng/pantheon/pull/1454) -- Add RTD config file to fix Python version issue [\#1453](https://github.com/PegaSysEng/pantheon/pull/1453) +- Add RTD config file to fix Python version issue [\#1453](https://github.com/PegaSysEng/pantheon/pull/1453) - \[PAN-2647\] Validate Private Transaction nonce before submitting to Transaction Pool [\#1449](https://github.com/PegaSysEng/pantheon/pull/1449) (thanks to [iikirilov](https://github.com/iikirilov)) -- Add placeholders system to have global variables in markdown [\#1425](https://github.com/PegaSysEng/pantheon/pull/1425) +- Add placeholders system to have global variables in markdown [\#1425](https://github.com/PegaSysEng/pantheon/pull/1425) -## 1.1.1 +## 1.1.1 -### Additions and Improvements +### Additions and Improvements - [GraphQL](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/GraphQL/) [\#1311](https://github.com/PegaSysEng/pantheon/pull/1311) (thanks to [zyfrank](https://github.com/zyfrank)) - Added [`--tx-pool-retention-hours`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#tx-pool-retention-hours) [\#1333](https://github.com/PegaSysEng/pantheon/pull/1333) -- Added Genesis file support for specifying the maximum stack size. [\#1431](https://github.com/PegaSysEng/pantheon/pull/1431) -- Included transaction details when subscribed to Pending transactions [\#1410](https://github.com/PegaSysEng/pantheon/pull/1410) +- Added Genesis file support for specifying the maximum stack size. [\#1431](https://github.com/PegaSysEng/pantheon/pull/1431) +- Included transaction details when subscribed to Pending transactions [\#1410](https://github.com/PegaSysEng/pantheon/pull/1410) - Documentation updates include: - [Added configuration items specified in the genesis file](https://besu.hyperledger.org/en/latest/Reference/Config-Items/#configuration-items) - - [Added pending transaction details subscription](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/RPC-PubSub/#pending-transactionss) + - [Added pending transaction details subscription](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/RPC-PubSub/#pending-transactionss) - [Added Troubleshooting content](https://besu.hyperledger.org/en/latest/HowTo/Troubleshoot/Troubleshooting/) - [Added Privacy Quickstart](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Privacy-Quickstart/) - [Added privacy roadmap](https://github.com/hyperledger/besu/blob/master/ROADMAP.md) -### Technical Improvements +### Technical Improvements -- Create MaintainedPeers class [\#1484](https://github.com/PegaSysEng/pantheon/pull/1484) -- Fix for permissioned network with single bootnode [\#1479](https://github.com/PegaSysEng/pantheon/pull/1479) -- Have ThreadBesuNodeRunner support plugin tests [\#1477](https://github.com/PegaSysEng/pantheon/pull/1477) -- Less pointless plugins errors [\#1473](https://github.com/PegaSysEng/pantheon/pull/1473) -- Rename GraphQLRPC to just GraphQL [\#1472](https://github.com/PegaSysEng/pantheon/pull/1472) -- eth\_protocolVersion is a Quantity, not an Integer [\#1470](https://github.com/PegaSysEng/pantheon/pull/1470) -- Don't require 'to' in 'blocks' queries [\#1464](https://github.com/PegaSysEng/pantheon/pull/1464) -- Events Plugin - Add initial "NewBlock" event message [\#1463](https://github.com/PegaSysEng/pantheon/pull/1463) +- Create MaintainedPeers class [\#1484](https://github.com/PegaSysEng/pantheon/pull/1484) +- Fix for permissioned network with single bootnode [\#1479](https://github.com/PegaSysEng/pantheon/pull/1479) +- Have ThreadBesuNodeRunner support plugin tests [\#1477](https://github.com/PegaSysEng/pantheon/pull/1477) +- Less pointless plugins errors [\#1473](https://github.com/PegaSysEng/pantheon/pull/1473) +- Rename GraphQLRPC to just GraphQL [\#1472](https://github.com/PegaSysEng/pantheon/pull/1472) +- eth\_protocolVersion is a Quantity, not an Integer [\#1470](https://github.com/PegaSysEng/pantheon/pull/1470) +- Don't require 'to' in 'blocks' queries [\#1464](https://github.com/PegaSysEng/pantheon/pull/1464) +- Events Plugin - Add initial "NewBlock" event message [\#1463](https://github.com/PegaSysEng/pantheon/pull/1463) - Make restriction field in Private Transaction an enum [\#1462](https://github.com/PegaSysEng/pantheon/pull/1462) (thanks to [iikirilov](https://github.com/iikirilov)) -- Helpful graphql error when an account doesn't exist [\#1460](https://github.com/PegaSysEng/pantheon/pull/1460) -- Acceptance Test Cleanup [\#1458](https://github.com/PegaSysEng/pantheon/pull/1458) -- Large chain id support for private transactions [\#1452](https://github.com/PegaSysEng/pantheon/pull/1452) -- Optimise TransactionPool.addRemoteTransaction [\#1448](https://github.com/PegaSysEng/pantheon/pull/1448) -- Reduce synchronization in PendingTransactions [\#1447](https://github.com/PegaSysEng/pantheon/pull/1447) -- Add simple PeerPermissions interface [\#1446](https://github.com/PegaSysEng/pantheon/pull/1446) -- Make sure ThreadBesuNodeRunner is exercised by automation [\#1442](https://github.com/PegaSysEng/pantheon/pull/1442) -- Decode devp2p packets off the event thread [\#1439](https://github.com/PegaSysEng/pantheon/pull/1439) -- Allow config files to specify no bootnodes [\#1438](https://github.com/PegaSysEng/pantheon/pull/1438) -- Capture all logs and errors in the Besu log output [\#1437](https://github.com/PegaSysEng/pantheon/pull/1437) +- Helpful graphql error when an account doesn't exist [\#1460](https://github.com/PegaSysEng/pantheon/pull/1460) +- Acceptance Test Cleanup [\#1458](https://github.com/PegaSysEng/pantheon/pull/1458) +- Large chain id support for private transactions [\#1452](https://github.com/PegaSysEng/pantheon/pull/1452) +- Optimise TransactionPool.addRemoteTransaction [\#1448](https://github.com/PegaSysEng/pantheon/pull/1448) +- Reduce synchronization in PendingTransactions [\#1447](https://github.com/PegaSysEng/pantheon/pull/1447) +- Add simple PeerPermissions interface [\#1446](https://github.com/PegaSysEng/pantheon/pull/1446) +- Make sure ThreadBesuNodeRunner is exercised by automation [\#1442](https://github.com/PegaSysEng/pantheon/pull/1442) +- Decode devp2p packets off the event thread [\#1439](https://github.com/PegaSysEng/pantheon/pull/1439) +- Allow config files to specify no bootnodes [\#1438](https://github.com/PegaSysEng/pantheon/pull/1438) +- Capture all logs and errors in the Besu log output [\#1437](https://github.com/PegaSysEng/pantheon/pull/1437) - Ensure failed Txns are deleted when detected during mining [\#1436](https://github.com/PegaSysEng/pantheon/pull/1436) -- Plugin Framework [\#1435](https://github.com/PegaSysEng/pantheon/pull/1435) -- Equals cleanup [\#1434](https://github.com/PegaSysEng/pantheon/pull/1434) -- Transaction smart contract permissioning controller [\#1433](https://github.com/PegaSysEng/pantheon/pull/1433) -- Renamed AccountPermissioningProver to TransactionPermissio… [\#1432](https://github.com/PegaSysEng/pantheon/pull/1432) -- Refactorings and additions to add Account based Smart Contract permissioning [\#1430](https://github.com/PegaSysEng/pantheon/pull/1430) -- Fix p2p PeerInfo handling [\#1428](https://github.com/PegaSysEng/pantheon/pull/1428) -- IbftProcessor logs when a throwable terminates mining [\#1427](https://github.com/PegaSysEng/pantheon/pull/1427) -- Renamed AccountWhitelistController [\#1424](https://github.com/PegaSysEng/pantheon/pull/1424) -- Unwrap DelegatingBytes32 and prevent Hash from wrapping other Hash instances [\#1423](https://github.com/PegaSysEng/pantheon/pull/1423) -- If nonce is invalid, do not delete during mining [\#1422](https://github.com/PegaSysEng/pantheon/pull/1422) -- Deleting unused windows jenkinsfile [\#1421](https://github.com/PegaSysEng/pantheon/pull/1421) +- Plugin Framework [\#1435](https://github.com/PegaSysEng/pantheon/pull/1435) +- Equals cleanup [\#1434](https://github.com/PegaSysEng/pantheon/pull/1434) +- Transaction smart contract permissioning controller [\#1433](https://github.com/PegaSysEng/pantheon/pull/1433) +- Renamed AccountPermissioningProver to TransactionPermissio… [\#1432](https://github.com/PegaSysEng/pantheon/pull/1432) +- Refactorings and additions to add Account based Smart Contract permissioning [\#1430](https://github.com/PegaSysEng/pantheon/pull/1430) +- Fix p2p PeerInfo handling [\#1428](https://github.com/PegaSysEng/pantheon/pull/1428) +- IbftProcessor logs when a throwable terminates mining [\#1427](https://github.com/PegaSysEng/pantheon/pull/1427) +- Renamed AccountWhitelistController [\#1424](https://github.com/PegaSysEng/pantheon/pull/1424) +- Unwrap DelegatingBytes32 and prevent Hash from wrapping other Hash instances [\#1423](https://github.com/PegaSysEng/pantheon/pull/1423) +- If nonce is invalid, do not delete during mining [\#1422](https://github.com/PegaSysEng/pantheon/pull/1422) +- Deleting unused windows jenkinsfile [\#1421](https://github.com/PegaSysEng/pantheon/pull/1421) - Get all our smoke tests for all platforms in 1 jenkins job [\#1420](https://github.com/PegaSysEng/pantheon/pull/1420) - Add pending object to GraphQL queries [\#1419](https://github.com/PegaSysEng/pantheon/pull/1419) -- Start listening for p2p connections after start\(\) is invoked [\#1418](https://github.com/PegaSysEng/pantheon/pull/1418) -- Improved JSON-RPC responses when EnodeURI parameter has invalid EnodeId [\#1417](https://github.com/PegaSysEng/pantheon/pull/1417) +- Start listening for p2p connections after start\(\) is invoked [\#1418](https://github.com/PegaSysEng/pantheon/pull/1418) +- Improved JSON-RPC responses when EnodeURI parameter has invalid EnodeId [\#1417](https://github.com/PegaSysEng/pantheon/pull/1417) - Use port 0 when starting a websocket server in tests [\#1416](https://github.com/PegaSysEng/pantheon/pull/1416) -- Windows jdk smoke tests [\#1413](https://github.com/PegaSysEng/pantheon/pull/1413) -- Change AT discard RPC tests to be more reliable by checking discard using proposals [\#1411](https://github.com/PegaSysEng/pantheon/pull/1411) -- Simple account permissioning [\#1409](https://github.com/PegaSysEng/pantheon/pull/1409) -- Fix clique miner to respect changes to vanity data made via JSON-RPC [\#1408](https://github.com/PegaSysEng/pantheon/pull/1408) -- Avoid recomputing the logs bloom filter when reading receipts [\#1407](https://github.com/PegaSysEng/pantheon/pull/1407) -- Remove NodePermissioningLocalConfig external references [\#1406](https://github.com/PegaSysEng/pantheon/pull/1406) -- Add constantinople fix block for Rinkeby [\#1404](https://github.com/PegaSysEng/pantheon/pull/1404) -- Update EnodeURL to support enodes with listening disabled [\#1403](https://github.com/PegaSysEng/pantheon/pull/1403) -- Integration Integration test\(s\) on p2p of 'net\_services' [\#1402](https://github.com/PegaSysEng/pantheon/pull/1402) +- Windows jdk smoke tests [\#1413](https://github.com/PegaSysEng/pantheon/pull/1413) +- Change AT discard RPC tests to be more reliable by checking discard using proposals [\#1411](https://github.com/PegaSysEng/pantheon/pull/1411) +- Simple account permissioning [\#1409](https://github.com/PegaSysEng/pantheon/pull/1409) +- Fix clique miner to respect changes to vanity data made via JSON-RPC [\#1408](https://github.com/PegaSysEng/pantheon/pull/1408) +- Avoid recomputing the logs bloom filter when reading receipts [\#1407](https://github.com/PegaSysEng/pantheon/pull/1407) +- Remove NodePermissioningLocalConfig external references [\#1406](https://github.com/PegaSysEng/pantheon/pull/1406) +- Add constantinople fix block for Rinkeby [\#1404](https://github.com/PegaSysEng/pantheon/pull/1404) +- Update EnodeURL to support enodes with listening disabled [\#1403](https://github.com/PegaSysEng/pantheon/pull/1403) +- Integration Integration test\(s\) on p2p of 'net\_services' [\#1402](https://github.com/PegaSysEng/pantheon/pull/1402) - Reference tests fail on Windows [\#1401](https://github.com/PegaSysEng/pantheon/pull/1401) -- Fix non-deterministic test caused by variable size of generated transactions [\#1399](https://github.com/PegaSysEng/pantheon/pull/1399) -- Start BlockPropagationManager immediately - don't wait for full sync [\#1398](https://github.com/PegaSysEng/pantheon/pull/1398) -- Added error message for RPC method disabled [\#1396](https://github.com/PegaSysEng/pantheon/pull/1396) -- Fix intermittency in FullSyncChainDownloaderTest [\#1394](https://github.com/PegaSysEng/pantheon/pull/1394) -- Add explanatory comment about default port [\#1392](https://github.com/PegaSysEng/pantheon/pull/1392) -- Handle case where peers advertise a listening port of 0 [\#1391](https://github.com/PegaSysEng/pantheon/pull/1391) +- Fix non-deterministic test caused by variable size of generated transactions [\#1399](https://github.com/PegaSysEng/pantheon/pull/1399) +- Start BlockPropagationManager immediately - don't wait for full sync [\#1398](https://github.com/PegaSysEng/pantheon/pull/1398) +- Added error message for RPC method disabled [\#1396](https://github.com/PegaSysEng/pantheon/pull/1396) +- Fix intermittency in FullSyncChainDownloaderTest [\#1394](https://github.com/PegaSysEng/pantheon/pull/1394) +- Add explanatory comment about default port [\#1392](https://github.com/PegaSysEng/pantheon/pull/1392) +- Handle case where peers advertise a listening port of 0 [\#1391](https://github.com/PegaSysEng/pantheon/pull/1391) - Cache extra data [\#1389](https://github.com/PegaSysEng/pantheon/pull/1389) - Update Log message in IBFT Controller [\#1387](https://github.com/PegaSysEng/pantheon/pull/1387) -- Remove unnecessary field [\#1384](https://github.com/PegaSysEng/pantheon/pull/1384) -- Add getPeer method to PeerConnection [\#1383](https://github.com/PegaSysEng/pantheon/pull/1383) +- Remove unnecessary field [\#1384](https://github.com/PegaSysEng/pantheon/pull/1384) +- Add getPeer method to PeerConnection [\#1383](https://github.com/PegaSysEng/pantheon/pull/1383) - Removing smart quotes [\#1381](https://github.com/PegaSysEng/pantheon/pull/1381) (thanks to [jmcnevin](https://github.com/jmcnevin)) -- Use streams and avoid iterating child nodes multiple times [\#1380](https://github.com/PegaSysEng/pantheon/pull/1380) -- Use execute instead of submit so unhandled exceptions get logged [\#1379](https://github.com/PegaSysEng/pantheon/pull/1379) +- Use streams and avoid iterating child nodes multiple times [\#1380](https://github.com/PegaSysEng/pantheon/pull/1380) +- Use execute instead of submit so unhandled exceptions get logged [\#1379](https://github.com/PegaSysEng/pantheon/pull/1379) - Prefer EnodeURL over Endpoint [\#1378](https://github.com/PegaSysEng/pantheon/pull/1378) - Add flat file based task collection [\#1377](https://github.com/PegaSysEng/pantheon/pull/1377) -- Consolidate local enode representation [\#1376](https://github.com/PegaSysEng/pantheon/pull/1376) -- Rename rocksdDbConfiguration to rocksDbConfiguration [\#1375](https://github.com/PegaSysEng/pantheon/pull/1375) -- Remove EthTaskChainDownloader and supporting code [\#1373](https://github.com/PegaSysEng/pantheon/pull/1373) -- Handle the pipeline being aborted while finalizing an async operation [\#1372](https://github.com/PegaSysEng/pantheon/pull/1372) -- Rename methods that create and return streams away from getX\(\) [\#1368](https://github.com/PegaSysEng/pantheon/pull/1368) +- Consolidate local enode representation [\#1376](https://github.com/PegaSysEng/pantheon/pull/1376) +- Rename rocksdDbConfiguration to rocksDbConfiguration [\#1375](https://github.com/PegaSysEng/pantheon/pull/1375) +- Remove EthTaskChainDownloader and supporting code [\#1373](https://github.com/PegaSysEng/pantheon/pull/1373) +- Handle the pipeline being aborted while finalizing an async operation [\#1372](https://github.com/PegaSysEng/pantheon/pull/1372) +- Rename methods that create and return streams away from getX\(\) [\#1368](https://github.com/PegaSysEng/pantheon/pull/1368) - eea\_getTransactionCount fails if account has not interacted with private state [\#1367](https://github.com/PegaSysEng/pantheon/pull/1367) (thanks to [iikirilov](https://github.com/iikirilov)) - Increase RocksDB settings [\#1364](https://github.com/PegaSysEng/pantheon/pull/1364) ([ajsutton](https://github.com/ajsutton)) -- Don't abort in-progress master builds when a new commit is added. [\#1358](https://github.com/PegaSysEng/pantheon/pull/1358) -- Request open ended headers from sync target [\#1355](https://github.com/PegaSysEng/pantheon/pull/1355) -- Enable the pipeline chain downloader by default [\#1344](https://github.com/PegaSysEng/pantheon/pull/1344) +- Don't abort in-progress master builds when a new commit is added. [\#1358](https://github.com/PegaSysEng/pantheon/pull/1358) +- Request open ended headers from sync target [\#1355](https://github.com/PegaSysEng/pantheon/pull/1355) +- Enable the pipeline chain downloader by default [\#1344](https://github.com/PegaSysEng/pantheon/pull/1344) - Create P2PNetwork Builder [\#1343](https://github.com/PegaSysEng/pantheon/pull/1343) - Include static nodes in permissioning logic [\#1339](https://github.com/PegaSysEng/pantheon/pull/1339) - JsonRpcError decoding to include message [\#1336](https://github.com/PegaSysEng/pantheon/pull/1336) -- Cache current chain head info [\#1335](https://github.com/PegaSysEng/pantheon/pull/1335) -- Queue pending requests when all peers are busy [\#1331](https://github.com/PegaSysEng/pantheon/pull/1331) -- Fix failed tests on Windows [\#1332](https://github.com/PegaSysEng/pantheon/pull/1332) -- Provide error message when invalid key specified in key file [\#1328](https://github.com/PegaSysEng/pantheon/pull/1328) -- Allow whitespace in file paths loaded from resources directory [\#1329](https://github.com/PegaSysEng/pantheon/pull/1329) +- Cache current chain head info [\#1335](https://github.com/PegaSysEng/pantheon/pull/1335) +- Queue pending requests when all peers are busy [\#1331](https://github.com/PegaSysEng/pantheon/pull/1331) +- Fix failed tests on Windows [\#1332](https://github.com/PegaSysEng/pantheon/pull/1332) +- Provide error message when invalid key specified in key file [\#1328](https://github.com/PegaSysEng/pantheon/pull/1328) +- Allow whitespace in file paths loaded from resources directory [\#1329](https://github.com/PegaSysEng/pantheon/pull/1329) - Allow whitespace in path [\#1327](https://github.com/PegaSysEng/pantheon/pull/1327) -- Require block numbers for debug\_traceBlockByNumber to be in hex [\#1326](https://github.com/PegaSysEng/pantheon/pull/1326) -- Improve logging of chain download errors in the pipeline chain downloader [\#1325](https://github.com/PegaSysEng/pantheon/pull/1325) -- Ensure eth scheduler is stopped in tests [\#1324](https://github.com/PegaSysEng/pantheon/pull/1324) -- Normalize account permissioning addresses in whitelist [\#1321](https://github.com/PegaSysEng/pantheon/pull/1321) +- Require block numbers for debug\_traceBlockByNumber to be in hex [\#1326](https://github.com/PegaSysEng/pantheon/pull/1326) +- Improve logging of chain download errors in the pipeline chain downloader [\#1325](https://github.com/PegaSysEng/pantheon/pull/1325) +- Ensure eth scheduler is stopped in tests [\#1324](https://github.com/PegaSysEng/pantheon/pull/1324) +- Normalize account permissioning addresses in whitelist [\#1321](https://github.com/PegaSysEng/pantheon/pull/1321) - Allow private contract invocations in multiple privacy groups [\#1318](https://github.com/PegaSysEng/pantheon/pull/1318) (thanks to [iikirilov](https://github.com/iikirilov)) -- Fix account permissioning check case matching [\#1315](https://github.com/PegaSysEng/pantheon/pull/1315) -- Use header validation mode for ommers [\#1313](https://github.com/PegaSysEng/pantheon/pull/1313) -- Configure RocksDb max background compaction and thread count [\#1312](https://github.com/PegaSysEng/pantheon/pull/1312) -- Missing p2p info when queried live [\#1310](https://github.com/PegaSysEng/pantheon/pull/1310) -- Tx limit size send peers follow up [\#1308](https://github.com/PegaSysEng/pantheon/pull/1308) -- Remove remnants of the old dev mode [\#1307](https://github.com/PegaSysEng/pantheon/pull/1307) -- Remove duplicate init code from BesuController instances [\#1305](https://github.com/PegaSysEng/pantheon/pull/1305) -- Stop synchronizer prior to stopping the network [\#1302](https://github.com/PegaSysEng/pantheon/pull/1302) -- Evict old transactions [\#1299](https://github.com/PegaSysEng/pantheon/pull/1299) -- Send local transactions to new peers [\#1253](https://github.com/PegaSysEng/pantheon/pull/1253) - -## 1.1 - -### Additions and Improvements - -- [Privacy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Privacy-Overview/) +- Fix account permissioning check case matching [\#1315](https://github.com/PegaSysEng/pantheon/pull/1315) +- Use header validation mode for ommers [\#1313](https://github.com/PegaSysEng/pantheon/pull/1313) +- Configure RocksDb max background compaction and thread count [\#1312](https://github.com/PegaSysEng/pantheon/pull/1312) +- Missing p2p info when queried live [\#1310](https://github.com/PegaSysEng/pantheon/pull/1310) +- Tx limit size send peers follow up [\#1308](https://github.com/PegaSysEng/pantheon/pull/1308) +- Remove remnants of the old dev mode [\#1307](https://github.com/PegaSysEng/pantheon/pull/1307) +- Remove duplicate init code from BesuController instances [\#1305](https://github.com/PegaSysEng/pantheon/pull/1305) +- Stop synchronizer prior to stopping the network [\#1302](https://github.com/PegaSysEng/pantheon/pull/1302) +- Evict old transactions [\#1299](https://github.com/PegaSysEng/pantheon/pull/1299) +- Send local transactions to new peers [\#1253](https://github.com/PegaSysEng/pantheon/pull/1253) + +## 1.1 + +### Additions and Improvements + +- [Privacy](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Privacy-Overview/) - [Onchain Permissioning](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Permissioning-Overview/#onchain) -- [Fastsync](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#fast-sync-min-peers) -- Documentation updates include: - - Added JSON-RPC methods: +- [Fastsync](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#fast-sync-min-peers) +- Documentation updates include: + - Added JSON-RPC methods: - [`txpool_pantheonStatistics`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#txpool_besustatistics) - [`net_services`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#net_services) - [Updated to indicate Docker image doesn't run on Windows](https://besu.hyperledger.org/en/latest/HowTo/Get-Started/Run-Docker-Image/) - - [Added how to configure a free gas network](https://besu.hyperledger.org/en/latest/HowTo/Configure/FreeGas/) + - [Added how to configure a free gas network](https://besu.hyperledger.org/en/latest/HowTo/Configure/FreeGas/) -### Technical Improvements +### Technical Improvements -- priv_getTransactionCount fails if account has not interacted with private state [\#1369](https://github.com/PegaSysEng/pantheon/pull/1369) +- priv_getTransactionCount fails if account has not interacted with private state [\#1369](https://github.com/PegaSysEng/pantheon/pull/1369) - Updating Orion to 0.9.0 [\#1360](https://github.com/PegaSysEng/pantheon/pull/1360) - Allow use of large chain IDs [\#1357](https://github.com/PegaSysEng/pantheon/pull/1357) - Allow private contract invocations in multiple privacy groups [\#1340](https://github.com/PegaSysEng/pantheon/pull/1340) @@ -919,58 +994,58 @@ Documentation updates include: - Fix account permissioning check case matching [\#1315](https://github.com/PegaSysEng/pantheon/pull/1315) - Removing 'all' from the help wording for host-whitelist [\#1304](https://github.com/PegaSysEng/pantheon/pull/1304) -## 1.1 RC +## 1.1 RC -### Technical Improvements +### Technical Improvements -- Better errors for when permissioning contract is set up wrong [\#1296](https://github.com/PegaSysEng/pantheon/pull/1296) -- Consolidate p2p node info methods [\#1288](https://github.com/PegaSysEng/pantheon/pull/1288) -- Update permissioning smart contract interface to match updated EEA proposal [\#1287](https://github.com/PegaSysEng/pantheon/pull/1287) -- Switch to new sync target if it exceeds the td threshold [\#1286](https://github.com/PegaSysEng/pantheon/pull/1286) -- Fix running ATs with in-process node runner [\#1285](https://github.com/PegaSysEng/pantheon/pull/1285) -- Simplify enode construction [\#1283](https://github.com/PegaSysEng/pantheon/pull/1283) -- Cleanup PeerConnection interface [\#1282](https://github.com/PegaSysEng/pantheon/pull/1282) -- Undo changes to PendingTransactions method visibility [\#1281](https://github.com/PegaSysEng/pantheon/pull/1281) +- Better errors for when permissioning contract is set up wrong [\#1296](https://github.com/PegaSysEng/pantheon/pull/1296) +- Consolidate p2p node info methods [\#1288](https://github.com/PegaSysEng/pantheon/pull/1288) +- Update permissioning smart contract interface to match updated EEA proposal [\#1287](https://github.com/PegaSysEng/pantheon/pull/1287) +- Switch to new sync target if it exceeds the td threshold [\#1286](https://github.com/PegaSysEng/pantheon/pull/1286) +- Fix running ATs with in-process node runner [\#1285](https://github.com/PegaSysEng/pantheon/pull/1285) +- Simplify enode construction [\#1283](https://github.com/PegaSysEng/pantheon/pull/1283) +- Cleanup PeerConnection interface [\#1282](https://github.com/PegaSysEng/pantheon/pull/1282) +- Undo changes to PendingTransactions method visibility [\#1281](https://github.com/PegaSysEng/pantheon/pull/1281) - Use default enclave public key to generate eea_getTransactionReceipt [\#1280](https://github.com/PegaSysEng/pantheon/pull/1280) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Rollback to rocksdb 5.15.10 [\#1279](https://github.com/PegaSysEng/pantheon/pull/1279) -- Log error when a JSON decode problem is encountered [\#1278](https://github.com/PegaSysEng/pantheon/pull/1278) -- Create EnodeURL builder [\#1275](https://github.com/PegaSysEng/pantheon/pull/1275) -- Keep enode nodeId stored as a BytesValue [\#1274](https://github.com/PegaSysEng/pantheon/pull/1274) -- Feature/move subclass in pantheon command [\#1272](https://github.com/PegaSysEng/pantheon/pull/1272) -- Expose sync mode option [\#1270](https://github.com/PegaSysEng/pantheon/pull/1270) -- Refactor RocksDBStats [\#1266](https://github.com/PegaSysEng/pantheon/pull/1266) -- Normalize EnodeURLs [\#1264](https://github.com/PegaSysEng/pantheon/pull/1264) -- Build broken in Java 12 [\#1263](https://github.com/PegaSysEng/pantheon/pull/1263) -- Make PeerDiscovertAgentTest less flakey [\#1262](https://github.com/PegaSysEng/pantheon/pull/1262) -- Ignore extra json rpc params [\#1261](https://github.com/PegaSysEng/pantheon/pull/1261) -- Fetch local transactions in isolation [\#1259](https://github.com/PegaSysEng/pantheon/pull/1259) -- Update to debug trace transaction [\#1258](https://github.com/PegaSysEng/pantheon/pull/1258) +- Rollback to rocksdb 5.15.10 [\#1279](https://github.com/PegaSysEng/pantheon/pull/1279) +- Log error when a JSON decode problem is encountered [\#1278](https://github.com/PegaSysEng/pantheon/pull/1278) +- Create EnodeURL builder [\#1275](https://github.com/PegaSysEng/pantheon/pull/1275) +- Keep enode nodeId stored as a BytesValue [\#1274](https://github.com/PegaSysEng/pantheon/pull/1274) +- Feature/move subclass in pantheon command [\#1272](https://github.com/PegaSysEng/pantheon/pull/1272) +- Expose sync mode option [\#1270](https://github.com/PegaSysEng/pantheon/pull/1270) +- Refactor RocksDBStats [\#1266](https://github.com/PegaSysEng/pantheon/pull/1266) +- Normalize EnodeURLs [\#1264](https://github.com/PegaSysEng/pantheon/pull/1264) +- Build broken in Java 12 [\#1263](https://github.com/PegaSysEng/pantheon/pull/1263) +- Make PeerDiscovertAgentTest less flakey [\#1262](https://github.com/PegaSysEng/pantheon/pull/1262) +- Ignore extra json rpc params [\#1261](https://github.com/PegaSysEng/pantheon/pull/1261) +- Fetch local transactions in isolation [\#1259](https://github.com/PegaSysEng/pantheon/pull/1259) +- Update to debug trace transaction [\#1258](https://github.com/PegaSysEng/pantheon/pull/1258) - Use labelled timer to differentiate between rocks db metrics [\#1254](https://github.com/PegaSysEng/pantheon/pull/1254) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Migrate TransactionPool (& affiliated test) from 'core' to 'eth' [\#1251](https://github.com/PegaSysEng/pantheon/pull/1251) +- Migrate TransactionPool (& affiliated test) from 'core' to 'eth' [\#1251](https://github.com/PegaSysEng/pantheon/pull/1251) - Use single instance of Rocksdb for privacy [\#1247](https://github.com/PegaSysEng/pantheon/pull/1247) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Subscribing to sync events should receive false when in sync [\#1240](https://github.com/PegaSysEng/pantheon/pull/1240) -- Ignore transactions from the network while behind chain head [\#1228](https://github.com/PegaSysEng/pantheon/pull/1228) -- RocksDB Statistics in Metrics [\#1169](https://github.com/PegaSysEng/pantheon/pull/1169) +- Subscribing to sync events should receive false when in sync [\#1240](https://github.com/PegaSysEng/pantheon/pull/1240) +- Ignore transactions from the network while behind chain head [\#1228](https://github.com/PegaSysEng/pantheon/pull/1228) +- RocksDB Statistics in Metrics [\#1169](https://github.com/PegaSysEng/pantheon/pull/1169) - Add block trace RPC methods [\#1088](https://github.com/PegaSysEng/pantheon/pull/1088) (thanks to [kziemianek](https://github.com/kziemianek)) ## 1.0.3 -### Additions and Improvements +### Additions and Improvements -- Notify of dropped messages [\#1156](https://github.com/PegaSysEng/pantheon/pull/1156) -- Documentation updates include: +- Notify of dropped messages [\#1156](https://github.com/PegaSysEng/pantheon/pull/1156) +- Documentation updates include: - Added [Permissioning Overview](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Permissioning-Overview/) - Added content on [Network vs Node Configuration](https://besu.hyperledger.org/en/latest/HowTo/Configure/Using-Configuration-File/) - Updated [RAM requirements](https://besu.hyperledger.org/en/latest/HowTo/Get-Started/System-Requirements/#ram) - Added [Privacy Overview](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Privacy-Overview/) and [Processing Private Transactions](https://besu.hyperledger.org/en/latest/Concepts/Privacy/Private-Transaction-Processing/) - Renaming of Ethstats Lite Explorer to [Ethereum Lite Explorer](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Lite-Block-Explorer/#lite-block-explorer-documentation) (thanks to [tzapu](https://github.com/tzapu)) - Added content on using [Truffle with Besu](https://besu.hyperledger.org/en/latest/HowTo/Develop-Dapps/Truffle/) - - Added [`droppedPendingTransactions` RPC Pub/Sub subscription](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/RPC-PubSub/#dropped-transactions) + - Added [`droppedPendingTransactions` RPC Pub/Sub subscription](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/RPC-PubSub/#dropped-transactions) - Added [`eea_*` JSON-RPC API methods](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#eea-methods) - - Added [architecture diagram](https://besu.hyperledger.org/en/latest/Concepts/ArchitectureOverview/) + - Added [architecture diagram](https://besu.hyperledger.org/en/latest/Concepts/ArchitectureOverview/) - Updated [permissioning CLI options](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#permissions-accounts-config-file-enabled) and [permissioned network tutorial](https://besu.hyperledger.org/en/stable/) -### Technical Improvements +### Technical Improvements - Choose sync target based on td rather than height [\#1256](https://github.com/PegaSysEng/pantheon/pull/1256) - CLI ewp options [\#1246](https://github.com/PegaSysEng/pantheon/pull/1246) @@ -982,209 +1057,209 @@ Documentation updates include: - Make contract size limit configurable [\#1227](https://github.com/PegaSysEng/pantheon/pull/1227) - Refactor PrivacyParameters config to use builder pattern [\#1226](https://github.com/PegaSysEng/pantheon/pull/1226) (thanks to [antonydenyer](https://github.com/antonydenyer)) - Different request limits for different request types [\#1224](https://github.com/PegaSysEng/pantheon/pull/1224) -- Finish off fast sync pipeline download [\#1222](https://github.com/PegaSysEng/pantheon/pull/1222) -- Enable fast-sync options on command line [\#1218](https://github.com/PegaSysEng/pantheon/pull/1218) -- Replace filtering headers after the fact with calculating number to request up-front [\#1216](https://github.com/PegaSysEng/pantheon/pull/1216) -- Support async processing while maintaining output order [\#1215](https://github.com/PegaSysEng/pantheon/pull/1215) -- Add Unstable Options to the CLI [\#1213](https://github.com/PegaSysEng/pantheon/pull/1213) +- Finish off fast sync pipeline download [\#1222](https://github.com/PegaSysEng/pantheon/pull/1222) +- Enable fast-sync options on command line [\#1218](https://github.com/PegaSysEng/pantheon/pull/1218) +- Replace filtering headers after the fact with calculating number to request up-front [\#1216](https://github.com/PegaSysEng/pantheon/pull/1216) +- Support async processing while maintaining output order [\#1215](https://github.com/PegaSysEng/pantheon/pull/1215) +- Add Unstable Options to the CLI [\#1213](https://github.com/PegaSysEng/pantheon/pull/1213) - Add private cluster acceptance tests [\#1211](https://github.com/PegaSysEng/pantheon/pull/1211) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Re-aligned smart contract interface to EEA client spec 477 [\#1209](https://github.com/PegaSysEng/pantheon/pull/1209) -- Count the number of items discarded when a pipe is aborted [\#1208](https://github.com/PegaSysEng/pantheon/pull/1208) -- Pipeline chain download - fetch and import data [\#1207](https://github.com/PegaSysEng/pantheon/pull/1207) -- Permission provider that allows bootnodes if you have no other connections [\#1206](https://github.com/PegaSysEng/pantheon/pull/1206) -- Cancel in-progress async operations when the pipeline is aborted [\#1205](https://github.com/PegaSysEng/pantheon/pull/1205) -- Pipeline chain download - Checkpoints [\#1203](https://github.com/PegaSysEng/pantheon/pull/1203) -- Push development images to public dockerhub [\#1202](https://github.com/PegaSysEng/pantheon/pull/1202) -- Push builds of master as docker development images [\#1200](https://github.com/PegaSysEng/pantheon/pull/1200) -- Doc CI pipeline for build and tests [\#1199](https://github.com/PegaSysEng/pantheon/pull/1199) -- Replace the use of a disconnect listener with EthPeer.isDisconnected [\#1197](https://github.com/PegaSysEng/pantheon/pull/1197) -- Prep chain downloader for branch by abstraction [\#1194](https://github.com/PegaSysEng/pantheon/pull/1194) +- Re-aligned smart contract interface to EEA client spec 477 [\#1209](https://github.com/PegaSysEng/pantheon/pull/1209) +- Count the number of items discarded when a pipe is aborted [\#1208](https://github.com/PegaSysEng/pantheon/pull/1208) +- Pipeline chain download - fetch and import data [\#1207](https://github.com/PegaSysEng/pantheon/pull/1207) +- Permission provider that allows bootnodes if you have no other connections [\#1206](https://github.com/PegaSysEng/pantheon/pull/1206) +- Cancel in-progress async operations when the pipeline is aborted [\#1205](https://github.com/PegaSysEng/pantheon/pull/1205) +- Pipeline chain download - Checkpoints [\#1203](https://github.com/PegaSysEng/pantheon/pull/1203) +- Push development images to public dockerhub [\#1202](https://github.com/PegaSysEng/pantheon/pull/1202) +- Push builds of master as docker development images [\#1200](https://github.com/PegaSysEng/pantheon/pull/1200) +- Doc CI pipeline for build and tests [\#1199](https://github.com/PegaSysEng/pantheon/pull/1199) +- Replace the use of a disconnect listener with EthPeer.isDisconnected [\#1197](https://github.com/PegaSysEng/pantheon/pull/1197) +- Prep chain downloader for branch by abstraction [\#1194](https://github.com/PegaSysEng/pantheon/pull/1194) - Maintain the state of MessageFrame in private Tx [\#1193](https://github.com/PegaSysEng/pantheon/pull/1193) (thanks to [Puneetha17](https://github.com/Puneetha17)) - Persist private world state only if we are mining [\#1191](https://github.com/PegaSysEng/pantheon/pull/1191) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Remove SyncState from SyncTargetManager [\#1188](https://github.com/PegaSysEng/pantheon/pull/1188) -- Acceptance tests base for smart contract node permissioning [\#1186](https://github.com/PegaSysEng/pantheon/pull/1186) -- Fix metrics breakages [\#1185](https://github.com/PegaSysEng/pantheon/pull/1185) +- Remove SyncState from SyncTargetManager [\#1188](https://github.com/PegaSysEng/pantheon/pull/1188) +- Acceptance tests base for smart contract node permissioning [\#1186](https://github.com/PegaSysEng/pantheon/pull/1186) +- Fix metrics breakages [\#1185](https://github.com/PegaSysEng/pantheon/pull/1185) - Typo [\#1184](https://github.com/PegaSysEng/pantheon/pull/1184) (thanks to [araskachoi](https://github.com/araskachoi)) -- StaticNodesParserTest to pass on Windows [\#1183](https://github.com/PegaSysEng/pantheon/pull/1183) -- Don't mark world state as stalled until a minimum time without progress is reached [\#1179](https://github.com/PegaSysEng/pantheon/pull/1179) -- Use header validation policy in DownloadHeaderSequenceTask [\#1172](https://github.com/PegaSysEng/pantheon/pull/1172) -- Bond with bootnodes [\#1160](https://github.com/PegaSysEng/pantheon/pull/1160) +- StaticNodesParserTest to pass on Windows [\#1183](https://github.com/PegaSysEng/pantheon/pull/1183) +- Don't mark world state as stalled until a minimum time without progress is reached [\#1179](https://github.com/PegaSysEng/pantheon/pull/1179) +- Use header validation policy in DownloadHeaderSequenceTask [\#1172](https://github.com/PegaSysEng/pantheon/pull/1172) +- Bond with bootnodes [\#1160](https://github.com/PegaSysEng/pantheon/pull/1160) -## 1.0.2 +## 1.0.2 ### Additions and Improvements -- Removed DB init when using `public-key` subcommand [\#1049](https://github.com/PegaSysEng/pantheon/pull/1049) -- Output enode URL on startup [\#1137](https://github.com/PegaSysEng/pantheon/pull/1137) -- Added Remove Peer JSON-RPC [\#1129](https://github.com/PegaSysEng/pantheon/pull/1129) +- Removed DB init when using `public-key` subcommand [\#1049](https://github.com/PegaSysEng/pantheon/pull/1049) +- Output enode URL on startup [\#1137](https://github.com/PegaSysEng/pantheon/pull/1137) +- Added Remove Peer JSON-RPC [\#1129](https://github.com/PegaSysEng/pantheon/pull/1129) - Added `net_enode` JSON-RPC [\#1119](https://github.com/PegaSysEng/pantheon/pull/1119) (thanks to [mbergstrand](https://github.com/mbergstrand)) -- Maintain a `staticnodes.json` [\#1106](https://github.com/PegaSysEng/pantheon/pull/1106) -- Added `tx-pool-max-size` command line parameter [\#1078](https://github.com/PegaSysEng/pantheon/pull/1078) +- Maintain a `staticnodes.json` [\#1106](https://github.com/PegaSysEng/pantheon/pull/1106) +- Added `tx-pool-max-size` command line parameter [\#1078](https://github.com/PegaSysEng/pantheon/pull/1078) - Added PendingTransactions JSON-RPC [\#1043](https://github.com/PegaSysEng/pantheon/pull/1043) (thanks to [EdwinLeeGreene](https://github.com/EdwinLeeGreene)) -- Added `admin_nodeInfo` JSON-RPC [\#1012](https://github.com/PegaSysEng/pantheon/pull/1012) -- Added `--metrics-category` CLI to only enable select metrics [\#969](https://github.com/PegaSysEng/pantheon/pull/969) -- Documentation updates include: +- Added `admin_nodeInfo` JSON-RPC [\#1012](https://github.com/PegaSysEng/pantheon/pull/1012) +- Added `--metrics-category` CLI to only enable select metrics [\#969](https://github.com/PegaSysEng/pantheon/pull/969) +- Documentation updates include: - Updated endpoints in [Private Network Quickstart](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Private-Network-Quickstart/) (thanks to [laubai](https://github.com/laubai)) - - Updated [documentation contribution guidelines](https://besu.hyperledger.org/en/stable/) - - Added [`admin_removePeer`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#admin_removepeer) - - Updated [tutorials](https://besu.hyperledger.org/en/latest/Tutorials/Private-Network/Create-Private-Clique-Network/) for printing of enode on startup - - Added [`txpool_pantheonTransactions`](https://besu.hyperledger.org/en/stable/Reference/API-Methods/#txpool_besutransactions) - - Added [Transaction Pool content](https://besu.hyperledger.org/en/latest/Concepts/Transactions/Transaction-Pool/) - - Added [`tx-pool-max-size` CLI option](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#tx-pool-max-size) - - Updated [developer build instructions to use installDist](https://besu.hyperledger.org/en/stable/) - - Added [Azure quickstart tutorial](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Azure-Private-Network-Quickstart/) - - Enabled copy button in code blocks - - Added [IBFT 1.0](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/QuorumIBFT/) + - Updated [documentation contribution guidelines](https://besu.hyperledger.org/en/stable/) + - Added [`admin_removePeer`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#admin_removepeer) + - Updated [tutorials](https://besu.hyperledger.org/en/latest/Tutorials/Private-Network/Create-Private-Clique-Network/) for printing of enode on startup + - Added [`txpool_pantheonTransactions`](https://besu.hyperledger.org/en/stable/Reference/API-Methods/#txpool_besutransactions) + - Added [Transaction Pool content](https://besu.hyperledger.org/en/latest/Concepts/Transactions/Transaction-Pool/) + - Added [`tx-pool-max-size` CLI option](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#tx-pool-max-size) + - Updated [developer build instructions to use installDist](https://besu.hyperledger.org/en/stable/) + - Added [Azure quickstart tutorial](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Azure-Private-Network-Quickstart/) + - Enabled copy button in code blocks + - Added [IBFT 1.0](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/QuorumIBFT/) - Added section on using [Geth attach with Besu](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/Using-JSON-RPC-API/#geth-console) - - Enabled the edit link doc site to ease external doc contributions + - Enabled the edit link doc site to ease external doc contributions - Added [EthStats docs](https://besu.hyperledger.org/HowTo/Deploy/Lite-Network-Monitor/) (thanks to [baxy](https://github.com/baxy)) - Updated [Postman collection](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/Authentication/#postman) - - Added [`metrics-category` CLI option](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#metrics-category) - - Added information on [block time and timeout settings](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/IBFT/#block-time) for IBFT 2.0 - - Added [`admin_nodeInfo`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#admin_nodeinfo) - - Added [permissions images](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Permissioning-Overview/) - - Added permissioning blog to [Resources](https://besu.hyperledger.org/en/latest/Reference/Resources/) - - Updated [Create Permissioned Network](https://besu.hyperledger.org/en/latest/Tutorials/Permissioning/Create-Permissioned-Network/) tutorial to use `export-address` + - Added [`metrics-category` CLI option](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#metrics-category) + - Added information on [block time and timeout settings](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/IBFT/#block-time) for IBFT 2.0 + - Added [`admin_nodeInfo`](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#admin_nodeinfo) + - Added [permissions images](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Permissioning-Overview/) + - Added permissioning blog to [Resources](https://besu.hyperledger.org/en/latest/Reference/Resources/) + - Updated [Create Permissioned Network](https://besu.hyperledger.org/en/latest/Tutorials/Permissioning/Create-Permissioned-Network/) tutorial to use `export-address` - Updated [Clique](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/Clique/) and [IBFT 2.0](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/IBFT/) docs to include complete genesis file - Updated [Clique tutorial](https://besu.hyperledger.org/en/latest/Tutorials/Private-Network/Create-Private-Clique-Network/) to use `export-address` subcommand - - Added IBFT 2.0 [future message configuration options](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/IBFT/#optional-configuration-options) - + - Added IBFT 2.0 [future message configuration options](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/IBFT/#optional-configuration-options) + ### Technical Improvements - Fixed so self persists to the whitelist [\#1176](https://github.com/PegaSysEng/pantheon/pull/1176) - Fixed to add self to permissioning whitelist [\#1175](https://github.com/PegaSysEng/pantheon/pull/1175) - Fixed permissioning issues [\#1174](https://github.com/PegaSysEng/pantheon/pull/1174) - AdminAddPeer returns custom Json RPC error code [\#1171](https://github.com/PegaSysEng/pantheon/pull/1171) - Periodically connect to peers from table [\#1170](https://github.com/PegaSysEng/pantheon/pull/1170) -- Improved bootnodes option error message [\#1092](https://github.com/PegaSysEng/pantheon/pull/1092) -- Automatically restrict trailing peers while syncing [\#1167](https://github.com/PegaSysEng/pantheon/pull/1167) -- Avoid bonding to ourselves [\#1166](https://github.com/PegaSysEng/pantheon/pull/1166) -- Fix Push Metrics [\#1164](https://github.com/PegaSysEng/pantheon/pull/1164) -- Synchroniser waits for new peer if best is up to date [\#1161](https://github.com/PegaSysEng/pantheon/pull/1161) -- Don't attempt to download checkpoint headers if the number of headers is negative [\#1158](https://github.com/PegaSysEng/pantheon/pull/1158) -- Capture metrics on Vertx event loop and worker thread queues [\#1155](https://github.com/PegaSysEng/pantheon/pull/1155) -- Simplify node permissioning ATs [\#1153](https://github.com/PegaSysEng/pantheon/pull/1153) -- Add metrics around discovery process [\#1152](https://github.com/PegaSysEng/pantheon/pull/1152) -- Prevent connecting to self [\#1150](https://github.com/PegaSysEng/pantheon/pull/1150) -- Refactoring permissioning ATs [\#1148](https://github.com/PegaSysEng/pantheon/pull/1148) -- Added two extra Ropsten bootnodes [\#1147](https://github.com/PegaSysEng/pantheon/pull/1147) -- Fixed TCP port handling [\#1144](https://github.com/PegaSysEng/pantheon/pull/1144) -- Better error on bad header [\#1143](https://github.com/PegaSysEng/pantheon/pull/1143) -- Refresh peer table while we have fewer than maxPeers connected [\#1142](https://github.com/PegaSysEng/pantheon/pull/1142) -- Refactor jsonrpc consumption of local node permissioning controller [\#1140](https://github.com/PegaSysEng/pantheon/pull/1140) -- Disconnect peers before the pivot block while fast syncing [\#1139](https://github.com/PegaSysEng/pantheon/pull/1139) -- Reduce the default transaction pool size from 30,000 to 4096 [\#1136](https://github.com/PegaSysEng/pantheon/pull/1136) -- Fail at load if static nodes not whitelisted [\#1135](https://github.com/PegaSysEng/pantheon/pull/1135) +- Improved bootnodes option error message [\#1092](https://github.com/PegaSysEng/pantheon/pull/1092) +- Automatically restrict trailing peers while syncing [\#1167](https://github.com/PegaSysEng/pantheon/pull/1167) +- Avoid bonding to ourselves [\#1166](https://github.com/PegaSysEng/pantheon/pull/1166) +- Fix Push Metrics [\#1164](https://github.com/PegaSysEng/pantheon/pull/1164) +- Synchroniser waits for new peer if best is up to date [\#1161](https://github.com/PegaSysEng/pantheon/pull/1161) +- Don't attempt to download checkpoint headers if the number of headers is negative [\#1158](https://github.com/PegaSysEng/pantheon/pull/1158) +- Capture metrics on Vertx event loop and worker thread queues [\#1155](https://github.com/PegaSysEng/pantheon/pull/1155) +- Simplify node permissioning ATs [\#1153](https://github.com/PegaSysEng/pantheon/pull/1153) +- Add metrics around discovery process [\#1152](https://github.com/PegaSysEng/pantheon/pull/1152) +- Prevent connecting to self [\#1150](https://github.com/PegaSysEng/pantheon/pull/1150) +- Refactoring permissioning ATs [\#1148](https://github.com/PegaSysEng/pantheon/pull/1148) +- Added two extra Ropsten bootnodes [\#1147](https://github.com/PegaSysEng/pantheon/pull/1147) +- Fixed TCP port handling [\#1144](https://github.com/PegaSysEng/pantheon/pull/1144) +- Better error on bad header [\#1143](https://github.com/PegaSysEng/pantheon/pull/1143) +- Refresh peer table while we have fewer than maxPeers connected [\#1142](https://github.com/PegaSysEng/pantheon/pull/1142) +- Refactor jsonrpc consumption of local node permissioning controller [\#1140](https://github.com/PegaSysEng/pantheon/pull/1140) +- Disconnect peers before the pivot block while fast syncing [\#1139](https://github.com/PegaSysEng/pantheon/pull/1139) +- Reduce the default transaction pool size from 30,000 to 4096 [\#1136](https://github.com/PegaSysEng/pantheon/pull/1136) +- Fail at load if static nodes not whitelisted [\#1135](https://github.com/PegaSysEng/pantheon/pull/1135) - Fix private transaction acceptance test [\#1134](https://github.com/PegaSysEng/pantheon/pull/1134) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Quieter exceptions when network is unreachable [\#1133](https://github.com/PegaSysEng/pantheon/pull/1133) -- nodepermissioningcontroller used for devp2p connection filtering [\#1132](https://github.com/PegaSysEng/pantheon/pull/1132) -- Remove duplicates from apis specified via CLI [\#1131](https://github.com/PegaSysEng/pantheon/pull/1131) -- Synchronizer returns false if it is in sync [\#1130](https://github.com/PegaSysEng/pantheon/pull/1130) -- Added fromHexStringStrict to check for exactly 20 byte addresses [\#1128](https://github.com/PegaSysEng/pantheon/pull/1128) -- Fix deadlock scenario in AsyncOperationProcessor and re-enable WorldStateDownloaderTest [\#1126](https://github.com/PegaSysEng/pantheon/pull/1126) -- Ignore WorldStateDownloaderTest [\#1125](https://github.com/PegaSysEng/pantheon/pull/1125) -- Updated local config permissioning flags [\#1118](https://github.com/PegaSysEng/pantheon/pull/1118) -- Pipeline Improvements [\#1117](https://github.com/PegaSysEng/pantheon/pull/1117) -- Permissioning cli smart contract [\#1116](https://github.com/PegaSysEng/pantheon/pull/1116) -- Adding default pending transactions value in BesuControllerBuilder [\#1114](https://github.com/PegaSysEng/pantheon/pull/1114) -- Fix intermittency in WorldStateDownloaderTest [\#1113](https://github.com/PegaSysEng/pantheon/pull/1113) -- Reduce number of seen blocks and transactions Besu tracks [\#1112](https://github.com/PegaSysEng/pantheon/pull/1112) -- Timeout long test [\#1111](https://github.com/PegaSysEng/pantheon/pull/1111) -- Errorprone 2.3.3 upgrades [\#1110](https://github.com/PegaSysEng/pantheon/pull/1110) -- Add metric to capture memory used by RocksDB table readers [\#1108](https://github.com/PegaSysEng/pantheon/pull/1108) -- Don't allow creation of multiple gauges with the same name [\#1107](https://github.com/PegaSysEng/pantheon/pull/1107) -- Update Peer Discovery to use NodePermissioningController [\#1105](https://github.com/PegaSysEng/pantheon/pull/1105) -- Move starting world state download process inside WorldDownloadState [\#1104](https://github.com/PegaSysEng/pantheon/pull/1104) +- Quieter exceptions when network is unreachable [\#1133](https://github.com/PegaSysEng/pantheon/pull/1133) +- nodepermissioningcontroller used for devp2p connection filtering [\#1132](https://github.com/PegaSysEng/pantheon/pull/1132) +- Remove duplicates from apis specified via CLI [\#1131](https://github.com/PegaSysEng/pantheon/pull/1131) +- Synchronizer returns false if it is in sync [\#1130](https://github.com/PegaSysEng/pantheon/pull/1130) +- Added fromHexStringStrict to check for exactly 20 byte addresses [\#1128](https://github.com/PegaSysEng/pantheon/pull/1128) +- Fix deadlock scenario in AsyncOperationProcessor and re-enable WorldStateDownloaderTest [\#1126](https://github.com/PegaSysEng/pantheon/pull/1126) +- Ignore WorldStateDownloaderTest [\#1125](https://github.com/PegaSysEng/pantheon/pull/1125) +- Updated local config permissioning flags [\#1118](https://github.com/PegaSysEng/pantheon/pull/1118) +- Pipeline Improvements [\#1117](https://github.com/PegaSysEng/pantheon/pull/1117) +- Permissioning cli smart contract [\#1116](https://github.com/PegaSysEng/pantheon/pull/1116) +- Adding default pending transactions value in BesuControllerBuilder [\#1114](https://github.com/PegaSysEng/pantheon/pull/1114) +- Fix intermittency in WorldStateDownloaderTest [\#1113](https://github.com/PegaSysEng/pantheon/pull/1113) +- Reduce number of seen blocks and transactions Besu tracks [\#1112](https://github.com/PegaSysEng/pantheon/pull/1112) +- Timeout long test [\#1111](https://github.com/PegaSysEng/pantheon/pull/1111) +- Errorprone 2.3.3 upgrades [\#1110](https://github.com/PegaSysEng/pantheon/pull/1110) +- Add metric to capture memory used by RocksDB table readers [\#1108](https://github.com/PegaSysEng/pantheon/pull/1108) +- Don't allow creation of multiple gauges with the same name [\#1107](https://github.com/PegaSysEng/pantheon/pull/1107) +- Update Peer Discovery to use NodePermissioningController [\#1105](https://github.com/PegaSysEng/pantheon/pull/1105) +- Move starting world state download process inside WorldDownloadState [\#1104](https://github.com/PegaSysEng/pantheon/pull/1104) - Enable private Tx capability to Clique [\#1102](https://github.com/PegaSysEng/pantheon/pull/1102) (thanks to [Puneetha17](https://github.com/Puneetha17)) - Enable private Tx capability to IBFT [\#1101](https://github.com/PegaSysEng/pantheon/pull/1101) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Version Upgrades [\#1100](https://github.com/PegaSysEng/pantheon/pull/1100) -- Don't delete completed tasks from RocksDbTaskQueue [\#1099](https://github.com/PegaSysEng/pantheon/pull/1099) -- Support flat mapping with multiple threads [\#1098](https://github.com/PegaSysEng/pantheon/pull/1098) -- Add pipe stage name to thread while executing [\#1097](https://github.com/PegaSysEng/pantheon/pull/1097) -- Use pipeline for world state download [\#1096](https://github.com/PegaSysEng/pantheon/pull/1096) -- TXPool JSON RPC tweaks [\#1095](https://github.com/PegaSysEng/pantheon/pull/1095) -- Add in-memory cache over world state download queue [\#1087](https://github.com/PegaSysEng/pantheon/pull/1087) -- Trim default metrics [\#1086](https://github.com/PegaSysEng/pantheon/pull/1086) -- Improve imported block log line [\#1085](https://github.com/PegaSysEng/pantheon/pull/1085) -- Smart contract permission controller [\#1083](https://github.com/PegaSysEng/pantheon/pull/1083) -- Add timeout when waiting for JSON-RPC, WebSocket RPC and Metrics services to stop [\#1082](https://github.com/PegaSysEng/pantheon/pull/1082) -- Add pipeline framework to make parallel processing simpler [\#1077](https://github.com/PegaSysEng/pantheon/pull/1077) -- Node permissioning controller [\#1075](https://github.com/PegaSysEng/pantheon/pull/1075) -- Smart contract permission controller stub [\#1074](https://github.com/PegaSysEng/pantheon/pull/1074) -- Expose a synchronous start method in Runner [\#1072](https://github.com/PegaSysEng/pantheon/pull/1072) -- Changes in chain head should trigger new permissioning check for active peers [\#1071](https://github.com/PegaSysEng/pantheon/pull/1071) -- Fix exceptions fetching metrics after world state download completes [\#1066](https://github.com/PegaSysEng/pantheon/pull/1066) -- Accept transactions in the pool with nonce above account sender nonce [\#1065](https://github.com/PegaSysEng/pantheon/pull/1065) -- Repair Istanbul to handle Eth/62 & Eth/63 [\#1063](https://github.com/PegaSysEng/pantheon/pull/1063) +- Version Upgrades [\#1100](https://github.com/PegaSysEng/pantheon/pull/1100) +- Don't delete completed tasks from RocksDbTaskQueue [\#1099](https://github.com/PegaSysEng/pantheon/pull/1099) +- Support flat mapping with multiple threads [\#1098](https://github.com/PegaSysEng/pantheon/pull/1098) +- Add pipe stage name to thread while executing [\#1097](https://github.com/PegaSysEng/pantheon/pull/1097) +- Use pipeline for world state download [\#1096](https://github.com/PegaSysEng/pantheon/pull/1096) +- TXPool JSON RPC tweaks [\#1095](https://github.com/PegaSysEng/pantheon/pull/1095) +- Add in-memory cache over world state download queue [\#1087](https://github.com/PegaSysEng/pantheon/pull/1087) +- Trim default metrics [\#1086](https://github.com/PegaSysEng/pantheon/pull/1086) +- Improve imported block log line [\#1085](https://github.com/PegaSysEng/pantheon/pull/1085) +- Smart contract permission controller [\#1083](https://github.com/PegaSysEng/pantheon/pull/1083) +- Add timeout when waiting for JSON-RPC, WebSocket RPC and Metrics services to stop [\#1082](https://github.com/PegaSysEng/pantheon/pull/1082) +- Add pipeline framework to make parallel processing simpler [\#1077](https://github.com/PegaSysEng/pantheon/pull/1077) +- Node permissioning controller [\#1075](https://github.com/PegaSysEng/pantheon/pull/1075) +- Smart contract permission controller stub [\#1074](https://github.com/PegaSysEng/pantheon/pull/1074) +- Expose a synchronous start method in Runner [\#1072](https://github.com/PegaSysEng/pantheon/pull/1072) +- Changes in chain head should trigger new permissioning check for active peers [\#1071](https://github.com/PegaSysEng/pantheon/pull/1071) +- Fix exceptions fetching metrics after world state download completes [\#1066](https://github.com/PegaSysEng/pantheon/pull/1066) +- Accept transactions in the pool with nonce above account sender nonce [\#1065](https://github.com/PegaSysEng/pantheon/pull/1065) +- Repair Istanbul to handle Eth/62 & Eth/63 [\#1063](https://github.com/PegaSysEng/pantheon/pull/1063) - Close Private Storage Provider [\#1059](https://github.com/PegaSysEng/pantheon/pull/1059) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Add labels to Pipelined tasks metrics [\#1057](https://github.com/PegaSysEng/pantheon/pull/1057) -- Re-enable Quorum Synchronisation [\#1056](https://github.com/PegaSysEng/pantheon/pull/1056) -- Don't log expected failures as errors [\#1054](https://github.com/PegaSysEng/pantheon/pull/1054) -- Make findSuitablePeer abstract [\#1053](https://github.com/PegaSysEng/pantheon/pull/1053) -- Track added at in txpool [\#1048](https://github.com/PegaSysEng/pantheon/pull/1048) -- Fix ImportBlocksTask to only request from peers that claim to have the blocks [\#1047](https://github.com/PegaSysEng/pantheon/pull/1047) -- Don't run the dao block validator if dao block is 0 [\#1044](https://github.com/PegaSysEng/pantheon/pull/1044) -- Don't make unnecessary copies of data in RocksDbKeyValueStorage [\#1040](https://github.com/PegaSysEng/pantheon/pull/1040) -- Update discovery logic to trust bootnodes only when out of sync [\#1039](https://github.com/PegaSysEng/pantheon/pull/1039) -- Fix IndexOutOfBoundsException in DetermineCommonAncestorTask [\#1038](https://github.com/PegaSysEng/pantheon/pull/1038) -- Add `rpc_modules` JSON-RPC [\#1036](https://github.com/PegaSysEng/pantheon/pull/1036) -- Simple permissioning smart contract [\#1035](https://github.com/PegaSysEng/pantheon/pull/1035) -- Refactor enodeurl to use inetaddr [\#1032](https://github.com/PegaSysEng/pantheon/pull/1032) -- Update CLI options in mismatched genesis file message [\#1031](https://github.com/PegaSysEng/pantheon/pull/1031) -- Remove dependence of eth.core on eth.permissioning [\#1030](https://github.com/PegaSysEng/pantheon/pull/1030) -- Make alloc optional and provide nicer error messages when genesis config is invalid [\#1029](https://github.com/PegaSysEng/pantheon/pull/1029) -- Handle metrics request closing before response is generated [\#1028](https://github.com/PegaSysEng/pantheon/pull/1028) -- Change EthNetworkConfig bootnodes to always be URIs [\#1027](https://github.com/PegaSysEng/pantheon/pull/1027) -- Avoid port conflicts in acceptance tests [\#1025](https://github.com/PegaSysEng/pantheon/pull/1025) -- Include reference tests in jacoco [\#1024](https://github.com/PegaSysEng/pantheon/pull/1024) -- Acceptance test - configurable gas price [\#1023](https://github.com/PegaSysEng/pantheon/pull/1023) +- Add labels to Pipelined tasks metrics [\#1057](https://github.com/PegaSysEng/pantheon/pull/1057) +- Re-enable Quorum Synchronisation [\#1056](https://github.com/PegaSysEng/pantheon/pull/1056) +- Don't log expected failures as errors [\#1054](https://github.com/PegaSysEng/pantheon/pull/1054) +- Make findSuitablePeer abstract [\#1053](https://github.com/PegaSysEng/pantheon/pull/1053) +- Track added at in txpool [\#1048](https://github.com/PegaSysEng/pantheon/pull/1048) +- Fix ImportBlocksTask to only request from peers that claim to have the blocks [\#1047](https://github.com/PegaSysEng/pantheon/pull/1047) +- Don't run the dao block validator if dao block is 0 [\#1044](https://github.com/PegaSysEng/pantheon/pull/1044) +- Don't make unnecessary copies of data in RocksDbKeyValueStorage [\#1040](https://github.com/PegaSysEng/pantheon/pull/1040) +- Update discovery logic to trust bootnodes only when out of sync [\#1039](https://github.com/PegaSysEng/pantheon/pull/1039) +- Fix IndexOutOfBoundsException in DetermineCommonAncestorTask [\#1038](https://github.com/PegaSysEng/pantheon/pull/1038) +- Add `rpc_modules` JSON-RPC [\#1036](https://github.com/PegaSysEng/pantheon/pull/1036) +- Simple permissioning smart contract [\#1035](https://github.com/PegaSysEng/pantheon/pull/1035) +- Refactor enodeurl to use inetaddr [\#1032](https://github.com/PegaSysEng/pantheon/pull/1032) +- Update CLI options in mismatched genesis file message [\#1031](https://github.com/PegaSysEng/pantheon/pull/1031) +- Remove dependence of eth.core on eth.permissioning [\#1030](https://github.com/PegaSysEng/pantheon/pull/1030) +- Make alloc optional and provide nicer error messages when genesis config is invalid [\#1029](https://github.com/PegaSysEng/pantheon/pull/1029) +- Handle metrics request closing before response is generated [\#1028](https://github.com/PegaSysEng/pantheon/pull/1028) +- Change EthNetworkConfig bootnodes to always be URIs [\#1027](https://github.com/PegaSysEng/pantheon/pull/1027) +- Avoid port conflicts in acceptance tests [\#1025](https://github.com/PegaSysEng/pantheon/pull/1025) +- Include reference tests in jacoco [\#1024](https://github.com/PegaSysEng/pantheon/pull/1024) +- Acceptance test - configurable gas price [\#1023](https://github.com/PegaSysEng/pantheon/pull/1023) - Get Internal logs and output [\#1022](https://github.com/PegaSysEng/pantheon/pull/1022) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Fix race condition in WebSocketService [\#1021](https://github.com/PegaSysEng/pantheon/pull/1021) -- Ensure devp2p ports are written to ports file correctly [\#1020](https://github.com/PegaSysEng/pantheon/pull/1020) -- Report the correct tcp port in PING packets when it differs from the UDP port [\#1019](https://github.com/PegaSysEng/pantheon/pull/1019) -- Refactor transient transaction processor [\#1017](https://github.com/PegaSysEng/pantheon/pull/1017) -- Resume world state download from existing queue [\#1016](https://github.com/PegaSysEng/pantheon/pull/1016) -- IBFT Acceptance tests updated with longer timeout on first block [\#1015](https://github.com/PegaSysEng/pantheon/pull/1015) -- Update IBFT acceptances tests to await first block [\#1013](https://github.com/PegaSysEng/pantheon/pull/1013) -- Remove full hashimoto implementation as its never used [\#1011](https://github.com/PegaSysEng/pantheon/pull/1011) -- Created SyncStatus notifications [\#1010](https://github.com/PegaSysEng/pantheon/pull/1010) -- Address acceptance test intermittency [\#1008](https://github.com/PegaSysEng/pantheon/pull/1008) -- Consider a world state download stalled after 100 requests with no progress [\#1007](https://github.com/PegaSysEng/pantheon/pull/1007) -- Reduce log level when block miner is interrupted [\#1006](https://github.com/PegaSysEng/pantheon/pull/1006) -- RunnerTest fail on Windows due to network startup timing issue [\#1005](https://github.com/PegaSysEng/pantheon/pull/1005) +- Fix race condition in WebSocketService [\#1021](https://github.com/PegaSysEng/pantheon/pull/1021) +- Ensure devp2p ports are written to ports file correctly [\#1020](https://github.com/PegaSysEng/pantheon/pull/1020) +- Report the correct tcp port in PING packets when it differs from the UDP port [\#1019](https://github.com/PegaSysEng/pantheon/pull/1019) +- Refactor transient transaction processor [\#1017](https://github.com/PegaSysEng/pantheon/pull/1017) +- Resume world state download from existing queue [\#1016](https://github.com/PegaSysEng/pantheon/pull/1016) +- IBFT Acceptance tests updated with longer timeout on first block [\#1015](https://github.com/PegaSysEng/pantheon/pull/1015) +- Update IBFT acceptances tests to await first block [\#1013](https://github.com/PegaSysEng/pantheon/pull/1013) +- Remove full hashimoto implementation as its never used [\#1011](https://github.com/PegaSysEng/pantheon/pull/1011) +- Created SyncStatus notifications [\#1010](https://github.com/PegaSysEng/pantheon/pull/1010) +- Address acceptance test intermittency [\#1008](https://github.com/PegaSysEng/pantheon/pull/1008) +- Consider a world state download stalled after 100 requests with no progress [\#1007](https://github.com/PegaSysEng/pantheon/pull/1007) +- Reduce log level when block miner is interrupted [\#1006](https://github.com/PegaSysEng/pantheon/pull/1006) +- RunnerTest fail on Windows due to network startup timing issue [\#1005](https://github.com/PegaSysEng/pantheon/pull/1005) - Generate Private Contract Address [\#1004](https://github.com/PegaSysEng/pantheon/pull/1004) (thanks to [vinistevam](https://github.com/vinistevam)) -- Delete the legacy pipelined import code [\#1003](https://github.com/PegaSysEng/pantheon/pull/1003) -- Fix race condition in WebSocket AT [\#1002](https://github.com/PegaSysEng/pantheon/pull/1002) -- Cleanup IBFT logging levels [\#995](https://github.com/PegaSysEng/pantheon/pull/995) -- Integration Test implementation dependency for non-IntelliJ IDE [\#992](https://github.com/PegaSysEng/pantheon/pull/992) -- Ignore fast sync and full sync tests to avoid race condition [\#991](https://github.com/PegaSysEng/pantheon/pull/991) -- Make acceptance tests use the process based runner again [\#990](https://github.com/PegaSysEng/pantheon/pull/990) -- RoundChangeCertificateValidator requires unique authors [\#989](https://github.com/PegaSysEng/pantheon/pull/989) -- Make Rinkeby the benchmark chain. [\#986](https://github.com/PegaSysEng/pantheon/pull/986) -- Add metrics to Parallel Download pipeline [\#985](https://github.com/PegaSysEng/pantheon/pull/985) -- Change ExpectBlockNumber to require at least the specified block number [\#981](https://github.com/PegaSysEng/pantheon/pull/981) -- Fix benchmark compilation [\#980](https://github.com/PegaSysEng/pantheon/pull/980) +- Delete the legacy pipelined import code [\#1003](https://github.com/PegaSysEng/pantheon/pull/1003) +- Fix race condition in WebSocket AT [\#1002](https://github.com/PegaSysEng/pantheon/pull/1002) +- Cleanup IBFT logging levels [\#995](https://github.com/PegaSysEng/pantheon/pull/995) +- Integration Test implementation dependency for non-IntelliJ IDE [\#992](https://github.com/PegaSysEng/pantheon/pull/992) +- Ignore fast sync and full sync tests to avoid race condition [\#991](https://github.com/PegaSysEng/pantheon/pull/991) +- Make acceptance tests use the process based runner again [\#990](https://github.com/PegaSysEng/pantheon/pull/990) +- RoundChangeCertificateValidator requires unique authors [\#989](https://github.com/PegaSysEng/pantheon/pull/989) +- Make Rinkeby the benchmark chain. [\#986](https://github.com/PegaSysEng/pantheon/pull/986) +- Add metrics to Parallel Download pipeline [\#985](https://github.com/PegaSysEng/pantheon/pull/985) +- Change ExpectBlockNumber to require at least the specified block number [\#981](https://github.com/PegaSysEng/pantheon/pull/981) +- Fix benchmark compilation [\#980](https://github.com/PegaSysEng/pantheon/pull/980) - RPC tests can use 127.0.0.1 loopback rather than localhost [\#974](https://github.com/PegaSysEng/pantheon/pull/974) thanks to [glethuillier](https://github.com/glethuillier) for raising) -- Disable picocli ansi when testing [\#973](https://github.com/PegaSysEng/pantheon/pull/973) -- Add a jmh benchmark for WorldStateDownloader [\#972](https://github.com/PegaSysEng/pantheon/pull/972) -- Gradle dependency for JMH annotation, for IDEs that aren't IntelliJ \(… [\#971](https://github.com/PegaSysEng/pantheon/pull/971) +- Disable picocli ansi when testing [\#973](https://github.com/PegaSysEng/pantheon/pull/973) +- Add a jmh benchmark for WorldStateDownloader [\#972](https://github.com/PegaSysEng/pantheon/pull/972) +- Gradle dependency for JMH annotation, for IDEs that aren't IntelliJ \(… [\#971](https://github.com/PegaSysEng/pantheon/pull/971) - Separate download state tracking from WorldStateDownloader [\#967](https://github.com/PegaSysEng/pantheon/pull/967) -- Gradle dependency for JMH annotation, for IDEs that aren't IntelliJ [\#966](https://github.com/PegaSysEng/pantheon/pull/966) -- Truffle HDwallet Web3 1.0 [\#964](https://github.com/PegaSysEng/pantheon/pull/964) -- Add missing JavaDoc tags in JSONToRLP [\#963](https://github.com/PegaSysEng/pantheon/pull/963) -- Only import block if it isn't already on the block chain [\#962](https://github.com/PegaSysEng/pantheon/pull/962) -- CLI stack traces when debugging [\#960](https://github.com/PegaSysEng/pantheon/pull/960) -- Create peer discovery packets on a worker thread [\#955](https://github.com/PegaSysEng/pantheon/pull/955) -- Remove start functionality from IbftController and IbftBlockHeightMan… [\#952](https://github.com/PegaSysEng/pantheon/pull/952) -- Cleanup IBFT executors [\#951](https://github.com/PegaSysEng/pantheon/pull/951) -- Single threaded world state persistence [\#950](https://github.com/PegaSysEng/pantheon/pull/950) -- Fix version number on master [\#946](https://github.com/PegaSysEng/pantheon/pull/946) -- Change automatic benchmark [\#945](https://github.com/PegaSysEng/pantheon/pull/945) -- Eliminate redundant header validation [\#943](https://github.com/PegaSysEng/pantheon/pull/943) -- RocksDbQueue Threading Tweaks [\#940](https://github.com/PegaSysEng/pantheon/pull/940) -- Validate DAO block [\#939](https://github.com/PegaSysEng/pantheon/pull/939) +- Gradle dependency for JMH annotation, for IDEs that aren't IntelliJ [\#966](https://github.com/PegaSysEng/pantheon/pull/966) +- Truffle HDwallet Web3 1.0 [\#964](https://github.com/PegaSysEng/pantheon/pull/964) +- Add missing JavaDoc tags in JSONToRLP [\#963](https://github.com/PegaSysEng/pantheon/pull/963) +- Only import block if it isn't already on the block chain [\#962](https://github.com/PegaSysEng/pantheon/pull/962) +- CLI stack traces when debugging [\#960](https://github.com/PegaSysEng/pantheon/pull/960) +- Create peer discovery packets on a worker thread [\#955](https://github.com/PegaSysEng/pantheon/pull/955) +- Remove start functionality from IbftController and IbftBlockHeightMan… [\#952](https://github.com/PegaSysEng/pantheon/pull/952) +- Cleanup IBFT executors [\#951](https://github.com/PegaSysEng/pantheon/pull/951) +- Single threaded world state persistence [\#950](https://github.com/PegaSysEng/pantheon/pull/950) +- Fix version number on master [\#946](https://github.com/PegaSysEng/pantheon/pull/946) +- Change automatic benchmark [\#945](https://github.com/PegaSysEng/pantheon/pull/945) +- Eliminate redundant header validation [\#943](https://github.com/PegaSysEng/pantheon/pull/943) +- RocksDbQueue Threading Tweaks [\#940](https://github.com/PegaSysEng/pantheon/pull/940) +- Validate DAO block [\#939](https://github.com/PegaSysEng/pantheon/pull/939) - Complete Private Transaction Processor [\#938](https://github.com/PegaSysEng/pantheon/pull/938) (thanks to [iikirilov](https://github.com/iikirilov)) -- Add metrics for netty queue length [\#932](https://github.com/PegaSysEng/pantheon/pull/932) -- Update GetNodeDataFromPeerTask to return a map [\#931](https://github.com/PegaSysEng/pantheon/pull/931) +- Add metrics for netty queue length [\#932](https://github.com/PegaSysEng/pantheon/pull/932) +- Update GetNodeDataFromPeerTask to return a map [\#931](https://github.com/PegaSysEng/pantheon/pull/931) ## 1.0.1 @@ -1195,191 +1270,191 @@ Public key address export subcommand was missing in 1.0 release. - Documentation update for the [`public-key export-address`](https://besu.hyperledger.org/en/stable/) subcommand. - Updated [IBFT 2.0 overview](https://besu.hyperledger.org/en/stable/) to include use of `rlp encode` command and information on setting IBFT 2.0 properties to achieve your desired block time. -## 1.0 +## 1.0 -### Additions and Improvements -- [IBFT 2.0](https://besu.hyperledger.org/en/latest/Tutorials/Private-Network/Create-IBFT-Network/) -- [Permissioning](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Permissioning-Overview/) -- [JSON-RPC Authentication](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/Authentication/) +### Additions and Improvements +- [IBFT 2.0](https://besu.hyperledger.org/en/latest/Tutorials/Private-Network/Create-IBFT-Network/) +- [Permissioning](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Permissioning-Overview/) +- [JSON-RPC Authentication](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/Authentication/) - Added `rlp encode` subcommand [\#965](https://github.com/PegaSysEng/pantheon/pull/965) -- Method to reload permissions file [\#834](https://github.com/PegaSysEng/pantheon/pull/834) -- Added rebind mitigation for Websockets. [\#905](https://github.com/PegaSysEng/pantheon/pull/905) +- Method to reload permissions file [\#834](https://github.com/PegaSysEng/pantheon/pull/834) +- Added rebind mitigation for Websockets. [\#905](https://github.com/PegaSysEng/pantheon/pull/905) - Support genesis contract code [\#749](https://github.com/PegaSysEng/pantheon/pull/749) (thanks to [kziemianek](https://github.com/kziemianek)). -- Documentation updates include: +- Documentation updates include: - Added details on [port configuration](https://besu.hyperledger.org/en/latest/HowTo/Find-and-Connect/Configuring-Ports/) - - Added [Resources page](https://besu.hyperledger.org/en/latest/Reference/Resources/) linking to Besu blog posts and webinars + - Added [Resources page](https://besu.hyperledger.org/en/latest/Reference/Resources/) linking to Besu blog posts and webinars - Added [JSON-RPC Authentication](https://besu.hyperledger.org/en/latest/HowTo/Interact/APIs/Authentication/) - - Added [tutorial to create permissioned network](https://besu.hyperledger.org/en/latest/Tutorials/Permissioning/Create-Permissioned-Network/) - - Added [Permissioning](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Permissioning-Overview/) content - - Added [Permissioning API methods](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#permissioning-methods) + - Added [tutorial to create permissioned network](https://besu.hyperledger.org/en/latest/Tutorials/Permissioning/Create-Permissioned-Network/) + - Added [Permissioning](https://besu.hyperledger.org/en/latest/Concepts/Permissioning/Permissioning-Overview/) content + - Added [Permissioning API methods](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#permissioning-methods) - Added [tutorial to create Clique private network](https://besu.hyperledger.org/en/latest/Tutorials/Private-Network/Create-Private-Clique-Network/) - Added [tutorial to create IBFT 2.0 private network](https://besu.hyperledger.org/en/latest/Tutorials/Private-Network/Create-IBFT-Network/) - -### Technical Improvements + +### Technical Improvements - RoundChangeCertificateValidator requires unique authors [\#997](https://github.com/PegaSysEng/pantheon/pull/997) - RPC tests can use 127.0.0.1 loopback rather than localhost [\#979](https://github.com/PegaSysEng/pantheon/pull/979) - Integration Test implementation dependency for non-IntelliJ IDE [\#978](https://github.com/PegaSysEng/pantheon/pull/978) - Only import block if it isn't already on the block chain [\#977](https://github.com/PegaSysEng/pantheon/pull/977) - Disable picocli ansi when testing [\#975](https://github.com/PegaSysEng/pantheon/pull/975) - Create peer discovery packets on a worker thread [\#961](https://github.com/PegaSysEng/pantheon/pull/961) -- Removed Orion snapshot dependency [\#933](https://github.com/PegaSysEng/pantheon/pull/933) -- Use network ID instead of chain ID in MainnetBesuController. [\#929](https://github.com/PegaSysEng/pantheon/pull/929) -- Propagate new block messages to other clients in a worker thread [\#928](https://github.com/PegaSysEng/pantheon/pull/928) -- Parallel downloader should stop on puts if requested. [\#927](https://github.com/PegaSysEng/pantheon/pull/927) -- Permission config file location and option under docker [\#925](https://github.com/PegaSysEng/pantheon/pull/925) -- Fixed potential stall in world state download [\#922](https://github.com/PegaSysEng/pantheon/pull/922) -- Refactoring to introduce deleteOnExit\(\) for temp files [\#920](https://github.com/PegaSysEng/pantheon/pull/920) -- Reduce "Received transactions message" log from debug to trace [\#919](https://github.com/PegaSysEng/pantheon/pull/919) -- Handle PeerNotConnected exceptions when sending wire keep alives [\#918](https://github.com/PegaSysEng/pantheon/pull/918) -- admin_addpeers: error if node not whitelisted [\#917](https://github.com/PegaSysEng/pantheon/pull/917) -- Expose the Ibft MiningCoordinator [\#916](https://github.com/PegaSysEng/pantheon/pull/916) -- Check perm api against perm cli [\#915](https://github.com/PegaSysEng/pantheon/pull/915) -- Update metrics when completing a world state request with existing data [\#914](https://github.com/PegaSysEng/pantheon/pull/914) -- Improve RocksDBQueue dequeue performance [\#913](https://github.com/PegaSysEng/pantheon/pull/913) -- Error when removing bootnodes from nodes whitelist [\#912](https://github.com/PegaSysEng/pantheon/pull/912) -- Incremental Optimization\(s\) on BlockBroadcaster [\#911](https://github.com/PegaSysEng/pantheon/pull/911) -- Check permissions CLI dependencies [\#909](https://github.com/PegaSysEng/pantheon/pull/909) -- Limit the number of times we retry peer discovery interactions [\#908](https://github.com/PegaSysEng/pantheon/pull/908) -- IBFT to use VoteTallyCache [\#907](https://github.com/PegaSysEng/pantheon/pull/907) -- Add metric to expose number of inflight world state requests [\#906](https://github.com/PegaSysEng/pantheon/pull/906) -- Bootnodes not on whitelist - improve errors [\#904](https://github.com/PegaSysEng/pantheon/pull/904) -- Make chain download cancellable [\#901](https://github.com/PegaSysEng/pantheon/pull/901) -- Enforce accounts must start with 0x [\#900](https://github.com/PegaSysEng/pantheon/pull/900) -- When picking fast sync pivot block, use the peer with the best total difficulty [\#899](https://github.com/PegaSysEng/pantheon/pull/899) -- Process world state download data on a worker thread [\#898](https://github.com/PegaSysEng/pantheon/pull/898) +- Removed Orion snapshot dependency [\#933](https://github.com/PegaSysEng/pantheon/pull/933) +- Use network ID instead of chain ID in MainnetBesuController. [\#929](https://github.com/PegaSysEng/pantheon/pull/929) +- Propagate new block messages to other clients in a worker thread [\#928](https://github.com/PegaSysEng/pantheon/pull/928) +- Parallel downloader should stop on puts if requested. [\#927](https://github.com/PegaSysEng/pantheon/pull/927) +- Permission config file location and option under docker [\#925](https://github.com/PegaSysEng/pantheon/pull/925) +- Fixed potential stall in world state download [\#922](https://github.com/PegaSysEng/pantheon/pull/922) +- Refactoring to introduce deleteOnExit\(\) for temp files [\#920](https://github.com/PegaSysEng/pantheon/pull/920) +- Reduce "Received transactions message" log from debug to trace [\#919](https://github.com/PegaSysEng/pantheon/pull/919) +- Handle PeerNotConnected exceptions when sending wire keep alives [\#918](https://github.com/PegaSysEng/pantheon/pull/918) +- admin_addpeers: error if node not whitelisted [\#917](https://github.com/PegaSysEng/pantheon/pull/917) +- Expose the Ibft MiningCoordinator [\#916](https://github.com/PegaSysEng/pantheon/pull/916) +- Check perm api against perm cli [\#915](https://github.com/PegaSysEng/pantheon/pull/915) +- Update metrics when completing a world state request with existing data [\#914](https://github.com/PegaSysEng/pantheon/pull/914) +- Improve RocksDBQueue dequeue performance [\#913](https://github.com/PegaSysEng/pantheon/pull/913) +- Error when removing bootnodes from nodes whitelist [\#912](https://github.com/PegaSysEng/pantheon/pull/912) +- Incremental Optimization\(s\) on BlockBroadcaster [\#911](https://github.com/PegaSysEng/pantheon/pull/911) +- Check permissions CLI dependencies [\#909](https://github.com/PegaSysEng/pantheon/pull/909) +- Limit the number of times we retry peer discovery interactions [\#908](https://github.com/PegaSysEng/pantheon/pull/908) +- IBFT to use VoteTallyCache [\#907](https://github.com/PegaSysEng/pantheon/pull/907) +- Add metric to expose number of inflight world state requests [\#906](https://github.com/PegaSysEng/pantheon/pull/906) +- Bootnodes not on whitelist - improve errors [\#904](https://github.com/PegaSysEng/pantheon/pull/904) +- Make chain download cancellable [\#901](https://github.com/PegaSysEng/pantheon/pull/901) +- Enforce accounts must start with 0x [\#900](https://github.com/PegaSysEng/pantheon/pull/900) +- When picking fast sync pivot block, use the peer with the best total difficulty [\#899](https://github.com/PegaSysEng/pantheon/pull/899) +- Process world state download data on a worker thread [\#898](https://github.com/PegaSysEng/pantheon/pull/898) - CLI mixin help [\#895](https://github.com/PegaSysEng/pantheon/pull/895) ([macfarla](https://github.com/macfarla)) - Use absolute datapath instead of relative. [\#894](https://github.com/PegaSysEng/pantheon/pull/894). -- Fix task queue so that the updated failure count for requests is stored [\#893](https://github.com/PegaSysEng/pantheon/pull/893) -- Fix authentication header [\#891](https://github.com/PegaSysEng/pantheon/pull/891) -- Reorganize eth tasks [\#890](https://github.com/PegaSysEng/pantheon/pull/890) -- Unit tests of BlockBroadcaster [\#887](https://github.com/PegaSysEng/pantheon/pull/887) -- Fix authentication file validation errors [\#886](https://github.com/PegaSysEng/pantheon/pull/886) -- Fixing file locations under docker [\#885](https://github.com/PegaSysEng/pantheon/pull/885) +- Fix task queue so that the updated failure count for requests is stored [\#893](https://github.com/PegaSysEng/pantheon/pull/893) +- Fix authentication header [\#891](https://github.com/PegaSysEng/pantheon/pull/891) +- Reorganize eth tasks [\#890](https://github.com/PegaSysEng/pantheon/pull/890) +- Unit tests of BlockBroadcaster [\#887](https://github.com/PegaSysEng/pantheon/pull/887) +- Fix authentication file validation errors [\#886](https://github.com/PegaSysEng/pantheon/pull/886) +- Fixing file locations under docker [\#885](https://github.com/PegaSysEng/pantheon/pull/885) - Handle exceptions properly in EthScheduler [\#884](https://github.com/PegaSysEng/pantheon/pull/884) - More bootnodes for goerli [\#880](https://github.com/PegaSysEng/pantheon/pull/880) -- Rename password hash command [\#879](https://github.com/PegaSysEng/pantheon/pull/879) -- Add metrics for EthScheduler executors [\#878](https://github.com/PegaSysEng/pantheon/pull/878) -- Disconnect peer removed from node whitelist [\#877](https://github.com/PegaSysEng/pantheon/pull/877) -- Reduce logging noise from invalid peer discovery packets and handshaking [\#876](https://github.com/PegaSysEng/pantheon/pull/876) -- Detect stalled world state downloads [\#875](https://github.com/PegaSysEng/pantheon/pull/875) -- Limit size of Ibft future message buffer [\#873](https://github.com/PegaSysEng/pantheon/pull/873) -- Ibft2: Replace NewRound with extended Proposal [\#872](https://github.com/PegaSysEng/pantheon/pull/872) -- Fixed admin_addPeer to periodically check maintained connections [\#871](https://github.com/PegaSysEng/pantheon/pull/871) +- Rename password hash command [\#879](https://github.com/PegaSysEng/pantheon/pull/879) +- Add metrics for EthScheduler executors [\#878](https://github.com/PegaSysEng/pantheon/pull/878) +- Disconnect peer removed from node whitelist [\#877](https://github.com/PegaSysEng/pantheon/pull/877) +- Reduce logging noise from invalid peer discovery packets and handshaking [\#876](https://github.com/PegaSysEng/pantheon/pull/876) +- Detect stalled world state downloads [\#875](https://github.com/PegaSysEng/pantheon/pull/875) +- Limit size of Ibft future message buffer [\#873](https://github.com/PegaSysEng/pantheon/pull/873) +- Ibft2: Replace NewRound with extended Proposal [\#872](https://github.com/PegaSysEng/pantheon/pull/872) +- Fixed admin_addPeer to periodically check maintained connections [\#871](https://github.com/PegaSysEng/pantheon/pull/871) - WebSocket method permissions [\#870](https://github.com/PegaSysEng/pantheon/pull/870) -- Select new pivot block when world state becomes unavailable [\#869](https://github.com/PegaSysEng/pantheon/pull/869) -- Introduce FutureUtils to reduce duplicated code around CompletableFuture [\#868](https://github.com/PegaSysEng/pantheon/pull/868) -- Implement world state cancel [\#867](https://github.com/PegaSysEng/pantheon/pull/867) -- Renaming authentication configuration file CLI command [\#865](https://github.com/PegaSysEng/pantheon/pull/865) -- Break out RoundChangeCertificate validation [\#864](https://github.com/PegaSysEng/pantheon/pull/864) -- Disconnect peers where the common ancestor is before our fast sync pivot [\#862](https://github.com/PegaSysEng/pantheon/pull/862) -- Initial scaffolding for block propagation [\#860](https://github.com/PegaSysEng/pantheon/pull/860) -- Fix NullPointerException when determining fast sync pivot [\#859](https://github.com/PegaSysEng/pantheon/pull/859) -- Check for invalid token [\#856](https://github.com/PegaSysEng/pantheon/pull/856) -- Moving NodeWhitelistController to permissioning package [\#855](https://github.com/PegaSysEng/pantheon/pull/855) -- Fix state download race condition by creating a TaskQueue API [\#853](https://github.com/PegaSysEng/pantheon/pull/853) -- Changed separator in JSON RPC permissions [\#852](https://github.com/PegaSysEng/pantheon/pull/852) -- WebSocket acceptance tests now can use WebSockets [\#851](https://github.com/PegaSysEng/pantheon/pull/851) -- IBFT notifies EthPeer when remote node has a better block [\#849](https://github.com/PegaSysEng/pantheon/pull/849) -- Support resuming fast-sync downloads [\#848](https://github.com/PegaSysEng/pantheon/pull/848) -- Tweak Fast Sync Config [\#847](https://github.com/PegaSysEng/pantheon/pull/847) -- RPC authentication configuration validation + tests. [\#846](https://github.com/PegaSysEng/pantheon/pull/846) -- Tidy-up FastSyncState persistence [\#845](https://github.com/PegaSysEng/pantheon/pull/845) -- Do parallel extract signatures in the parallel block importer. [\#844](https://github.com/PegaSysEng/pantheon/pull/844) -- Fix 'the Input Is Too Long' Error on Windows [\#843](https://github.com/PegaSysEng/pantheon/pull/843) (thanks to [glethuillier](https://github.com/glethuillier)). -- Remove unnecessary sleep [\#842](https://github.com/PegaSysEng/pantheon/pull/842) +- Select new pivot block when world state becomes unavailable [\#869](https://github.com/PegaSysEng/pantheon/pull/869) +- Introduce FutureUtils to reduce duplicated code around CompletableFuture [\#868](https://github.com/PegaSysEng/pantheon/pull/868) +- Implement world state cancel [\#867](https://github.com/PegaSysEng/pantheon/pull/867) +- Renaming authentication configuration file CLI command [\#865](https://github.com/PegaSysEng/pantheon/pull/865) +- Break out RoundChangeCertificate validation [\#864](https://github.com/PegaSysEng/pantheon/pull/864) +- Disconnect peers where the common ancestor is before our fast sync pivot [\#862](https://github.com/PegaSysEng/pantheon/pull/862) +- Initial scaffolding for block propagation [\#860](https://github.com/PegaSysEng/pantheon/pull/860) +- Fix NullPointerException when determining fast sync pivot [\#859](https://github.com/PegaSysEng/pantheon/pull/859) +- Check for invalid token [\#856](https://github.com/PegaSysEng/pantheon/pull/856) +- Moving NodeWhitelistController to permissioning package [\#855](https://github.com/PegaSysEng/pantheon/pull/855) +- Fix state download race condition by creating a TaskQueue API [\#853](https://github.com/PegaSysEng/pantheon/pull/853) +- Changed separator in JSON RPC permissions [\#852](https://github.com/PegaSysEng/pantheon/pull/852) +- WebSocket acceptance tests now can use WebSockets [\#851](https://github.com/PegaSysEng/pantheon/pull/851) +- IBFT notifies EthPeer when remote node has a better block [\#849](https://github.com/PegaSysEng/pantheon/pull/849) +- Support resuming fast-sync downloads [\#848](https://github.com/PegaSysEng/pantheon/pull/848) +- Tweak Fast Sync Config [\#847](https://github.com/PegaSysEng/pantheon/pull/847) +- RPC authentication configuration validation + tests. [\#846](https://github.com/PegaSysEng/pantheon/pull/846) +- Tidy-up FastSyncState persistence [\#845](https://github.com/PegaSysEng/pantheon/pull/845) +- Do parallel extract signatures in the parallel block importer. [\#844](https://github.com/PegaSysEng/pantheon/pull/844) +- Fix 'the Input Is Too Long' Error on Windows [\#843](https://github.com/PegaSysEng/pantheon/pull/843) (thanks to [glethuillier](https://github.com/glethuillier)). +- Remove unnecessary sleep [\#842](https://github.com/PegaSysEng/pantheon/pull/842) - Shutdown improvements [\#841](https://github.com/PegaSysEng/pantheon/pull/841) -- Speed up shutdown time [\#838](https://github.com/PegaSysEng/pantheon/pull/838) -- Add metrics to world state downloader [\#837](https://github.com/PegaSysEng/pantheon/pull/837) -- Store pivot block header [\#836](https://github.com/PegaSysEng/pantheon/pull/836) -- Clique should use beneficiary of zero on epoch blocks [\#833](https://github.com/PegaSysEng/pantheon/pull/833) -- Clique should ignore proposals for address 0 [\#831](https://github.com/PegaSysEng/pantheon/pull/831) -- Fix intermittency in FullSyncDownloaderTest [\#830](https://github.com/PegaSysEng/pantheon/pull/830) +- Speed up shutdown time [\#838](https://github.com/PegaSysEng/pantheon/pull/838) +- Add metrics to world state downloader [\#837](https://github.com/PegaSysEng/pantheon/pull/837) +- Store pivot block header [\#836](https://github.com/PegaSysEng/pantheon/pull/836) +- Clique should use beneficiary of zero on epoch blocks [\#833](https://github.com/PegaSysEng/pantheon/pull/833) +- Clique should ignore proposals for address 0 [\#831](https://github.com/PegaSysEng/pantheon/pull/831) +- Fix intermittency in FullSyncDownloaderTest [\#830](https://github.com/PegaSysEng/pantheon/pull/830) - Added the authentication service to the WebSocket service [\#829](https://github.com/PegaSysEng/pantheon/pull/829) -- Extract creation and init of ProtocolContext into a re-usable class [\#828](https://github.com/PegaSysEng/pantheon/pull/828) -- Prevent duplicate commit seals in ibft header [\#827](https://github.com/PegaSysEng/pantheon/pull/827) -- Validate Ibft vanity data length [\#826](https://github.com/PegaSysEng/pantheon/pull/826) -- Refactored json rpc authentication to be provided as a service [\#825](https://github.com/PegaSysEng/pantheon/pull/825) -- Handle unavailable world states [\#824](https://github.com/PegaSysEng/pantheon/pull/824) +- Extract creation and init of ProtocolContext into a re-usable class [\#828](https://github.com/PegaSysEng/pantheon/pull/828) +- Prevent duplicate commit seals in ibft header [\#827](https://github.com/PegaSysEng/pantheon/pull/827) +- Validate Ibft vanity data length [\#826](https://github.com/PegaSysEng/pantheon/pull/826) +- Refactored json rpc authentication to be provided as a service [\#825](https://github.com/PegaSysEng/pantheon/pull/825) +- Handle unavailable world states [\#824](https://github.com/PegaSysEng/pantheon/pull/824) - Password in JWT payload [\#823](https://github.com/PegaSysEng/pantheon/pull/823) - Homogenize error messages when required parameters are set [\#822](https://github.com/PegaSysEng/pantheon/pull/822) ([glethuillier](https://github.com/glethuillier)). -- Set remote peer chain head to parent of block received in NEW\_BLOCK\_MESSAGE [\#819](https://github.com/PegaSysEng/pantheon/pull/819) -- Peer disconnects should not result in stack traces [\#818](https://github.com/PegaSysEng/pantheon/pull/818) -- Abort previous builds [\#817](https://github.com/PegaSysEng/pantheon/pull/817) -- Parallel build stages [\#816](https://github.com/PegaSysEng/pantheon/pull/816) -- JWT authentication for JSON-RPC [\#815](https://github.com/PegaSysEng/pantheon/pull/815) -- Log errors that occur while finding a common ancestor [\#814](https://github.com/PegaSysEng/pantheon/pull/814) +- Set remote peer chain head to parent of block received in NEW\_BLOCK\_MESSAGE [\#819](https://github.com/PegaSysEng/pantheon/pull/819) +- Peer disconnects should not result in stack traces [\#818](https://github.com/PegaSysEng/pantheon/pull/818) +- Abort previous builds [\#817](https://github.com/PegaSysEng/pantheon/pull/817) +- Parallel build stages [\#816](https://github.com/PegaSysEng/pantheon/pull/816) +- JWT authentication for JSON-RPC [\#815](https://github.com/PegaSysEng/pantheon/pull/815) +- Log errors that occur while finding a common ancestor [\#814](https://github.com/PegaSysEng/pantheon/pull/814) - Shuffled log levels [\#813](https://github.com/PegaSysEng/pantheon/pull/813) -- Prevent duplicate IBFT messages being processed by state machine [\#811](https://github.com/PegaSysEng/pantheon/pull/811) -- Fix Orion startup ports [\#810](https://github.com/PegaSysEng/pantheon/pull/810) -- Commit world state continuously [\#809](https://github.com/PegaSysEng/pantheon/pull/809) -- Improve block propagation time [\#808](https://github.com/PegaSysEng/pantheon/pull/808) -- JSON-RPC authentication cli options & acceptance tests [\#807](https://github.com/PegaSysEng/pantheon/pull/807) +- Prevent duplicate IBFT messages being processed by state machine [\#811](https://github.com/PegaSysEng/pantheon/pull/811) +- Fix Orion startup ports [\#810](https://github.com/PegaSysEng/pantheon/pull/810) +- Commit world state continuously [\#809](https://github.com/PegaSysEng/pantheon/pull/809) +- Improve block propagation time [\#808](https://github.com/PegaSysEng/pantheon/pull/808) +- JSON-RPC authentication cli options & acceptance tests [\#807](https://github.com/PegaSysEng/pantheon/pull/807) - Remove privacy not supported warning [\#806](https://github.com/PegaSysEng/pantheon/pull/806) (thanks to [vinistevam](https://github.com/vinistevam)) - Wire up Private Transaction Processor [\#805](https://github.com/PegaSysEng/pantheon/pull/805) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Apply a limit to the number of responses in RespondingEthPeer.respondWhile [\#803](https://github.com/PegaSysEng/pantheon/pull/803) +- Apply a limit to the number of responses in RespondingEthPeer.respondWhile [\#803](https://github.com/PegaSysEng/pantheon/pull/803) - Avoid requesting empty block bodies from the network. [\#802](https://github.com/PegaSysEng/pantheon/pull/802) -- Handle partial responses to get receipts requests [\#801](https://github.com/PegaSysEng/pantheon/pull/801) -- Rename functions in Ibft MessageValidator [\#800](https://github.com/PegaSysEng/pantheon/pull/800) -- Upgrade GoogleJavaFormat to 1.7 [\#795](https://github.com/PegaSysEng/pantheon/pull/795) -- Minor refactorings of IntegrationTest infrastructure [\#786](https://github.com/PegaSysEng/pantheon/pull/786) -- Rework Ibft MessageValidatorFactory [\#785](https://github.com/PegaSysEng/pantheon/pull/785) -- Rework IbftRoundFactory [\#784](https://github.com/PegaSysEng/pantheon/pull/784) -- Rename artefacts to artifacts within IBFT [\#782](https://github.com/PegaSysEng/pantheon/pull/782) +- Handle partial responses to get receipts requests [\#801](https://github.com/PegaSysEng/pantheon/pull/801) +- Rename functions in Ibft MessageValidator [\#800](https://github.com/PegaSysEng/pantheon/pull/800) +- Upgrade GoogleJavaFormat to 1.7 [\#795](https://github.com/PegaSysEng/pantheon/pull/795) +- Minor refactorings of IntegrationTest infrastructure [\#786](https://github.com/PegaSysEng/pantheon/pull/786) +- Rework Ibft MessageValidatorFactory [\#785](https://github.com/PegaSysEng/pantheon/pull/785) +- Rework IbftRoundFactory [\#784](https://github.com/PegaSysEng/pantheon/pull/784) +- Rename artefacts to artifacts within IBFT [\#782](https://github.com/PegaSysEng/pantheon/pull/782) - Rename TerminatedRoundArtefacts to PreparedRoundArtefacts [\#781](https://github.com/PegaSysEng/pantheon/pull/781) -- Rename Ibft MessageFactory methods [\#779](https://github.com/PegaSysEng/pantheon/pull/779) -- Update WorldStateDownloader to only filter out known code requests [\#777](https://github.com/PegaSysEng/pantheon/pull/777) +- Rename Ibft MessageFactory methods [\#779](https://github.com/PegaSysEng/pantheon/pull/779) +- Update WorldStateDownloader to only filter out known code requests [\#777](https://github.com/PegaSysEng/pantheon/pull/777) - Multiple name options only search for the longest one [\#776](https://github.com/PegaSysEng/pantheon/pull/776) -- Move ethTaskTimer to abstract root [\#775](https://github.com/PegaSysEng/pantheon/pull/775) -- Parallel Block importer [\#774](https://github.com/PegaSysEng/pantheon/pull/774) -- Wait for a peer with an estimated chain height before selecting a pivot block [\#772](https://github.com/PegaSysEng/pantheon/pull/772) -- Randomly perform full validation when fast syncing blocks [\#770](https://github.com/PegaSysEng/pantheon/pull/770) -- IBFT Message rework, piggybacking blocks on msgs. [\#769](https://github.com/PegaSysEng/pantheon/pull/769) +- Move ethTaskTimer to abstract root [\#775](https://github.com/PegaSysEng/pantheon/pull/775) +- Parallel Block importer [\#774](https://github.com/PegaSysEng/pantheon/pull/774) +- Wait for a peer with an estimated chain height before selecting a pivot block [\#772](https://github.com/PegaSysEng/pantheon/pull/772) +- Randomly perform full validation when fast syncing blocks [\#770](https://github.com/PegaSysEng/pantheon/pull/770) +- IBFT Message rework, piggybacking blocks on msgs. [\#769](https://github.com/PegaSysEng/pantheon/pull/769) - EthScheduler additions [\#767](https://github.com/PegaSysEng/pantheon/pull/767) -- Fixing node whitelist isPermitted check [\#766](https://github.com/PegaSysEng/pantheon/pull/766) -- Eth/63 labels [\#764](https://github.com/PegaSysEng/pantheon/pull/764) -- Permissioning whitelist persistence. [\#763](https://github.com/PegaSysEng/pantheon/pull/763) -- Created message validators for NewRound and RoundChange [\#760](https://github.com/PegaSysEng/pantheon/pull/760) -- Add tests for FastSyncChainDownloader as a whole [\#758](https://github.com/PegaSysEng/pantheon/pull/758) +- Fixing node whitelist isPermitted check [\#766](https://github.com/PegaSysEng/pantheon/pull/766) +- Eth/63 labels [\#764](https://github.com/PegaSysEng/pantheon/pull/764) +- Permissioning whitelist persistence. [\#763](https://github.com/PegaSysEng/pantheon/pull/763) +- Created message validators for NewRound and RoundChange [\#760](https://github.com/PegaSysEng/pantheon/pull/760) +- Add tests for FastSyncChainDownloader as a whole [\#758](https://github.com/PegaSysEng/pantheon/pull/758) - Flatten IBFT Message API [\#757](https://github.com/PegaSysEng/pantheon/pull/757) -- Added TerminatedRoundArtefacts [\#756](https://github.com/PegaSysEng/pantheon/pull/756) -- Fix thread names in EthScheduler to include the thread number [\#755](https://github.com/PegaSysEng/pantheon/pull/755) -- Separate round change reception from RoundChangeCertificate [\#754](https://github.com/PegaSysEng/pantheon/pull/754) -- JSON-RPC authentication login [\#753](https://github.com/PegaSysEng/pantheon/pull/753) -- Spilt Ibft MessageValidator into components [\#752](https://github.com/PegaSysEng/pantheon/pull/752) -- Ensure first checkpoint headers is always in local blockchain for FastSyncCheckpointHeaderManager [\#750](https://github.com/PegaSysEng/pantheon/pull/750) -- Refactored permissioning components to be Optional. [\#747](https://github.com/PegaSysEng/pantheon/pull/747) -- Integrate rocksdb-based queue into WorldStateDownloader [\#746](https://github.com/PegaSysEng/pantheon/pull/746) +- Added TerminatedRoundArtefacts [\#756](https://github.com/PegaSysEng/pantheon/pull/756) +- Fix thread names in EthScheduler to include the thread number [\#755](https://github.com/PegaSysEng/pantheon/pull/755) +- Separate round change reception from RoundChangeCertificate [\#754](https://github.com/PegaSysEng/pantheon/pull/754) +- JSON-RPC authentication login [\#753](https://github.com/PegaSysEng/pantheon/pull/753) +- Spilt Ibft MessageValidator into components [\#752](https://github.com/PegaSysEng/pantheon/pull/752) +- Ensure first checkpoint headers is always in local blockchain for FastSyncCheckpointHeaderManager [\#750](https://github.com/PegaSysEng/pantheon/pull/750) +- Refactored permissioning components to be Optional. [\#747](https://github.com/PegaSysEng/pantheon/pull/747) +- Integrate rocksdb-based queue into WorldStateDownloader [\#746](https://github.com/PegaSysEng/pantheon/pull/746) - Generify orion to enclave [\#745](https://github.com/PegaSysEng/pantheon/pull/745) (thanks to [vinistevam](https://github.com/vinistevam)) -- Moved IBFT Message factory to use wrapped message types [\#744](https://github.com/PegaSysEng/pantheon/pull/744) +- Moved IBFT Message factory to use wrapped message types [\#744](https://github.com/PegaSysEng/pantheon/pull/744) - Handle timeouts when requesting checkpoint headers correctly [\#743](https://github.com/PegaSysEng/pantheon/pull/743) -- Update RoundChangeManager to use flattened message [\#742](https://github.com/PegaSysEng/pantheon/pull/742) -- Handle validation failures when fast importing blocks [\#741](https://github.com/PegaSysEng/pantheon/pull/741) +- Update RoundChangeManager to use flattened message [\#742](https://github.com/PegaSysEng/pantheon/pull/742) +- Handle validation failures when fast importing blocks [\#741](https://github.com/PegaSysEng/pantheon/pull/741) - Updated IbftRound and RoundState APIs to use wrapped messages [\#740](https://github.com/PegaSysEng/pantheon/pull/740) - Exception handling [\#739](https://github.com/PegaSysEng/pantheon/pull/739) - Upgrade dependency versions and build cleanup [\#738](https://github.com/PegaSysEng/pantheon/pull/738) -- Update IbftBlockHeigntManager to accept new message types. [\#737](https://github.com/PegaSysEng/pantheon/pull/737) -- Error response handling for permissions APIs [\#736](https://github.com/PegaSysEng/pantheon/pull/736) -- IPV6 bootnodes don't work [\#735](https://github.com/PegaSysEng/pantheon/pull/735) -- Updated to use tags of pantheon build rather than another repo [\#734](https://github.com/PegaSysEng/pantheon/pull/734) -- Log milestones at startup and other minor logging improvements [\#733](https://github.com/PegaSysEng/pantheon/pull/733) +- Update IbftBlockHeigntManager to accept new message types. [\#737](https://github.com/PegaSysEng/pantheon/pull/737) +- Error response handling for permissions APIs [\#736](https://github.com/PegaSysEng/pantheon/pull/736) +- IPV6 bootnodes don't work [\#735](https://github.com/PegaSysEng/pantheon/pull/735) +- Updated to use tags of pantheon build rather than another repo [\#734](https://github.com/PegaSysEng/pantheon/pull/734) +- Log milestones at startup and other minor logging improvements [\#733](https://github.com/PegaSysEng/pantheon/pull/733) - Create wrapper types for Ibft Signed messages [\#731](https://github.com/PegaSysEng/pantheon/pull/731) -- Ibft to uniquely ID messages by their hash [\#730](https://github.com/PegaSysEng/pantheon/pull/730) +- Ibft to uniquely ID messages by their hash [\#730](https://github.com/PegaSysEng/pantheon/pull/730) - Rename ibftrevised to ibft2 [\#722](https://github.com/PegaSysEng/pantheon/pull/722) -- Limit ibft msg queues [\#704](https://github.com/PegaSysEng/pantheon/pull/704) +- Limit ibft msg queues [\#704](https://github.com/PegaSysEng/pantheon/pull/704) - Implement privacy precompiled contract [\#696](https://github.com/PegaSysEng/pantheon/pull/696) (thanks to [Puneetha17](https://github.com/Puneetha17)) -- Integration of RecursivePeerRefreshState and PeerDiscoveryController [\#420](https://github.com/PegaSysEng/pantheon/pull/420) +- Integration of RecursivePeerRefreshState and PeerDiscoveryController [\#420](https://github.com/PegaSysEng/pantheon/pull/420) ## 0.9.1 -Built and compatible with with JDK8. +Built and compatible with with JDK8. ## 0.9 ### Breaking Changes to Command Line -Breaking changes have been made to the command line options in v0.9 to improve usability. Many v0.8 command line options no longer work. +Breaking changes have been made to the command line options in v0.9 to improve usability. Many v0.8 command line options no longer work. -The [documentation](https://docs.pantheon.pegasys.tech/en/latest/) has been updated throughout to use the changed command line options and the [command line reference](https://besu.hyperledger.org/en/stable/) documents the changed options. +The [documentation](https://docs.pantheon.pegasys.tech/en/latest/) has been updated throughout to use the changed command line options and the [command line reference](https://besu.hyperledger.org/en/stable/) documents the changed options. | Previous Option | New Option | Change | |-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------| @@ -1397,14 +1472,14 @@ The [documentation](https://docs.pantheon.pegasys.tech/en/latest/) has been upda | `--p2p-listen=` | [`--p2p-host=`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#p2p-hostt) and [`--p2p-port=`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#p2p-port) | Split into host and port options | | `--rinkeby` | [`--network=rinkeby`]((https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#network) | Replaced by `--network` option | | `--ropsten` | [`--network=ropsten`]((https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#network) | Replaced by `--network` option | -| `--rpc-enabled` | [` --rpc-http-enabled`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-http-enabled)| Renamed| +| `--rpc-enabled` | [` --rpc-http-enabled`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-http-enabled)| Renamed| | `--rpc-listen=` | [`--rpc-http-host=`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-http-host) and [`--rpc-http-port=`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-http-port) | Split into host and port options | | `--rpc-api` | [`--rpc-http-api`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-http-api)| Renamed | -| `--rpc-cors-origins` | [`--rpc-http-cors-origins`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-http-cors-origins) | Renamed | -| `--ws-enabled` | [`--rpc-ws-enabled`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-ws-enabled) | Renamed | +| `--rpc-cors-origins` | [`--rpc-http-cors-origins`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-http-cors-origins) | Renamed | +| `--ws-enabled` | [`--rpc-ws-enabled`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-ws-enabled) | Renamed | | `--ws-api` | [`--rpc-ws-api`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-ws-api) | Renamed| | `--ws-listen=` | [`--rpc-ws-host=`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-ws-host) and [`--rpc-ws-port=`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-ws-port) | Split into host and port options | -| `--ws-refresh-delay` | [`--rpc-ws-refresh-delay`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-ws-refresh-delay)|Renamed| +| `--ws-refresh-delay` | [`--rpc-ws-refresh-delay`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Syntax/#rpc-ws-refresh-delay)|Renamed| | Previous Subcommand | New Subcommand | Change | |-------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------| @@ -1412,47 +1487,47 @@ The [documentation](https://docs.pantheon.pegasys.tech/en/latest/) has been upda | `pantheon export-pub-key `| [`pantheon public-key export --to=`](https://besu.hyperledger.org/en/latest/Reference/CLI/CLI-Subcommands/#public-key) | Renamed | -### Private Network Quickstart +### Private Network Quickstart -The Private Network Quickstart has been moved from the `pantheon` repository to the `pantheon-quickstart` -repository. The [Private Network Quickstart tutorial](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Private-Network-Quickstart/) +The Private Network Quickstart has been moved from the `pantheon` repository to the `pantheon-quickstart` +repository. The [Private Network Quickstart tutorial](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Private-Network-Quickstart/) has been updated to use the moved quickstart. -### Additions and Improvements +### Additions and Improvements - `--network=goerli` supports relaunch of Görli testnet [\#717](https://github.com/PegaSysEng/pantheon/pull/717) -- TOML authentication provider [\#689](https://github.com/PegaSysEng/pantheon/pull/689) +- TOML authentication provider [\#689](https://github.com/PegaSysEng/pantheon/pull/689) - Metrics Push Gateway Options [\#678](https://github.com/PegaSysEng/pantheon/pull/678) -- Additional logging details for IBFT 2.0 [\#650](https://github.com/PegaSysEng/pantheon/pull/650) +- Additional logging details for IBFT 2.0 [\#650](https://github.com/PegaSysEng/pantheon/pull/650) - Permissioning config TOML file [\#643](https://github.com/PegaSysEng/pantheon/pull/643) -- Added metrics Prometheus Push Gateway Support [\#638](https://github.com/PegaSysEng/pantheon/pull/638) +- Added metrics Prometheus Push Gateway Support [\#638](https://github.com/PegaSysEng/pantheon/pull/638) - Clique and IBFT not enabled by default in RPC APIs [\#635](https://github.com/PegaSysEng/pantheon/pull/635) - Added `admin_addPeer` JSON-RPC API method [\#622](https://github.com/PegaSysEng/pantheon/pull/622) -- Implemented `--p2p-enabled` configuration item [\#619](https://github.com/PegaSysEng/pantheon/pull/619) -- Command options and commands renaming [\#618](https://github.com/PegaSysEng/pantheon/pull/618) -- Added IBFT get pending votes [\#603](https://github.com/PegaSysEng/pantheon/pull/603) -- Implement Petersburg hardfork [\#601](https://github.com/PegaSysEng/pantheon/pull/601) +- Implemented `--p2p-enabled` configuration item [\#619](https://github.com/PegaSysEng/pantheon/pull/619) +- Command options and commands renaming [\#618](https://github.com/PegaSysEng/pantheon/pull/618) +- Added IBFT get pending votes [\#603](https://github.com/PegaSysEng/pantheon/pull/603) +- Implement Petersburg hardfork [\#601](https://github.com/PegaSysEng/pantheon/pull/601) - Added private transaction abstraction [\#592](https://github.com/PegaSysEng/pantheon/pull/592) (thanks to [iikirilov](https://github.com/iikirilov)) - Added privacy command line commands [\#584](https://github.com/PegaSysEng/pantheon/pull/584) (thanks to [Puneetha17](https://github.com/Puneetha17)) - Documentation updates include: - - Updated [Private Network Quickstart tutorial](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Private-Network-Quickstart/) + - Updated [Private Network Quickstart tutorial](https://besu.hyperledger.org/en/latest/Tutorials/Quickstarts/Private-Network-Quickstart/) to use quickstart in `pantheon-quickstart` repository and indicate that the quickstart is not supported on Windows. - - Added IBFT 2.0 [content](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/IBFT/) and [JSON RPC API methods](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#ibft-20-methods). + - Added IBFT 2.0 [content](https://besu.hyperledger.org/en/latest/HowTo/Configure/Consensus-Protocols/IBFT/) and [JSON RPC API methods](https://besu.hyperledger.org/en/latest/Reference/API-Methods/#ibft-20-methods). - Added [consensus protocols content](https://besu.hyperledger.org/en/latest/Concepts/Consensus-Protocols/Comparing-PoA/). - - Added content on [events and logs](https://besu.hyperledger.org/en/latest/Concepts/Events-and-Logs/), and [using filters](https://besu.hyperledger.org/en/latest/HowTo/Interact/Filters/Accessing-Logs-Using-JSON-RPC/). + - Added content on [events and logs](https://besu.hyperledger.org/en/latest/Concepts/Events-and-Logs/), and [using filters](https://besu.hyperledger.org/en/latest/HowTo/Interact/Filters/Accessing-Logs-Using-JSON-RPC/). - Added content on integrating with [Prometheus Push Gateway](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Monitoring-Performance/#running-prometheus-with-besu-in-push-mode) - -### Technical Improvements -- Download receipts during fast sync and import without processing transactions [\#701](https://github.com/PegaSysEng/pantheon/pull/701) -- Removed CLI options for `--nodes-whitelist` and `--accounts-whitelist` [\#694](https://github.com/PegaSysEng/pantheon/pull/694) -- Delegate `getRootCause` through to Guava's implementation [\#692](https://github.com/PegaSysEng/pantheon/pull/692) +### Technical Improvements + +- Download receipts during fast sync and import without processing transactions [\#701](https://github.com/PegaSysEng/pantheon/pull/701) +- Removed CLI options for `--nodes-whitelist` and `--accounts-whitelist` [\#694](https://github.com/PegaSysEng/pantheon/pull/694) +- Delegate `getRootCause` through to Guava's implementation [\#692](https://github.com/PegaSysEng/pantheon/pull/692) - Benchmark update [\#691](https://github.com/PegaSysEng/pantheon/pull/691) -- Implement chain download for fast sync [\#690](https://github.com/PegaSysEng/pantheon/pull/690) -- Allow missing accounts to create zero-cost transactions [\#685](https://github.com/PegaSysEng/pantheon/pull/685) -- Node private key location should be fixed under docker [\#684](https://github.com/PegaSysEng/pantheon/pull/684) -- Parallel Processing File Import Performance [\#683](https://github.com/PegaSysEng/pantheon/pull/683) -- Integrate actual `WorldStateDownloader` with the fast sync work flow [\#682](https://github.com/PegaSysEng/pantheon/pull/682) +- Implement chain download for fast sync [\#690](https://github.com/PegaSysEng/pantheon/pull/690) +- Allow missing accounts to create zero-cost transactions [\#685](https://github.com/PegaSysEng/pantheon/pull/685) +- Node private key location should be fixed under docker [\#684](https://github.com/PegaSysEng/pantheon/pull/684) +- Parallel Processing File Import Performance [\#683](https://github.com/PegaSysEng/pantheon/pull/683) +- Integrate actual `WorldStateDownloader` with the fast sync work flow [\#682](https://github.com/PegaSysEng/pantheon/pull/682) - Removed `--max-trailing-peers` option [\#680](https://github.com/PegaSysEng/pantheon/pull/680) - Enabled warning on CLI dependent options [\#679](https://github.com/PegaSysEng/pantheon/pull/679) - Update WorldStateDownloader run\(\) interface to accept header [\#677](https://github.com/PegaSysEng/pantheon/pull/677) @@ -1461,53 +1536,53 @@ has been updated to use the moved quickstart. - Update orion default port approach [\#660](https://github.com/PegaSysEng/pantheon/pull/660) - Extract out generic parts of Downloader [\#659](https://github.com/PegaSysEng/pantheon/pull/659) - Start world downloader [\#658](https://github.com/PegaSysEng/pantheon/pull/658) -- Create a simple `WorldStateDownloader` [\#657](https://github.com/PegaSysEng/pantheon/pull/657) +- Create a simple `WorldStateDownloader` [\#657](https://github.com/PegaSysEng/pantheon/pull/657) - Added handling for when p2p is disabled [\#655](https://github.com/PegaSysEng/pantheon/pull/655) - Enabled command line configuration for privacy precompiled contract address [\#653](https://github.com/PegaSysEng/pantheon/pull/653) (thanks to [Puneetha17](https://github.com/Puneetha17)) - IBFT transmitted packets are logged by gossiper [\#652](https://github.com/PegaSysEng/pantheon/pull/652) -- `admin_addPeer` acceptance test [\#651](https://github.com/PegaSysEng/pantheon/pull/651) -- Added `p2pEnabled` configuration to `ProcessBesuNodeRunner` [\#649](https://github.com/PegaSysEng/pantheon/pull/649) -- Added description to automatic benchmarks [\#646](https://github.com/PegaSysEng/pantheon/pull/646) -- Added `network` option [\#645](https://github.com/PegaSysEng/pantheon/pull/645) +- `admin_addPeer` acceptance test [\#651](https://github.com/PegaSysEng/pantheon/pull/651) +- Added `p2pEnabled` configuration to `ProcessBesuNodeRunner` [\#649](https://github.com/PegaSysEng/pantheon/pull/649) +- Added description to automatic benchmarks [\#646](https://github.com/PegaSysEng/pantheon/pull/646) +- Added `network` option [\#645](https://github.com/PegaSysEng/pantheon/pull/645) - Remove OrionConfiguration [\#644](https://github.com/PegaSysEng/pantheon/pull/644) (thanks to [Puneetha17](https://github.com/Puneetha17)) - IBFT Json Acceptance tests [\#634](https://github.com/PegaSysEng/pantheon/pull/634) -- Upgraded build image to one that contains libsodium [\#632](https://github.com/PegaSysEng/pantheon/pull/632) -- Command line fixes [\#630](https://github.com/PegaSysEng/pantheon/pull/630) -- Consider peer count insufficient until minimum peers for fast sync are connected [\#629](https://github.com/PegaSysEng/pantheon/pull/629) +- Upgraded build image to one that contains libsodium [\#632](https://github.com/PegaSysEng/pantheon/pull/632) +- Command line fixes [\#630](https://github.com/PegaSysEng/pantheon/pull/630) +- Consider peer count insufficient until minimum peers for fast sync are connected [\#629](https://github.com/PegaSysEng/pantheon/pull/629) - Build tweaks [\#628](https://github.com/PegaSysEng/pantheon/pull/628) - IBFT ensure non-validator does not partake in consensus [\#627](https://github.com/PegaSysEng/pantheon/pull/627) - Added ability in acceptance tests to set up a node with `--no-discovery` [\#624](https://github.com/PegaSysEng/pantheon/pull/624) - Gossip integration test [\#623](https://github.com/PegaSysEng/pantheon/pull/623) -- Removed quickstart code and CI pipeline [\#616](https://github.com/PegaSysEng/pantheon/pull/616) -- IBFT Integration Tests - Spurious Behaviour [\#615](https://github.com/PegaSysEng/pantheon/pull/615) -- Refactoring for more readable IBFT IT [\#614](https://github.com/PegaSysEng/pantheon/pull/614) -- Start of fast sync downloader [\#613](https://github.com/PegaSysEng/pantheon/pull/613) -- Split `IbftProcessor` into looping and event processing [\#612](https://github.com/PegaSysEng/pantheon/pull/612) -- IBFT Int Test - changed `TestContextFactory` to a builder [\#611](https://github.com/PegaSysEng/pantheon/pull/611) -- Discard prior round change msgs [\#610](https://github.com/PegaSysEng/pantheon/pull/610) -- `IbftGetValidatorsByBlockHash` added to json factory [\#607](https://github.com/PegaSysEng/pantheon/pull/607) -- IBFT Validator RPCs to return list of strings [\#606](https://github.com/PegaSysEng/pantheon/pull/606) -- Update Benchmark [\#605](https://github.com/PegaSysEng/pantheon/pull/605) -- Remove db package and move classes to more appropriate locations [\#599](https://github.com/PegaSysEng/pantheon/pull/599) -- Added `GetReceiptsFromPeerTask` [\#598](https://github.com/PegaSysEng/pantheon/pull/598) -- Added `GetNodeDataFromPeerTask` [\#597](https://github.com/PegaSysEng/pantheon/pull/597) -- Fixed deprecation warnings [\#596](https://github.com/PegaSysEng/pantheon/pull/596) -- IBFT Integration Tests - Future Height [\#591](https://github.com/PegaSysEng/pantheon/pull/591) -- Added `getNodeData` to `EthPeer` to enable requesting node data [\#589](https://github.com/PegaSysEng/pantheon/pull/589) -- `Blockcreator` to use `parentblock` specified at constuction [\#588](https://github.com/PegaSysEng/pantheon/pull/588) -- Support responding to `GetNodeData` requests [\#587](https://github.com/PegaSysEng/pantheon/pull/587) -- IBFT validates block on proposal reception [\#583](https://github.com/PegaSysEng/pantheon/pull/583) -- Rework `NewRoundValidator` tests [\#582](https://github.com/PegaSysEng/pantheon/pull/582) -- IBFT split extra data validation rule into components [\#581](https://github.com/PegaSysEng/pantheon/pull/581) -- Allow attached rules to be flagged `light` [\#580](https://github.com/PegaSysEng/pantheon/pull/580) -- Split Block Validation from Importing [\#579](https://github.com/PegaSysEng/pantheon/pull/579) -- Refactor `RoundChangeManager` creation [\#578](https://github.com/PegaSysEng/pantheon/pull/578) -- Add `-SNAPSHOT` postfix to version [\#577](https://github.com/PegaSysEng/pantheon/pull/577) -- IBFT - prevent proposed block being imported twice [\#576](https://github.com/PegaSysEng/pantheon/pull/576) -- Version upgrades [\#571](https://github.com/PegaSysEng/pantheon/pull/571) -- Tests that CLI options are disabled under docker [\#566](https://github.com/PegaSysEng/pantheon/pull/566) -- Renamed IBFT networking classes [\#555](https://github.com/PegaSysEng/pantheon/pull/555) -- Removed dead code from the consensus package [\#554](https://github.com/PegaSysEng/pantheon/pull/554) +- Removed quickstart code and CI pipeline [\#616](https://github.com/PegaSysEng/pantheon/pull/616) +- IBFT Integration Tests - Spurious Behaviour [\#615](https://github.com/PegaSysEng/pantheon/pull/615) +- Refactoring for more readable IBFT IT [\#614](https://github.com/PegaSysEng/pantheon/pull/614) +- Start of fast sync downloader [\#613](https://github.com/PegaSysEng/pantheon/pull/613) +- Split `IbftProcessor` into looping and event processing [\#612](https://github.com/PegaSysEng/pantheon/pull/612) +- IBFT Int Test - changed `TestContextFactory` to a builder [\#611](https://github.com/PegaSysEng/pantheon/pull/611) +- Discard prior round change msgs [\#610](https://github.com/PegaSysEng/pantheon/pull/610) +- `IbftGetValidatorsByBlockHash` added to json factory [\#607](https://github.com/PegaSysEng/pantheon/pull/607) +- IBFT Validator RPCs to return list of strings [\#606](https://github.com/PegaSysEng/pantheon/pull/606) +- Update Benchmark [\#605](https://github.com/PegaSysEng/pantheon/pull/605) +- Remove db package and move classes to more appropriate locations [\#599](https://github.com/PegaSysEng/pantheon/pull/599) +- Added `GetReceiptsFromPeerTask` [\#598](https://github.com/PegaSysEng/pantheon/pull/598) +- Added `GetNodeDataFromPeerTask` [\#597](https://github.com/PegaSysEng/pantheon/pull/597) +- Fixed deprecation warnings [\#596](https://github.com/PegaSysEng/pantheon/pull/596) +- IBFT Integration Tests - Future Height [\#591](https://github.com/PegaSysEng/pantheon/pull/591) +- Added `getNodeData` to `EthPeer` to enable requesting node data [\#589](https://github.com/PegaSysEng/pantheon/pull/589) +- `Blockcreator` to use `parentblock` specified at constuction [\#588](https://github.com/PegaSysEng/pantheon/pull/588) +- Support responding to `GetNodeData` requests [\#587](https://github.com/PegaSysEng/pantheon/pull/587) +- IBFT validates block on proposal reception [\#583](https://github.com/PegaSysEng/pantheon/pull/583) +- Rework `NewRoundValidator` tests [\#582](https://github.com/PegaSysEng/pantheon/pull/582) +- IBFT split extra data validation rule into components [\#581](https://github.com/PegaSysEng/pantheon/pull/581) +- Allow attached rules to be flagged `light` [\#580](https://github.com/PegaSysEng/pantheon/pull/580) +- Split Block Validation from Importing [\#579](https://github.com/PegaSysEng/pantheon/pull/579) +- Refactor `RoundChangeManager` creation [\#578](https://github.com/PegaSysEng/pantheon/pull/578) +- Add `-SNAPSHOT` postfix to version [\#577](https://github.com/PegaSysEng/pantheon/pull/577) +- IBFT - prevent proposed block being imported twice [\#576](https://github.com/PegaSysEng/pantheon/pull/576) +- Version upgrades [\#571](https://github.com/PegaSysEng/pantheon/pull/571) +- Tests that CLI options are disabled under docker [\#566](https://github.com/PegaSysEng/pantheon/pull/566) +- Renamed IBFT networking classes [\#555](https://github.com/PegaSysEng/pantheon/pull/555) +- Removed dead code from the consensus package [\#554](https://github.com/PegaSysEng/pantheon/pull/554) - Prepared private transaction support [\#538](https://github.com/PegaSysEng/pantheon/pull/538) (thanks to [iikirilov](https://github.com/iikirilov)) ## 0.8.5 @@ -1525,186 +1600,186 @@ Indefinitely delays the roll-out of Constantinople on Ethereum Mainnet due to a ### Docker Image -If you have been running a node using the v0.8.3 Docker image, the node was not saving data to the +If you have been running a node using the v0.8.3 Docker image, the node was not saving data to the specified [data directory](https://besu.hyperledger.org/en/stable/), or referring to the custom [configuration file](https://besu.hyperledger.org/en/stable/) -or [genesis file](https://besu.hyperledger.org/en/stable/). +or [genesis file](https://besu.hyperledger.org/en/stable/). To recover the node key and data directory from the Docker container: `docker cp :/opt/pantheon/key ` -`docker cp :/opt/pantheon/database ` +`docker cp :/opt/pantheon/database ` -Where `container` is the name or ID of the Docker container containing the Besu node. +Where `container` is the name or ID of the Docker container containing the Besu node. -The container can be running or stopped when you copy the key and data directory. If your node was +The container can be running or stopped when you copy the key and data directory. If your node was fully synchronized to MainNet, the data directory will be ~2TB. When restarting your node with the v0.8.4 Docker image: -* Save the node key in the [`key` file](https://besu.hyperledger.org/en/latest/Concepts/Node-Keys/#node-private-key) in the data +* Save the node key in the [`key` file](https://besu.hyperledger.org/en/latest/Concepts/Node-Keys/#node-private-key) in the data directory or specify the location using the [`--node-private-key` option](https://besu.hyperledger.org/en/stable/). -* Specify the ` Date: Thu, 27 Feb 2020 14:51:43 +0100 Subject: [PATCH 38/38] [BESU-194] Remove max pivot block resets during fast sync (#427) * remove max pivot block resets during fast sync * increase max retry number and fix test * change logs in the handleFailure method * change logs related to suspicious number of retries Signed-off-by: Karim TAAM --- .../eth/sync/fastsync/FastSyncActions.java | 6 ++ .../eth/sync/fastsync/FastSyncDownloader.java | 20 ++++- .../sync/fastsync/PivotBlockRetriever.java | 8 +- .../sync/fastsync/FastSyncDownloaderTest.java | 73 +++++++++++++++++++ 4 files changed, 102 insertions(+), 5 deletions(-) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java index 2e50fe98aca..b183ea0c4ee 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java @@ -36,6 +36,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -88,6 +89,11 @@ public CompletableFuture waitForSuitablePeers(final FastSyncState .thenApply(successfulWaitResult -> fastSyncState); } + public CompletableFuture scheduleFutureTask( + final Supplier> future, final Duration duration) { + return ethContext.getScheduler().scheduleFutureTask(future, duration); + } + private CompletableFuture waitForAnyPeer() { final CompletableFuture waitForPeerResult = ethContext.getScheduler().timeout(WaitForPeersTask.create(ethContext, 1, metricsSystem)); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloader.java index fa53e2aee9f..40a5d22aeb8 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloader.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.nio.file.Path; +import java.time.Duration; import java.util.Optional; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; @@ -38,6 +39,9 @@ import org.apache.logging.log4j.Logger; public class FastSyncDownloader { + + private static final Duration FAST_SYNC_RETRY_DELAY = Duration.ofSeconds(5); + private static final Logger LOG = LogManager.getLogger(); private final FastSyncActions fastSyncActions; private final WorldStateDownloader worldStateDownloader; @@ -80,17 +84,25 @@ private CompletableFuture start(final FastSyncState fastSyncState .thenApply(this::updateMaxTrailingPeers) .thenApply(this::storeState) .thenCompose(this::downloadChainAndWorldState), - this::handleWorldStateUnavailable); + this::handleFailure); } - private CompletableFuture handleWorldStateUnavailable(final Throwable error) { + private CompletableFuture handleFailure(final Throwable error) { trailingPeerRequirements = Optional.empty(); - if (ExceptionUtils.rootCause(error) instanceof StalledDownloadException) { + if (ExceptionUtils.rootCause(error) instanceof FastSyncException) { + return completedExceptionally(error); + } else if (ExceptionUtils.rootCause(error) instanceof StalledDownloadException) { LOG.warn( "Fast sync was unable to download the world state. Retrying with a new pivot block."); return start(FastSyncState.EMPTY_SYNC_STATE); } else { - return completedExceptionally(error); + LOG.error( + "Encountered an unexpected error during fast sync. Restarting fast sync in " + + FAST_SYNC_RETRY_DELAY + + " seconds.", + error); + return fastSyncActions.scheduleFutureTask( + () -> start(FastSyncState.EMPTY_SYNC_STATE), FAST_SYNC_RETRY_DELAY); } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotBlockRetriever.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotBlockRetriever.java index 1750d2bc36b..b6697efedd8 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotBlockRetriever.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotBlockRetriever.java @@ -42,7 +42,8 @@ public class PivotBlockRetriever { private static final Logger LOG = LogManager.getLogger(); public static final int MAX_QUERY_RETRIES_PER_PEER = 3; - private static final int DEFAULT_MAX_PIVOT_BLOCK_RESETS = 5; + private static final int DEFAULT_MAX_PIVOT_BLOCK_RESETS = 50; + private static final int SUSPICIOUS_NUMBER_OF_RETRIES = 5; private final EthContext ethContext; private final MetricsSystem metricsSystem; @@ -148,6 +149,11 @@ private void handleContestedPivotBlock(final long contestedBlockNumber) { LOG.info("Received conflicting pivot blocks for {}.", contestedBlockNumber); final int retryCount = confirmationTasks.size(); + + if ((retryCount % SUSPICIOUS_NUMBER_OF_RETRIES) == 0) { + LOG.warn("{} attempts have failed to find a fast sync pivot block", retryCount); + } + if (retryCount > maxPivotBlockResets || pivotBlockNumber.get() <= BlockHeader.GENESIS_BLOCK_NUMBER) { LOG.info("Max retries reached, cancel pivot block download."); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java index 53b62689131..ee517bceb82 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloaderTest.java @@ -16,6 +16,7 @@ import static java.util.concurrent.CompletableFuture.completedFuture; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -34,6 +35,7 @@ import java.nio.file.Path; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; import org.assertj.core.api.Assertions; import org.junit.Test; @@ -358,6 +360,77 @@ public void shouldResetFastSyncStateAndRestartProcessIfWorldStateIsUnavailable() assertThat(result).isCompletedWithValue(secondDownloadPivotBlockHeaderState); } + @SuppressWarnings("unchecked") + @Test + public void shouldResetFastSyncStateAndRestartProcessIfANonFastSyncExceptionOccurs() { + final CompletableFuture firstWorldStateFuture = new CompletableFuture<>(); + final CompletableFuture secondWorldStateFuture = new CompletableFuture<>(); + final CompletableFuture chainFuture = new CompletableFuture<>(); + final ChainDownloader secondChainDownloader = mock(ChainDownloader.class); + final FastSyncState selectPivotBlockState = new FastSyncState(50); + final FastSyncState secondSelectPivotBlockState = new FastSyncState(90); + final BlockHeader pivotBlockHeader = new BlockHeaderTestFixture().number(50).buildHeader(); + final BlockHeader secondPivotBlockHeader = + new BlockHeaderTestFixture().number(90).buildHeader(); + final FastSyncState downloadPivotBlockHeaderState = new FastSyncState(pivotBlockHeader); + final FastSyncState secondDownloadPivotBlockHeaderState = + new FastSyncState(secondPivotBlockHeader); + // First attempt + when(fastSyncActions.waitForSuitablePeers(FastSyncState.EMPTY_SYNC_STATE)).thenReturn(COMPLETE); + when(fastSyncActions.selectPivotBlock(FastSyncState.EMPTY_SYNC_STATE)) + .thenReturn( + completedFuture(selectPivotBlockState), completedFuture(secondSelectPivotBlockState)); + when(fastSyncActions.downloadPivotBlockHeader(selectPivotBlockState)) + .thenReturn(completedFuture(downloadPivotBlockHeaderState)); + when(fastSyncActions.createChainDownloader(downloadPivotBlockHeaderState)) + .thenReturn(chainDownloader); + when(chainDownloader.start()).thenReturn(chainFuture); + when(worldStateDownloader.run(pivotBlockHeader)).thenReturn(firstWorldStateFuture); + when(fastSyncActions.scheduleFutureTask(any(), any())) + .thenAnswer(invocation -> ((Supplier) invocation.getArgument(0)).get()); + + // Second attempt + when(fastSyncActions.downloadPivotBlockHeader(secondSelectPivotBlockState)) + .thenReturn(completedFuture(secondDownloadPivotBlockHeaderState)); + + when(fastSyncActions.createChainDownloader(secondDownloadPivotBlockHeaderState)) + .thenReturn(secondChainDownloader); + when(secondChainDownloader.start()).thenReturn(completedFuture(null)); + when(worldStateDownloader.run(secondPivotBlockHeader)).thenReturn(secondWorldStateFuture); + + final CompletableFuture result = downloader.start(); + + verify(fastSyncActions).waitForSuitablePeers(FastSyncState.EMPTY_SYNC_STATE); + verify(fastSyncActions).selectPivotBlock(FastSyncState.EMPTY_SYNC_STATE); + verify(fastSyncActions).downloadPivotBlockHeader(selectPivotBlockState); + verify(storage).storeState(downloadPivotBlockHeaderState); + verify(fastSyncActions).createChainDownloader(downloadPivotBlockHeaderState); + verify(worldStateDownloader).run(pivotBlockHeader); + verifyNoMoreInteractions(fastSyncActions, worldStateDownloader, storage); + + assertThat(result).isNotDone(); + + firstWorldStateFuture.completeExceptionally(new RuntimeException("Test")); + + assertThat(result).isNotDone(); + verify(chainDownloader).cancel(); + // A real chain downloader would cause the chainFuture to complete when cancel is called. + chainFuture.completeExceptionally(new CancellationException()); + + verify(fastSyncActions).scheduleFutureTask(any(), any()); + verify(fastSyncActions, times(2)).waitForSuitablePeers(FastSyncState.EMPTY_SYNC_STATE); + verify(fastSyncActions, times(2)).selectPivotBlock(FastSyncState.EMPTY_SYNC_STATE); + verify(fastSyncActions).downloadPivotBlockHeader(secondSelectPivotBlockState); + verify(storage).storeState(secondDownloadPivotBlockHeaderState); + verify(fastSyncActions).createChainDownloader(secondDownloadPivotBlockHeaderState); + verify(worldStateDownloader).run(secondPivotBlockHeader); + verifyNoMoreInteractions(fastSyncActions, worldStateDownloader, storage); + + secondWorldStateFuture.complete(null); + + assertThat(result).isCompletedWithValue(secondDownloadPivotBlockHeaderState); + } + @Test public void shouldNotHaveTrailingPeerRequirementsBeforePivotBlockSelected() { when(fastSyncActions.waitForSuitablePeers(FastSyncState.EMPTY_SYNC_STATE))