Remove EIP-3074 code (#7208)

Remove all EIP-3074 code from Besu. Since EIP-3074 has been replaced
with EIP-7702 in Pectra, and there is no intent to schedule it for a
future fork there is no need to retain the code.

Signed-off-by: Danno Ferrin <danno@numisight.com>
revert-7203-patch-1
Danno Ferrin 6 months ago committed by GitHub
parent 8a8f1ce712
commit 365737c2eb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  2. 25
      evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java
  3. 43
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java
  4. 58
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java
  5. 102
      evm/src/main/java/org/hyperledger/besu/evm/operation/AuthCallOperation.java
  6. 118
      evm/src/main/java/org/hyperledger/besu/evm/operation/AuthOperation.java
  7. 56
      evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java
  8. 153
      evm/src/test/java/org/hyperledger/besu/evm/operations/AuthOperationTest.java
  9. 152
      evm/src/test/java/org/hyperledger/besu/evm/processor/AuthCallProcessorTest.java

@ -33,8 +33,6 @@ import org.hyperledger.besu.evm.operation.AddModOperation;
import org.hyperledger.besu.evm.operation.AddOperation;
import org.hyperledger.besu.evm.operation.AddressOperation;
import org.hyperledger.besu.evm.operation.AndOperation;
import org.hyperledger.besu.evm.operation.AuthCallOperation;
import org.hyperledger.besu.evm.operation.AuthOperation;
import org.hyperledger.besu.evm.operation.BalanceOperation;
import org.hyperledger.besu.evm.operation.BaseFeeOperation;
import org.hyperledger.besu.evm.operation.BlobBaseFeeOperation;
@ -951,9 +949,7 @@ public class MainnetEVMs {
final BigInteger chainID) {
registerCancunOperations(registry, gasCalculator, chainID);
// EIP-3074 AUTH and AUTHCALL
registry.put(new AuthOperation(gasCalculator));
registry.put(new AuthCallOperation(gasCalculator));
// TODO add EOF operations here once PragueEOF is collapsed into Prague
}
/**

@ -246,9 +246,6 @@ public class MessageFrame {
/** The mark of the undoable collections at the creation of this message frame */
private final long undoMark;
/** mutated by AUTH operation */
private Address authorizedBy = null;
/**
* Builder builder.
*
@ -1373,24 +1370,6 @@ public class MessageFrame {
return txValues.versionedHashes();
}
/**
* Accessor for address that authorized future AUTHCALLs.
*
* @return the revert reason
*/
public Address getAuthorizedBy() {
return authorizedBy;
}
/**
* Mutator for address that authorizes future AUTHCALLs, set by AUTH opcode
*
* @param authorizedBy the address that authorizes future AUTHCALLs
*/
public void setAuthorizedBy(final Address authorizedBy) {
this.authorizedBy = authorizedBy;
}
/** Reset. */
public void reset() {
maybeUpdatedMemory = Optional.empty();
@ -1428,7 +1407,9 @@ public class MessageFrame {
private Optional<List<VersionedHash>> versionedHashes = Optional.empty();
/** Instantiates a new Builder. */
public Builder() {}
public Builder() {
// constructor added to deal with JavaDoc linting rules.
}
/**
* The "parent" message frame. When present some fields will be populated from the parent and

@ -210,35 +210,6 @@ public interface GasCalculator {
Address contract,
boolean accountIsWarm);
/**
* Returns the gas cost for AUTHCALL.
*
* @param frame The current frame
* @param stipend The gas stipend being provided by the CALL caller
* @param inputDataOffset The offset in memory to retrieve the CALL input data
* @param inputDataLength The CALL input data length
* @param outputDataOffset The offset in memory to place the CALL output data
* @param outputDataLength The CALL output data length
* @param transferValue The wei being transferred
* @param invoker The contract calling out on behalf of the authority
* @param invokee The address of the recipient (never null)
* @param accountIsWarm The address of the contract is "warm" as per EIP-2929
* @return The gas cost for the CALL operation
*/
default long authCallOperationGasCost(
final MessageFrame frame,
final long stipend,
final long inputDataOffset,
final long inputDataLength,
final long outputDataOffset,
final long outputDataLength,
final Wei transferValue,
final Account invoker,
final Address invokee,
final boolean accountIsWarm) {
return 0L;
}
/**
* Gets additional call stipend.
*
@ -646,18 +617,4 @@ public interface GasCalculator {
default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) {
return 0L;
}
/**
* Returns the gas cost of validating an auth commitment for an AUTHCALL
*
* @param frame the current frame, with memory to be read from
* @param offset start of memory read
* @param length amount of memory read
* @param authority address to check for warmup
* @return total gas cost for the operation
*/
default long authOperationGasCost(
final MessageFrame frame, final long offset, final long length, final Address authority) {
return 0L;
}
}

@ -16,11 +16,6 @@ package org.hyperledger.besu.evm.gascalculator;
import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;
/**
* Gas Calculator for Prague
*
@ -32,8 +27,6 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
* </UL>
*/
public class PragueGasCalculator extends CancunGasCalculator {
private static final int AUTH_OP_FIXED_FEE = 3100;
private static final long AUTH_CALL_VALUE_TRANSFER_GAS_COST = 6700;
/** Instantiates a new Prague Gas Calculator. */
public PragueGasCalculator() {
@ -48,55 +41,4 @@ public class PragueGasCalculator extends CancunGasCalculator {
protected PragueGasCalculator(final int maxPrecompile) {
super(maxPrecompile);
}
@Override
public long authOperationGasCost(
final MessageFrame frame, final long offset, final long length, final Address authority) {
final long memoryExpansionGasCost = memoryExpansionGasCost(frame, offset, length);
final long accessFee = frame.isAddressWarm(authority) ? 100 : 2600;
final long gasCost = AUTH_OP_FIXED_FEE + memoryExpansionGasCost + accessFee;
return gasCost;
}
/**
* Returns the gas cost to call another contract on behalf of an authority
*
* @return the gas cost to call another contract on behalf of an authority
*/
@Override
public long authCallOperationGasCost(
final MessageFrame frame,
final long stipend,
final long inputDataOffset,
final long inputDataLength,
final long outputDataOffset,
final long outputDataLength,
final Wei transferValue,
final Account invoker,
final Address invokee,
final boolean accountIsWarm) {
final long inputDataMemoryExpansionCost =
memoryExpansionGasCost(frame, inputDataOffset, inputDataLength);
final long outputDataMemoryExpansionCost =
memoryExpansionGasCost(frame, outputDataOffset, outputDataLength);
final long memoryExpansionCost =
Math.max(inputDataMemoryExpansionCost, outputDataMemoryExpansionCost);
final long staticGasCost = getWarmStorageReadCost();
long dynamicGasCost = accountIsWarm ? 0 : getColdAccountAccessCost() - getWarmStorageReadCost();
if (!transferValue.isZero()) {
dynamicGasCost += AUTH_CALL_VALUE_TRANSFER_GAS_COST;
}
if ((invoker == null || invoker.isEmpty()) && !transferValue.isZero()) {
dynamicGasCost += newAccountGasCost();
}
long cost = staticGasCost + memoryExpansionCost + dynamicGasCost;
return cost;
}
}

@ -1,102 +0,0 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.evm.operation;
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import org.apache.tuweni.bytes.Bytes32;
/** Introduced via EIP-3074 to call another contract with a different authorization context. */
public class AuthCallOperation extends AbstractCallOperation {
/**
* Instantiates a new AuthCallOperation.
*
* @param gasCalculator a Prague or later gas calculator
*/
public AuthCallOperation(final GasCalculator gasCalculator) {
super(0xF7, "AUTHCALL", 7, 1, gasCalculator);
}
@Override
protected Address to(final MessageFrame frame) {
return Words.toAddress(frame.getStackItem(1));
}
@Override
protected Wei value(final MessageFrame frame) {
return Wei.wrap(frame.getStackItem(2));
}
@Override
protected Wei apparentValue(final MessageFrame frame) {
return value(frame);
}
@Override
protected long inputDataOffset(final MessageFrame frame) {
return clampedToLong(frame.getStackItem(3));
}
@Override
protected long inputDataLength(final MessageFrame frame) {
return clampedToLong(frame.getStackItem(4));
}
@Override
protected long outputDataOffset(final MessageFrame frame) {
return clampedToLong(frame.getStackItem(5));
}
@Override
protected long outputDataLength(final MessageFrame frame) {
return clampedToLong(frame.getStackItem(6));
}
@Override
protected Address address(final MessageFrame frame) {
return to(frame);
}
@Override
protected Address sender(final MessageFrame frame) {
return frame.getAuthorizedBy();
}
@Override
public long gasAvailableForChildCall(final MessageFrame frame) {
return gasCalculator().gasAvailableForChildCall(frame, gas(frame), !value(frame).isZero());
}
@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
if (frame.isStatic() && !value(frame).isZero()) {
return new OperationResult(cost(frame, true), ExceptionalHaltReason.ILLEGAL_STATE_CHANGE);
} else if (frame.getAuthorizedBy() != null) {
return super.execute(frame, evm);
} else {
frame.pushStackItem(Bytes32.ZERO);
return new OperationResult(cost(frame, true), null);
}
}
}

@ -1,118 +0,0 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.evm.operation;
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.crypto.Hash;
import org.hyperledger.besu.crypto.SECPPublicKey;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** The AUTH operation. */
public class AuthOperation extends AbstractOperation {
/** The constant MAGIC defined by EIP-3074 */
public static final byte MAGIC = 0x4;
private static final Logger LOG = LoggerFactory.getLogger(AuthOperation.class);
private static final SignatureAlgorithm signatureAlgorithm =
SignatureAlgorithmFactory.getInstance();
/**
* Instantiates a new AuthOperation.
*
* @param gasCalculator a Prague or later gas calculator
*/
public AuthOperation(final GasCalculator gasCalculator) {
super(0xF6, "AUTH", 3, 1, gasCalculator);
}
@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
// create authority from stack
Address authority = Words.toAddress(frame.getStackItem(0));
long offset = clampedToLong(frame.getStackItem(1));
long length = clampedToLong(frame.getStackItem(2));
final long gasCost =
super.gasCalculator().authOperationGasCost(frame, offset, length, authority);
if (frame.getRemainingGas() < gasCost) {
return new OperationResult(gasCost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
byte yParity = frame.readMemory(offset, 1).get(0);
Bytes32 r = Bytes32.wrap(frame.readMemory(offset + 1, 32));
Bytes32 s = Bytes32.wrap(frame.readMemory(offset + 33, 32));
Bytes32 commit = Bytes32.wrap(frame.readMemory(offset + 65, 32));
Bytes32 invoker = Bytes32.leftPad(frame.getContractAddress());
// TODO add test for getting sender nonce when account does not exist
Bytes32 senderNonce =
Bytes32.leftPad(
Bytes.ofUnsignedLong(
Optional.ofNullable(frame.getWorldUpdater().getAccount(authority))
.map(Account::getNonce)
.orElse(0L)));
if (evm.getChainId().isEmpty()) {
frame.pushStackItem(UInt256.ZERO);
LOG.error("ChainId is not set");
return new OperationResult(0, null);
}
Bytes authPreImage =
Bytes.concatenate(
Bytes.ofUnsignedShort(MAGIC), evm.getChainId().get(), senderNonce, invoker, commit);
Bytes32 messageHash = Hash.keccak256(authPreImage);
Optional<SECPPublicKey> publicKey;
try {
SECPSignature signature =
signatureAlgorithm.createSignature(
r.toUnsignedBigInteger(), s.toUnsignedBigInteger(), yParity);
publicKey = signatureAlgorithm.recoverPublicKeyFromSignature(messageHash, signature);
} catch (IllegalArgumentException e) {
frame.pushStackItem(UInt256.ZERO);
return new OperationResult(gasCost, null);
}
if (publicKey.isPresent()) {
Address signerAddress = Address.extract(publicKey.get());
if (signerAddress.equals(authority)) {
frame.setAuthorizedBy(authority);
frame.pushStackItem(UInt256.ONE);
} else {
frame.pushStackItem(UInt256.ZERO);
}
} else {
frame.pushStackItem(UInt256.ZERO);
}
return new OperationResult(gasCost, null);
}
}

@ -14,61 +14,17 @@
*/
package org.hyperledger.besu.evm.gascalculator;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.junit.jupiter.api.Test;
public class PragueGasCalculatorTest {
class PragueGasCalculatorTest {
@Test
public void testAuthOperationGasCost() {
PragueGasCalculator pragueGasCalculator = new PragueGasCalculator();
MessageFrame runningIn = mock(MessageFrame.class);
Address authority = Address.fromHexString("0xdeadbeef");
when(runningIn.isAddressWarm(authority)).thenReturn(true);
long gasSpent = pragueGasCalculator.authOperationGasCost(runningIn, 0, 97, authority);
assertEquals(
3100 + 100 + pragueGasCalculator.memoryExpansionGasCost(runningIn, 0, 97), gasSpent);
}
@Test
public void testAuthCallOperationGasCostWithTransfer() {
PragueGasCalculator pragueGasCalculator = new PragueGasCalculator();
MessageFrame runningIn = mock(MessageFrame.class);
Account invoker = mock(MutableAccount.class);
when(invoker.getAddress()).thenReturn(Address.fromHexString("0xCafeBabe"));
Address invokee = Address.fromHexString("0xdeadbeef");
when(runningIn.isAddressWarm(invokee)).thenReturn(true);
long gasSpentInAuthCall =
pragueGasCalculator.authCallOperationGasCost(
runningIn, 63, 0, 97, 100, 97, Wei.ONE, invoker, invokee, true);
long gasSpentInCall =
pragueGasCalculator.callOperationGasCost(
runningIn, 63, 0, 97, 100, 97, Wei.ONE, invoker, invokee, true);
assertEquals(gasSpentInCall - 2300, gasSpentInAuthCall);
}
@Test
public void testAuthCallOperationGasCostNoTransfer() {
PragueGasCalculator pragueGasCalculator = new PragueGasCalculator();
MessageFrame runningIn = mock(MessageFrame.class);
Account invoker = mock(MutableAccount.class);
when(invoker.getAddress()).thenReturn(Address.fromHexString("0xCafeBabe"));
Address invokee = Address.fromHexString("0xdeadbeef");
when(runningIn.isAddressWarm(invokee)).thenReturn(true);
long gasSpentInAuthCall =
pragueGasCalculator.authCallOperationGasCost(
runningIn, 63, 0, 97, 100, 97, Wei.ZERO, invoker, invokee, true);
long gasSpentInCall =
pragueGasCalculator.callOperationGasCost(
runningIn, 63, 0, 97, 100, 97, Wei.ZERO, invoker, invokee, true);
assertEquals(gasSpentInCall, gasSpentInAuthCall);
void testPrecompileSize() {
PragueGasCalculator subject = new PragueGasCalculator();
assertThat(subject.isPrecompile(Address.precompiled(0x14))).isFalse();
assertThat(subject.isPrecompile(Address.BLS12_MAP_FP2_TO_G2)).isTrue();
}
}

@ -1,153 +0,0 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.evm.operations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.Hash;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import org.hyperledger.besu.evm.operation.AuthOperation;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.Test;
public class AuthOperationTest {
@Test
public void testAuthOperation() {
SignatureAlgorithm algo = SignatureAlgorithmFactory.getInstance();
KeyPair keys = algo.generateKeyPair();
Address authingAddress = Address.extract(keys.getPublicKey());
EVM fakeEVM = mock(EVM.class);
Optional<Bytes> chainId = Optional.of(Bytes.of(1));
when(fakeEVM.getChainId()).thenReturn(chainId);
long senderNonce = 0;
Address invokerAddress = Address.fromHexString("0xdeadbeef");
Bytes32 invoker = Bytes32.leftPad(invokerAddress);
Bytes32 contractCommitment = Bytes32.leftPad(Bytes.fromHexString("0x1234"));
Bytes authPreImage =
Bytes.concatenate(
Bytes.ofUnsignedShort(AuthOperation.MAGIC),
chainId.get(),
Bytes32.leftPad(Bytes.ofUnsignedLong(senderNonce)),
invoker,
contractCommitment);
Bytes32 messageHash = Hash.keccak256(authPreImage);
SECPSignature signature = algo.sign(messageHash, keys);
MessageFrame frame = mock(MessageFrame.class);
when(frame.getContractAddress()).thenReturn(invokerAddress);
MutableAccount authingAccount = mock(MutableAccount.class);
when(authingAccount.getAddress()).thenReturn(authingAddress);
when(authingAccount.getNonce()).thenReturn(senderNonce);
when(frame.getRemainingGas()).thenReturn(1000000L);
WorldUpdater state = mock(WorldUpdater.class);
when(state.getAccount(authingAddress)).thenReturn(authingAccount);
when(frame.getWorldUpdater()).thenReturn(state);
when(frame.getSenderAddress()).thenReturn(authingAddress);
when(state.getSenderAccount(frame)).thenReturn(authingAccount);
when(frame.getStackItem(0)).thenReturn(authingAddress);
when(frame.getStackItem(1)).thenReturn(Bytes.of(0));
when(frame.getStackItem(2)).thenReturn(Bytes.of(97));
Bytes encodedSignature = signature.encodedBytes();
when(frame.readMemory(0, 1)).thenReturn(encodedSignature.slice(64, 1));
when(frame.readMemory(1, 32)).thenReturn(Bytes32.wrap(encodedSignature.slice(0, 32).toArray()));
when(frame.readMemory(33, 32))
.thenReturn(Bytes32.wrap(encodedSignature.slice(32, 32).toArray()));
when(frame.readMemory(65, 32)).thenReturn(contractCommitment);
AuthOperation authOperation = new AuthOperation(new PragueGasCalculator());
authOperation.execute(frame, fakeEVM);
verify(frame).setAuthorizedBy(authingAddress);
verify(frame).pushStackItem(UInt256.ONE);
}
@Test
public void testAuthOperationNegative() {
SignatureAlgorithm algo = SignatureAlgorithmFactory.getInstance();
KeyPair keys = algo.generateKeyPair();
Address authingAddress = Address.extract(keys.getPublicKey());
EVM fakeEVM = mock(EVM.class);
Optional<Bytes> chainId = Optional.of(Bytes.of(1));
when(fakeEVM.getChainId()).thenReturn(chainId);
long senderNonce = 0;
Address invokerAddress = Address.fromHexString("0xdeadbeef");
Bytes32 invoker = Bytes32.leftPad(invokerAddress);
Bytes32 contractCommitment = Bytes32.leftPad(Bytes.fromHexString("0x1234"));
Bytes authPreImage =
Bytes.concatenate(
Bytes.ofUnsignedShort(AuthOperation.MAGIC),
chainId.get(),
Bytes32.leftPad(Bytes.ofUnsignedLong(senderNonce)),
invoker,
contractCommitment);
Bytes32 messageHash = Hash.keccak256(authPreImage);
// Generate a new key pair to create an incorrect signature
KeyPair wrongKeys = algo.generateKeyPair();
SECPSignature wrongSignature = algo.sign(messageHash, wrongKeys);
MessageFrame frame = mock(MessageFrame.class);
when(frame.getRemainingGas()).thenReturn(1000000L);
when(frame.getContractAddress()).thenReturn(invokerAddress);
MutableAccount authingAccount = mock(MutableAccount.class);
when(authingAccount.getAddress()).thenReturn(authingAddress);
when(authingAccount.getNonce()).thenReturn(senderNonce);
WorldUpdater state = mock(WorldUpdater.class);
when(state.getAccount(authingAddress)).thenReturn(authingAccount);
when(frame.getWorldUpdater()).thenReturn(state);
when(frame.getSenderAddress()).thenReturn(authingAddress);
when(state.getSenderAccount(frame)).thenReturn(authingAccount);
when(frame.getStackItem(0)).thenReturn(authingAddress);
when(frame.getStackItem(1)).thenReturn(Bytes.of(0));
when(frame.getStackItem(2)).thenReturn(Bytes.of(97));
Bytes encodedSignature = wrongSignature.encodedBytes(); // Use the wrong signature
when(frame.readMemory(0, 1)).thenReturn(encodedSignature.slice(64, 1));
when(frame.readMemory(1, 32)).thenReturn(Bytes32.wrap(encodedSignature.slice(0, 32).toArray()));
when(frame.readMemory(33, 32))
.thenReturn(Bytes32.wrap(encodedSignature.slice(32, 32).toArray()));
when(frame.readMemory(65, 32)).thenReturn(contractCommitment);
AuthOperation authOperation = new AuthOperation(new PragueGasCalculator());
authOperation.execute(frame, fakeEVM);
verify(frame, never()).setAuthorizedBy(authingAddress); // The address should not be authorized
verify(frame).pushStackItem(UInt256.ZERO); // The stack should contain UInt256.ZERO
}
}

@ -1,152 +0,0 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* 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.evm.processor;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import org.hyperledger.besu.crypto.Hash;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.fluent.EVMExecutor;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.operation.AuthOperation;
import org.hyperledger.besu.evm.toy.ToyWorld;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.math.BigInteger;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
public class AuthCallProcessorTest extends MessageCallProcessorTest {
MessageCallProcessor spyingMessageCallProcessor;
ArgumentCaptor<MessageFrame> frameCaptor = ArgumentCaptor.forClass(MessageFrame.class);
WorldUpdater toyWorld = new ToyWorld();
@Test
public void authCallHappyPath() {
final EVM pragueEVM =
MainnetEVMs.prague(new PragueGasCalculator(), BigInteger.ONE, EvmConfiguration.DEFAULT);
final EVMExecutor executor = EVMExecutor.evm(pragueEVM);
this.spyingMessageCallProcessor =
spy(new MessageCallProcessor(pragueEVM, precompileContractRegistry));
executor.messageCallProcessor(this.spyingMessageCallProcessor);
executor.worldUpdater(toyWorld);
executor.gas(10_000_000_000L);
SignatureAlgorithm algo = SignatureAlgorithmFactory.getInstance();
KeyPair keys = algo.generateKeyPair();
Optional<Bytes> chainId = Optional.of(Bytes.of(1));
long senderNonce = 0;
Address invokerAddress = Address.fromHexString("0xdeadbeef");
Bytes32 invoker = Bytes32.leftPad(invokerAddress);
Bytes32 contractCommitment = Bytes32.leftPad(Bytes.fromHexString("0x1234"));
Bytes authPreImage =
Bytes.concatenate(
Bytes.ofUnsignedShort(AuthOperation.MAGIC),
Bytes32.leftPad(chainId.get()),
Bytes32.leftPad(Bytes.ofUnsignedLong(senderNonce)),
invoker,
contractCommitment);
Bytes32 messageHash = Hash.keccak256(authPreImage);
SECPSignature signature = algo.sign(messageHash, keys);
Bytes encodedSignature = signature.encodedBytes();
Bytes authParam =
Bytes.concatenate(
encodedSignature.slice(64, 1), // y parity
encodedSignature.slice(0, 32), // r
encodedSignature.slice(32, 32), // s
contractCommitment);
toyWorld.createAccount(
Address.extract(keys.getPublicKey()), 0, Wei.MAX_WEI); // initialize authority account
toyWorld.createAccount(invokerAddress, 0, Wei.MAX_WEI); // initialize invoker account
final Bytes codeBytes =
Bytes.fromHexString(
"0x"
+ "6061" // push 97 the calldata length
+ "6000" // push 0 the offset
+ "6000" // push 0 the destination offset
+ "37" // calldatacopy 97 bytes of the auth param to mem 0
+ "6061" // param is 97 bytes (0x61)
+ "6000" // push 0 where in mem to find auth param
+ "73" // push next 20 bytes for the authority address
+ Address.extract(keys.getPublicKey())
.toUnprefixedHexString() // push authority address
+ "F6" // AUTH call, should work and set authorizedBy on the frame
+ "6000" // push 0 for return length, we don't care about the return
+ "6000" // push 0 for return offset, we don't care about the return
+ "6000" // push 0 for input length
+ "6000" // push 0 for input offset
+ "60FF" // push 255 for the value being sent
+ "73deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" // push20 the invokee address
+ "60FF" // push 255 gas
+ "F7"); // AUTHCALL, should work
executor.contract(invokerAddress);
executor.execute(codeBytes, authParam, Wei.ZERO, invokerAddress);
verify(this.spyingMessageCallProcessor, times(2))
.start(frameCaptor.capture(), any()); // one for parent frame, one for child
List<MessageFrame> frames = frameCaptor.getAllValues();
assertThat(frames.get(0).getStackItem(0)).isEqualTo((Bytes.of(1)));
}
@Test
public void unauthorizedAuthCall() {
final EVM pragueEVM =
MainnetEVMs.prague(new PragueGasCalculator(), BigInteger.ONE, EvmConfiguration.DEFAULT);
final EVMExecutor executor = EVMExecutor.evm(pragueEVM);
this.spyingMessageCallProcessor =
spy(new MessageCallProcessor(pragueEVM, precompileContractRegistry));
executor.messageCallProcessor(this.spyingMessageCallProcessor);
executor.gas(10_000_000_000L);
final Bytes codeBytes =
Bytes.fromHexString(
"0x"
+ "6000" // push 0 for return length
+ "6000" // push 0 for return offset
+ "6000" // push 0 for input length
+ "6000" // push 0 for input offset
+ "60FF" // push 255 for the value being sent
+ "73deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" // push20 the invokee address
+ "60FF" // push 255 gas
+ "F7"); // AUTHCALL without prior AUTH, should fail
executor.execute(codeBytes, Bytes.EMPTY, Wei.ZERO, Address.ZERO);
verify(this.spyingMessageCallProcessor).start(frameCaptor.capture(), any());
assertThat(frameCaptor.getValue().getStackItem(0)).isEqualTo(Bytes32.ZERO);
}
}
Loading…
Cancel
Save