Updated private transaction processing to generate private receipt for invalid transactions (#1241)

* Added private transaction receipt creation for invalid private transactions + tests.

Signed-off-by: Mark Terry <mark.terry@consensys.net>
pull/1270/head
mark-terry 4 years ago committed by GitHub
parent 80dc113a94
commit f9a01b897a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivConditions.java
  2. 4
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetExpectedFailedTransactionReceipt.java
  3. 38
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetExpectedInvalidTransactionReceipt.java
  4. 4
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/priv/PrivGetExpectedSuccessfulTransactionReceipt.java
  5. 4
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  6. 7
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/NodeConfigurationFactory.java
  7. 14
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/privacy/PrivacyNodeFactory.java
  8. 36
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/web3j/privacy/PrivacyReceiptAcceptanceTest.java
  9. 6
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/OnChainPrivacyPrecompiledContract.java
  10. 27
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContract.java
  11. 27
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateTransactionReceipt.java
  12. 46
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/precompiles/privacy/PrivacyPrecompiledContractTest.java

@ -94,12 +94,17 @@ public class PrivConditions {
}
public Condition getSuccessfulTransactionReceipt(final Hash transactionHash) {
return new PrivGetExecutedTransactionReceiptSuccess(
return new PrivGetExpectedSuccessfulTransactionReceipt(
transactions.getTransactionReceipt(transactionHash));
}
public Condition getFailedTransactionReceipt(final Hash transactionHash) {
return new PrivGetFailedTransactionReceiptSuccess(
return new PrivGetExpectedFailedTransactionReceipt(
transactions.getTransactionReceipt(transactionHash));
}
public Condition getInvalidTransactionReceipt(final Hash transactionHash) {
return new PrivGetExpectedInvalidTransactionReceipt(
transactions.getTransactionReceipt(transactionHash));
}

@ -21,11 +21,11 @@ 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 PrivGetFailedTransactionReceiptSuccess implements Condition {
public class PrivGetExpectedFailedTransactionReceipt implements Condition {
private final PrivGetTransactionReceiptTransaction getTransactionReceiptTransaction;
public PrivGetFailedTransactionReceiptSuccess(
public PrivGetExpectedFailedTransactionReceipt(
final PrivGetTransactionReceiptTransaction getTransactionReceiptTransaction) {
this.getTransactionReceiptTransaction = getTransactionReceiptTransaction;
}

@ -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 PrivGetExpectedInvalidTransactionReceipt implements Condition {
private final PrivGetTransactionReceiptTransaction getTransactionReceiptTransaction;
public PrivGetExpectedInvalidTransactionReceipt(
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("0x2");
}
}

@ -21,11 +21,11 @@ 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 PrivGetExecutedTransactionReceiptSuccess implements Condition {
public class PrivGetExpectedSuccessfulTransactionReceipt implements Condition {
private final PrivGetTransactionReceiptTransaction getTransactionReceiptTransaction;
public PrivGetExecutedTransactionReceiptSuccess(
public PrivGetExpectedSuccessfulTransactionReceipt(
final PrivGetTransactionReceiptTransaction getTransactionReceiptTransaction) {
this.getTransactionReceiptTransaction = getTransactionReceiptTransaction;
}

@ -303,7 +303,7 @@ public class BesuNodeFactory {
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig())
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig(false))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(genesis::createIbft2GenesisConfig)
@ -362,7 +362,7 @@ public class BesuNodeFactory {
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig())
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig(false))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(

@ -19,6 +19,7 @@ import static java.util.stream.Collectors.toList;
import static org.hyperledger.besu.consensus.clique.jsonrpc.CliqueRpcApis.CLIQUE;
import static org.hyperledger.besu.consensus.ibft.jsonrpc.IbftRpcApis.IBFT;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.ADMIN;
import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.MINER;
import org.hyperledger.besu.ethereum.api.jsonrpc.JsonRpcConfiguration;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcApi;
@ -47,8 +48,10 @@ public class NodeConfigurationFactory {
return createJsonRpcWithRpcApiEnabledConfig(CLIQUE);
}
public JsonRpcConfiguration createJsonRpcWithIbft2EnabledConfig() {
return createJsonRpcWithRpcApiEnabledConfig(IBFT);
public JsonRpcConfiguration createJsonRpcWithIbft2EnabledConfig(final boolean minerEnabled) {
return minerEnabled
? createJsonRpcWithRpcApiEnabledConfig(IBFT, MINER)
: createJsonRpcWithRpcApiEnabledConfig(IBFT);
}
public JsonRpcConfiguration createJsonRpcWithIbft2AdminEnabledConfig() {

@ -85,13 +85,21 @@ public class PrivacyNodeFactory {
privacyAccount.getEnclaveKeyPath(), privacyAccount.getEnclavePrivateKeyPath())));
}
public PrivacyNode createIbft2NodePrivacyMiningEnabled(
final String name, final PrivacyAccount privacyAccount) throws IOException {
return createIbft2NodePrivacyEnabled(name, privacyAccount, Address.PRIVACY, true);
}
public PrivacyNode createIbft2NodePrivacyEnabled(
final String name, final PrivacyAccount privacyAccount) throws IOException {
return createIbft2NodePrivacyEnabled(name, privacyAccount, Address.PRIVACY);
return createIbft2NodePrivacyEnabled(name, privacyAccount, Address.PRIVACY, false);
}
public PrivacyNode createIbft2NodePrivacyEnabled(
final String name, final PrivacyAccount privacyAccount, final int privacyAddress)
final String name,
final PrivacyAccount privacyAccount,
final int privacyAddress,
final boolean minerEnabled)
throws IOException {
return create(
new PrivacyNodeConfiguration(
@ -99,7 +107,7 @@ public class PrivacyNodeFactory {
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig())
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig(minerEnabled))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(genesis::createPrivacyIbft2GenesisConfig)

@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyAcceptanceTestBase;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.PrivacyNode;
import org.hyperledger.besu.tests.acceptance.dsl.privacy.transaction.CreatePrivacyGroupTransaction;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerTransactions;
import java.util.Optional;
@ -33,10 +34,12 @@ import org.junit.Test;
public class PrivacyReceiptAcceptanceTest extends PrivacyAcceptanceTestBase {
private PrivacyNode alice;
final MinerTransactions minerTransactions = new MinerTransactions();
@Before
public void setUp() throws Exception {
alice = privacyBesu.createIbft2NodePrivacyEnabled("node1", privacyAccountResolver.resolve(0));
alice =
privacyBesu.createIbft2NodePrivacyMiningEnabled("node1", privacyAccountResolver.resolve(0));
privacyCluster.start(alice);
}
@ -81,6 +84,37 @@ public class PrivacyReceiptAcceptanceTest extends PrivacyAcceptanceTestBase {
alice.getBesu().verify(priv.getFailedTransactionReceipt(transactionHash));
}
@Test
public void createPrivateTransactionReceiptInvalidTransaction() {
final CreatePrivacyGroupTransaction onlyAlice =
privacyTransactions.createPrivacyGroup("Only Alice", "", alice);
final String privacyGroupId = alice.execute(onlyAlice);
final PrivateTransaction validTransaction =
createSignedTransaction(alice, privacyGroupId, empty());
final BytesValueRLPOutput rlpOutput = getRLPOutput(validTransaction);
// Stop mining, to allow adding duplicate nonce block
alice.getBesu().execute(minerTransactions.minerStop());
final Hash transactionHash1 =
alice.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
final Hash transactionHash2 =
alice.execute(privacyTransactions.sendRawTransaction(rlpOutput.encoded().toHexString()));
// Start mining again
alice.getBesu().execute(minerTransactions.minerStart());
// Successful PMTs
alice.getBesu().verify(eth.expectSuccessfulTransactionReceipt(transactionHash1.toString()));
alice.getBesu().verify(eth.expectSuccessfulTransactionReceipt(transactionHash2.toString()));
// Successful first private transaction
alice.getBesu().verify(priv.getSuccessfulTransactionReceipt(transactionHash1));
// Invalid second private transaction
alice.getBesu().verify(priv.getInvalidTransactionReceipt(transactionHash2));
}
private BytesValueRLPOutput getRLPOutput(final PrivateTransaction privateTransaction) {
final BytesValueRLPOutput bvrlpo = new BytesValueRLPOutput();
privateTransaction.writeTo(bvrlpo);

@ -36,6 +36,7 @@ import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
import org.hyperledger.besu.ethereum.privacy.Restriction;
import org.hyperledger.besu.ethereum.privacy.VersionedPrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.group.OnChainGroupManagement;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateStateStorage;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.vm.DebugOperationTracer;
import org.hyperledger.besu.ethereum.vm.GasCalculator;
@ -151,6 +152,11 @@ public class OnChainPrivacyPrecompiledContract extends PrivacyPrecompiledContrac
"Failed to process private transaction {}: {}",
pmtHash,
result.getValidationResult().getErrorMessage());
final PrivateStateStorage.Updater privateStateUpdater = privateStateStorage.updater();
storeTransactionReceipt(pmtHash, currentBlockHash, result, privateStateUpdater);
privateStateUpdater.commit();
return Bytes.EMPTY;
}

@ -163,6 +163,11 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
"Failed to process private transaction {}: {}",
pmtHash,
result.getValidationResult().getErrorMessage());
final PrivateStateStorage.Updater privateStateUpdater = privateStateStorage.updater();
storeTransactionReceipt(pmtHash, currentBlockHash, result, privateStateUpdater);
privateStateUpdater.commit();
return Bytes.EMPTY;
}
@ -206,21 +211,23 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {
disposablePrivateState.rootHash(),
privateStateUpdater);
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, commitmentHash, privateTransactionReceipt);
maybeUpdateGroupHeadBlockMap(privacyGroupId, currentBlockHash, privateStateUpdater);
storeTransactionReceipt(commitmentHash, currentBlockHash, result, privateStateUpdater);
privateStateUpdater.commit();
}
void storeTransactionReceipt(
final Hash pmtHash,
final Hash currentBlockHash,
final PrivateTransactionProcessor.Result result,
final PrivateStateStorage.Updater privateStateUpdater) {
final PrivateTransactionReceipt privateTransactionReceipt =
new PrivateTransactionReceipt(result);
privateStateUpdater.putTransactionReceipt(currentBlockHash, pmtHash, privateTransactionReceipt);
}
void maybeUpdateGroupHeadBlockMap(
final Bytes32 privacyGroupId,
final Hash currentBlockHash,

@ -32,11 +32,13 @@ import org.apache.tuweni.bytes.Bytes;
* execution.
*/
public class PrivateTransactionReceipt {
@SuppressWarnings("unchecked")
public static final PrivateTransactionReceipt FAILED =
new PrivateTransactionReceipt(
0, Collections.EMPTY_LIST, Bytes.EMPTY, Optional.ofNullable(null));
new PrivateTransactionReceipt(0, Collections.EMPTY_LIST, Bytes.EMPTY, Optional.empty());
private static final int STATUS_FAILED = 0;
private static final int STATUS_SUCCESSFUL = 1;
private static final int STATUS_INVALID = 2;
private final int status;
private final List<Log> logs;
@ -64,12 +66,25 @@ public class PrivateTransactionReceipt {
public PrivateTransactionReceipt(final TransactionProcessor.Result result) {
this(
result.getStatus() == PrivateTransactionProcessor.Result.Status.SUCCESSFUL ? 1 : 0,
getStatusCode(result.getStatus()),
result.getLogs(),
result.getOutput(),
result.getRevertReason());
}
private static int getStatusCode(final TransactionProcessor.Result.Status result) {
switch (result) {
case SUCCESSFUL:
return STATUS_SUCCESSFUL;
case INVALID:
return STATUS_INVALID;
case FAILED:
return STATUS_FAILED;
default:
throw new IllegalStateException("Unexpected private transaction status.");
}
}
/**
* Write an RLP representation.
*
@ -81,9 +96,7 @@ public class PrivateTransactionReceipt {
out.writeLongScalar(status);
out.writeList(logs, Log::writeTo);
out.writeBytes(output);
if (revertReason.isPresent()) {
out.writeBytes(revertReason.get());
}
revertReason.ifPresent(out::writeBytes);
out.endList();
}

@ -39,6 +39,8 @@ 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.mainnet.TransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver;
import org.hyperledger.besu.ethereum.privacy.PrivateTransaction;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionProcessor;
@ -76,13 +78,10 @@ public class PrivacyPrecompiledContractTest {
final PrivateStateRootResolver privateStateRootResolver =
new PrivateStateRootResolver(privateStateStorage);
private PrivateTransactionProcessor mockPrivateTxProcessor() {
private PrivateTransactionProcessor mockPrivateTxProcessor(
final PrivateTransactionProcessor.Result result) {
final PrivateTransactionProcessor mockPrivateTransactionProcessor =
mock(PrivateTransactionProcessor.class);
final List<Log> logs = new ArrayList<>();
final PrivateTransactionProcessor.Result result =
PrivateTransactionProcessor.Result.successful(
logs, 0, 0, Bytes.fromHexString(DEFAULT_OUTPUT), null);
when(mockPrivateTransactionProcessor.processTransaction(
nullable(Blockchain.class),
nullable(WorldUpdater.class),
@ -138,7 +137,11 @@ public class PrivacyPrecompiledContractTest {
public void testPayloadFoundInEnclave() {
final Enclave enclave = mock(Enclave.class);
final PrivacyPrecompiledContract contract = buildPrivacyPrecompiledContract(enclave);
contract.setPrivateTransactionProcessor(mockPrivateTxProcessor());
final List<Log> logs = new ArrayList<>();
contract.setPrivateTransactionProcessor(
mockPrivateTxProcessor(
PrivateTransactionProcessor.Result.successful(
logs, 0, 0, Bytes.fromHexString(DEFAULT_OUTPUT), null)));
final PrivateTransaction privateTransaction = privateTransactionBesu();
byte[] payload = convertPrivateTransactionToBytes(privateTransaction);
@ -223,6 +226,37 @@ public class PrivacyPrecompiledContractTest {
assertThat(expected).isEqualTo(Bytes.EMPTY);
}
@Test
public void testInvalidPrivateTransaction() {
final Enclave enclave = mock(Enclave.class);
final PrivacyPrecompiledContract contract =
new PrivacyPrecompiledContract(
new SpuriousDragonGasCalculator(),
enclave,
worldStateArchive,
privateStateStorage,
privateStateRootResolver);
contract.setPrivateTransactionProcessor(
mockPrivateTxProcessor(
PrivateTransactionProcessor.Result.invalid(
ValidationResult.invalid(
TransactionValidator.TransactionInvalidReason.INCORRECT_NONCE))));
final PrivateTransaction privateTransaction = privateTransactionBesu();
final byte[] payload = convertPrivateTransactionToBytes(privateTransaction);
final String privateFrom = privateTransaction.getPrivateFrom().toBase64String();
final ReceiveResponse response =
new ReceiveResponse(payload, PAYLOAD_TEST_PRIVACY_GROUP_ID, privateFrom);
when(enclave.receive(any(String.class))).thenReturn(response);
final Bytes actual = contract.compute(txEnclaveKey, messageFrame);
assertThat(actual).isEqualTo(Bytes.EMPTY);
}
private byte[] convertPrivateTransactionToBytes(final PrivateTransaction privateTransaction) {
final BytesValueRLPOutput bytesValueRLPOutput = new BytesValueRLPOutput();
privateTransaction.writeTo(bytesValueRLPOutput);

Loading…
Cancel
Save