Signed-off-by: Danno Ferrin <danno@numisight.com>
mega-eof
Danno Ferrin 6 months ago
commit c08ad23b3b
  1. 35
      .github/ISSUE_TEMPLATE/release-checklist.md
  2. 2
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodeSmartContractPermissioningOutOfSyncAcceptanceTest.java
  3. 2
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/permissioning/NodesSmartContractPermissioningStaticNodesAcceptanceTest.java
  4. 6
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  5. 25
      evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java
  6. 43
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java
  7. 58
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java
  8. 102
      evm/src/main/java/org/hyperledger/besu/evm/operation/AuthCallOperation.java
  9. 118
      evm/src/main/java/org/hyperledger/besu/evm/operation/AuthOperation.java
  10. 56
      evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java
  11. 153
      evm/src/test/java/org/hyperledger/besu/evm/operations/AuthOperationTest.java
  12. 5
      evm/src/test/java/org/hyperledger/besu/evm/toy/ToyAccount.java

@ -0,0 +1,35 @@
---
name: Release Checklist
about: items to be completed for each release
title: ''
labels: ''
assignees: ''
---
- [ ] Confirm anything outstanding for release with other maintainers on #besu-release in Discord
- [ ] Notify maintainers about updating changelog for in-flight PRs
- [ ] Update changelog if necessary, and merge a PR for it to main
- [ ] Optional: for hotfixes, create a release branch and cherry-pick, e.g. `release-<version>-hotfix`
- [ ] Optional: create a PR into main from the hotfix branch to see the CI checks pass
- [ ] On the appropriate branch/commit, create a calver tag for the release candidate, format example: `24.4.0-RC2`
- [ ] Sign-off with team; confirm tag is correct in #besu-release in Discord
- [ ] Consensys staff start burn-in using the proposed release <version-RCX> tag
- [ ] Sign off burn-in; convey burn-in results in #besu-release in Discord
- [ ] Using the same git sha, create a calver tag for the FULL RELEASE, example format `24.4.0`
- [ ] Using the FULL RELEASE tag, create a release in github to trigger the workflows. Once published:
- makes the release "latest" in github
- this is now public and notifies subscribed users
- publishes artefacts and version-specific docker tags
- publishes the docker `latest` tag variants
- [ ] Draft homebrew PR
- [ ] Draft documentation release
- [ ] Ensure binary SHAs are correct on the release page
- [ ] Docker release startup test:
- `docker run hyperledger/besu:<version>`
- `docker run hyperledger/besu:<version>-arm64`
- `docker run --platform linux/amd64 hyperledger/besu:<version>-amd64`
- `docker run --pull=always hyperledger/besu:latest` (check version is <version>)
- [ ] Merge homebrew PR
- [ ] Publish Docs Release
- [ ] Social announcements

@ -17,6 +17,7 @@ package org.hyperledger.besu.tests.acceptance.permissioning;
import org.hyperledger.besu.tests.acceptance.dsl.node.Node;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
public class NodeSmartContractPermissioningOutOfSyncAcceptanceTest
@ -42,6 +43,7 @@ public class NodeSmartContractPermissioningOutOfSyncAcceptanceTest
}
@Test
@Disabled("test is flaky #7108")
public void addNodeToClusterAndVerifyNonBootNodePeerConnectionWorksAfterSync() {
final long blockchainHeight = 25L;
waitForBlockHeight(permissionedNodeA, blockchainHeight);

@ -25,8 +25,10 @@ import java.util.List;
import javax.annotation.Nonnull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled("flaky test #7155")
public class NodesSmartContractPermissioningStaticNodesAcceptanceTest
extends NodeSmartContractPermissioningAcceptanceTestBase {

@ -34,8 +34,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;
@ -966,9 +964,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.
*
@ -1317,24 +1314,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();
@ -1372,7 +1351,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

@ -224,35 +224,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.
*
@ -674,18 +645,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
}
}

@ -171,7 +171,10 @@ public class ToyAccount implements MutableAccount {
*/
@Override
public boolean isStorageEmpty() {
return storage.isEmpty();
if (storage.isEmpty()) {
return parent == null || parent.isStorageEmpty();
}
return false;
}
@Override

Loading…
Cancel
Save