mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 21:17:54 -05:00
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>
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user