Skip to content

Commit

Permalink
Updated private transaction processing to generate private receipt fo…
Browse files Browse the repository at this point in the history
…r invalid transactions (#1241)

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

Signed-off-by: Mark Terry <mark.terry@consensys.net>
  • Loading branch information
mark-terry authored Jul 28, 2020
1 parent 80dc113 commit f9a01b8
Show file tree
Hide file tree
Showing 12 changed files with 185 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,17 @@ public Condition getEeaTransactionCount(
}

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));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ public BesuNode createIbft2Node(final String name) throws IOException {
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig())
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig(false))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(genesis::createIbft2GenesisConfig)
Expand Down Expand Up @@ -362,7 +362,7 @@ public BesuNode createIbft2NodeWithValidators(final String name, final String...
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig())
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig(false))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
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;
Expand Down Expand Up @@ -47,8 +48,10 @@ public JsonRpcConfiguration createJsonRpcWithCliqueEnabledConfig() {
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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,21 +85,29 @@ public PrivacyNode createPrivateTransactionEnabledNode(
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(
privacyAddress,
new BesuNodeConfigurationBuilder()
.name(name)
.miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig())
.jsonRpcConfiguration(node.createJsonRpcWithIbft2EnabledConfig(minerEnabled))
.webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false)
.genesisConfigProvider(genesis::createPrivacyIbft2GenesisConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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;

Expand All @@ -33,10 +34,12 @@

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);
}

Expand Down Expand Up @@ -81,6 +84,37 @@ public void createPrivateTransactionReceiptFailedTransaction() {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
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;
Expand Down Expand Up @@ -151,6 +152,11 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
"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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
"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;
}

Expand Down Expand Up @@ -206,21 +211,23 @@ void persistPrivateState(
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@
* 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;
Expand Down Expand Up @@ -64,12 +66,25 @@ public 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.
*
Expand All @@ -81,9 +96,7 @@ public void writeTo(final RLPOutput out) {
out.writeLongScalar(status);
out.writeList(logs, Log::writeTo);
out.writeBytes(output);
if (revertReason.isPresent()) {
out.writeBytes(revertReason.get());
}
revertReason.ifPresent(out::writeBytes);
out.endList();
}

Expand Down
Loading

0 comments on commit f9a01b8

Please sign in to comment.