Skip to content

Commit

Permalink
Richer return information for Precompiled Contracts (hyperledger#3546)
Browse files Browse the repository at this point in the history
Allow precompiled contracts to return richer information instead of
results/fail. System/precompile contracts can now revert, fail, refund
gas, etc. instead of just succeed or fail.

Signed-off-by: Danno Ferrin <[email protected]>
  • Loading branch information
shemnon authored Mar 9, 2022
1 parent de432ce commit ed54f09
Show file tree
Hide file tree
Showing 12 changed files with 198 additions and 80 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- new API methods: trace_rawTransaction, trace_get, trace_callMany
- added revertReason to trace APIs including: trace_transaction, trace_get, trace_call, trace_callMany, and trace_rawTransaction
- Allow mining beneficiary to transition at specific blocks for ibft2 and qbft consensus mechanisms. [#3115](https://github.com/hyperledger/besu/issues/3115)
- Return richer information from the PrecompiledContract interface. [\#3546](https://github.com/hyperledger/besu/pull/3546)

### Bug Fixes
- Reject locally-sourced transactions below the minimum gas price when not mining. [#3397](https://github.com/hyperledger/besu/pull/3397)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.precompile.PrecompiledContract;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.enclave.testutil.EnclaveKeyConfiguration;
Expand Down Expand Up @@ -203,8 +204,10 @@ public void testSendAndReceive() {

privacyPrecompiledContract.setPrivateTransactionProcessor(mockPrivateTxProcessor());

final Bytes actual =
privacyPrecompiledContract.compute(Bytes.fromBase64String(sr.getKey()), messageFrame);
final PrecompiledContract.PrecompileContractResult result =
privacyPrecompiledContract.computePrecompile(
Bytes.fromBase64String(sr.getKey()), messageFrame);
final Bytes actual = result.getOutput();

assertThat(actual).isEqualTo(Bytes.fromHexString(DEFAULT_OUTPUT));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ public boolean removePrivateTransactionObserver(final long observerId) {
}

@Override
public Bytes compute(final Bytes input, final MessageFrame messageFrame) {

public PrecompileContractResult computePrecompile(
final Bytes input, final MessageFrame messageFrame) {
if (skipContractExecution(messageFrame)) {
return Bytes.EMPTY;
return NO_RESULT;
}

final Hash pmtHash = messageFrame.getContextVariable(KEY_TRANSACTION_HASH);
Expand All @@ -114,7 +114,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
receiveResponse = getReceiveResponse(key);
} catch (final EnclaveClientException e) {
LOG.debug("Can not fetch private transaction payload with key {}", key, e);
return Bytes.EMPTY;
return NO_RESULT;
}

final BytesValueRLPInput bytesValueRLPInput =
Expand All @@ -127,12 +127,12 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {

final Bytes privateFrom = privateTransaction.getPrivateFrom();
if (!privateFromMatchesSenderKey(privateFrom, receiveResponse.getSenderKey())) {
return Bytes.EMPTY;
return NO_RESULT;
}

final Optional<Bytes> maybeGroupId = privateTransaction.getPrivacyGroupId();
if (maybeGroupId.isEmpty()) {
return Bytes.EMPTY;
return NO_RESULT;
}

final Bytes32 privacyGroupId = Bytes32.wrap(maybeGroupId.get());
Expand Down Expand Up @@ -168,7 +168,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
disposablePrivateState,
privateWorldStateUpdater,
privateFrom)) {
return Bytes.EMPTY;
return NO_RESULT;
}

final TransactionProcessingResult result =
Expand All @@ -183,7 +183,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {

privateMetadataUpdater.putTransactionReceipt(pmtHash, new PrivateTransactionReceipt(result));

return Bytes.EMPTY;
return NO_RESULT;
}

sendParticipantRemovedEvent(privateTransaction);
Expand All @@ -197,7 +197,8 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
pmtHash, privacyGroupId, disposablePrivateState, privateMetadataUpdater, result);
}

return result.getOutput();
return new PrecompileContractResult(
result.getOutput(), true, MessageFrame.State.CODE_EXECUTING, Optional.empty());
}

private void sendParticipantRemovedEvent(final PrivateTransaction privateTransaction) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ public PrivacyPluginPrecompiledContract(
}

@Override
public Bytes compute(final Bytes input, final MessageFrame messageFrame) {

public PrecompileContractResult computePrecompile(
final Bytes input, final MessageFrame messageFrame) {
if (skipContractExecution(messageFrame)) {
return Bytes.EMPTY;
return NO_RESULT;
}

final Optional<org.hyperledger.besu.plugin.data.PrivateTransaction> pluginPrivateTransaction =
Expand All @@ -58,7 +58,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
messageFrame.getContextVariable(PrivateStateUtils.KEY_TRANSACTION));

if (pluginPrivateTransaction.isEmpty()) {
return Bytes.EMPTY;
return NO_RESULT;
}

final PrivateTransaction privateTransaction =
Expand Down Expand Up @@ -101,7 +101,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {

privateMetadataUpdater.putTransactionReceipt(pmtHash, new PrivateTransactionReceipt(result));

return Bytes.EMPTY;
return NO_RESULT;
}

if (messageFrame.getContextVariable(PrivateStateUtils.KEY_IS_PERSISTING_PRIVATE_STATE, false)) {
Expand All @@ -113,6 +113,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
pmtHash, privacyGroupId, disposablePrivateState, privateMetadataUpdater, result);
}

return result.getOutput();
return new PrecompileContractResult(
result.getOutput(), true, MessageFrame.State.CODE_EXECUTING, Optional.empty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.hyperledger.besu.plugin.data.Hash;

import java.util.Base64;
import java.util.Optional;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
Expand All @@ -65,6 +66,10 @@ public class PrivacyPrecompiledContract extends AbstractPrecompiledContract {

private static final Logger LOG = LoggerFactory.getLogger(PrivacyPrecompiledContract.class);

static final PrecompileContractResult NO_RESULT =
new PrecompileContractResult(
Bytes.EMPTY, true, MessageFrame.State.CODE_EXECUTING, Optional.empty());

public PrivacyPrecompiledContract(
final GasCalculator gasCalculator,
final PrivacyParameters privacyParameters,
Expand Down Expand Up @@ -103,10 +108,11 @@ public Gas gasRequirement(final Bytes input) {
}

@Override
public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
public PrecompileContractResult computePrecompile(
final Bytes input, final MessageFrame messageFrame) {

if (skipContractExecution(messageFrame)) {
return Bytes.EMPTY;
return NO_RESULT;
}

final org.hyperledger.besu.plugin.data.Hash pmtHash =
Expand All @@ -118,7 +124,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
receiveResponse = getReceiveResponse(key);
} catch (final EnclaveClientException e) {
LOG.debug("Can not fetch private transaction payload with key {}", key, e);
return Bytes.EMPTY;
return NO_RESULT;
}

final BytesValueRLPInput bytesValueRLPInput =
Expand All @@ -129,7 +135,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {

final Bytes privateFrom = privateTransaction.getPrivateFrom();
if (!privateFromMatchesSenderKey(privateFrom, receiveResponse.getSenderKey())) {
return Bytes.EMPTY;
return NO_RESULT;
}

final Bytes32 privacyGroupId =
Expand All @@ -141,11 +147,11 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
.retrievePrivacyGroup(privacyGroupId.toBase64String())
.getMembers()
.contains(privateFrom.toBase64String())) {
return Bytes.EMPTY;
return NO_RESULT;
}
} catch (final EnclaveClientException e) {
// This exception is thrown when the privacy group can not be found
return Bytes.EMPTY;
return NO_RESULT;
} catch (final EnclaveServerException e) {
throw new IllegalStateException(
"Enclave is responding with an error, perhaps it has a misconfiguration?", e);
Expand Down Expand Up @@ -184,7 +190,7 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {

privateMetadataUpdater.putTransactionReceipt(pmtHash, new PrivateTransactionReceipt(result));

return Bytes.EMPTY;
return NO_RESULT;
}

if (messageFrame.getContextVariable(KEY_IS_PERSISTING_PRIVATE_STATE, false)) {
Expand All @@ -195,7 +201,8 @@ public Bytes compute(final Bytes input, final MessageFrame messageFrame) {
pmtHash, privacyGroupId, disposablePrivateState, privateMetadataUpdater, result);
}

return result.getOutput();
return new PrecompileContractResult(
result.getOutput(), true, MessageFrame.State.CODE_EXECUTING, Optional.empty());
}

protected void maybeApplyGenesisToPrivateWorldState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.precompile.PrecompiledContract;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;

Expand Down Expand Up @@ -166,7 +167,9 @@ public void testPayloadFoundInEnclave() {
.when(contractSpy)
.canExecute(any(), any(), any(), any(), any(), any(), any(), any());

final Bytes actual = contractSpy.compute(privateTransactionLookupId, messageFrame);
final PrecompiledContract.PrecompileContractResult result =
contractSpy.computePrecompile(privateTransactionLookupId, messageFrame);
final Bytes actual = result.getOutput();

assertThat(actual).isEqualTo(Bytes.fromHexString(DEFAULT_OUTPUT));
}
Expand All @@ -178,8 +181,11 @@ public void testPayloadNotFoundInEnclave() {

when(enclave.receive(any(String.class))).thenThrow(EnclaveClientException.class);

final Bytes expected = contract.compute(privateTransactionLookupId, messageFrame);
assertThat(expected).isEqualTo(Bytes.EMPTY);
final PrecompiledContract.PrecompileContractResult result =
contract.computePrecompile(privateTransactionLookupId, messageFrame);
final Bytes actual = result.getOutput();

assertThat(actual).isEqualTo(Bytes.EMPTY);
}

@Test(expected = RuntimeException.class)
Expand All @@ -189,7 +195,7 @@ public void testEnclaveDown() {

when(enclave.receive(any(String.class))).thenThrow(new RuntimeException());

contract.compute(privateTransactionLookupId, messageFrame);
contract.computePrecompile(privateTransactionLookupId, messageFrame);
}

@Test
Expand All @@ -205,7 +211,7 @@ public void testEnclaveBelowRequiredVersion() {
when(enclave.receive(eq(privateTransactionLookupId.toBase64String())))
.thenReturn(responseWithoutSenderKey);

assertThatThrownBy(() -> contract.compute(privateTransactionLookupId, messageFrame))
assertThatThrownBy(() -> contract.computePrecompile(privateTransactionLookupId, messageFrame))
.isInstanceOf(EnclaveConfigurationException.class)
.hasMessage("Incompatible Orion version. Orion version must be 1.6.0 or greater.");
}
Expand All @@ -224,8 +230,11 @@ public void testPayloadNotMatchingPrivateFrom() {
when(enclave.receive(eq(privateTransactionLookupId.toBase64String())))
.thenReturn(responseWithWrongSenderKey);

final Bytes expected = contract.compute(privateTransactionLookupId, messageFrame);
assertThat(expected).isEqualTo(Bytes.EMPTY);
final PrecompiledContract.PrecompileContractResult result =
contract.computePrecompile(privateTransactionLookupId, messageFrame);
final Bytes actual = result.getOutput();

assertThat(actual).isEqualTo(Bytes.EMPTY);
}

@Test
Expand Down Expand Up @@ -282,7 +291,9 @@ private void assertThatComputeReturnsEmptyGivenContractMembershipQueryReturns(
new ReceiveResponse(payload, PAYLOAD_TEST_PRIVACY_GROUP_ID, privateFrom);
when(enclave.receive(any(String.class))).thenReturn(response);

final Bytes actual = contractSpy.compute(privateTransactionLookupId, messageFrame);
final PrecompiledContract.PrecompileContractResult result =
contractSpy.computePrecompile(privateTransactionLookupId, messageFrame);
final Bytes actual = result.getOutput();

assertThat(actual).isEqualTo(Bytes.EMPTY);
}
Expand Down Expand Up @@ -319,7 +330,9 @@ public void testInvalidPrivateTransaction() {

when(enclave.receive(any(String.class))).thenReturn(response);

final Bytes actual = contractSpy.compute(privateTransactionLookupId, messageFrame);
final PrecompiledContract.PrecompileContractResult result =
contractSpy.computePrecompile(privateTransactionLookupId, messageFrame);
final Bytes actual = result.getOutput();

assertThat(actual).isEqualTo(Bytes.EMPTY);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.precompile.PrecompiledContract;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.data.PrivacyGenesis;
Expand Down Expand Up @@ -181,7 +182,9 @@ public void testPayloadFoundInPayloadOfMarker() {

when(messageFrame.getContextVariable(KEY_TRANSACTION)).thenReturn(transaction);

final Bytes actual = contract.compute(payload, messageFrame);
final PrecompiledContract.PrecompileContractResult result =
contract.computePrecompile(payload, messageFrame);
final Bytes actual = result.getOutput();

assertThat(actual).isEqualTo(Bytes.fromHexString(DEFAULT_OUTPUT));
}
Expand Down
Loading

0 comments on commit ed54f09

Please sign in to comment.