Skip to content

Commit

Permalink
Fix selfdestruct-to-mirror issue, add more CREATE2 tests (#2926)
Browse files Browse the repository at this point in the history
* Add failing test demonstrating selfdestruct-to-mirror-address bug

Signed-off-by: tinker-michaelj <michael.tinker@hedera.com>

* Reject selfdestruct-to-mirror-self with `SELF_DESTRUCT_TO_SELF`

Signed-off-by: tinker-michaelj <michael.tinker@hedera.com>
  • Loading branch information
Michael Tinker committed Feb 28, 2022
1 parent c3e8b63 commit 3b542b7
Show file tree
Hide file tree
Showing 11 changed files with 312 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*
*/

import com.hedera.services.store.contracts.HederaStackedWorldStateUpdater;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
Expand Down Expand Up @@ -60,14 +61,19 @@ public HederaSelfDestructOperation(final GasCalculator gasCalculator,

@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
Address recipientAddress = Words.toAddress(frame.getStackItem(0));
if (!addressValidator.test(recipientAddress, frame)) {
final var updater = (HederaStackedWorldStateUpdater) frame.getWorldUpdater();
final var beneficiaryAddressOrAlias = Words.toAddress(frame.getStackItem(0));
final var beneficiaryAddress = updater.priorityAddress(beneficiaryAddressOrAlias);
if (!addressValidator.test(beneficiaryAddress, frame)) {
return new OperationResult(errorGasCost(null),
Optional.of(HederaExceptionalHaltReason.INVALID_SOLIDITY_ADDRESS));
}
Address address = frame.getRecipientAddress();
if (address.equals(recipientAddress)) {
Account account = frame.getWorldUpdater().get(recipientAddress);

// This address is already the EIP-1014 address if applicable; so we can compare it directly to
// the "priority" address we computed above for the beneficiary
final var address = frame.getRecipientAddress();
if (address.equals(beneficiaryAddress)) {
final var account = frame.getWorldUpdater().get(beneficiaryAddress);
return new OperationResult(errorGasCost(account),
Optional.of(HederaExceptionalHaltReason.SELF_DESTRUCT_TO_SELF));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@
* ‍
*/

import com.hedera.services.store.contracts.HederaStackedWorldStateUpdater;
import com.hedera.services.utils.EntityNum;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.Gas;
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.worldstate.WorldUpdater;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand All @@ -44,56 +46,87 @@

@ExtendWith(MockitoExtension.class)
class HederaSelfDestructOperationTest {
final private String ethAddress = "0xc257274276a4e539741ca11b590b9447b26a8051";
final private String ethRecipientAddress = "0xc257274276a4e539741ca11b590b9447b26a8052";

private Address ethAddressInstance = Address.fromHexString(ethAddress);
private static final EntityNum beneficiary = EntityNum.fromLong(2_345);
private static final String ethAddress = "0xc257274276a4e539741ca11b590b9447b26a8051";
private static final Address eip1014Address = Address.fromHexString(ethAddress);

@Mock
WorldUpdater worldUpdater;

private HederaStackedWorldStateUpdater worldUpdater;
@Mock
GasCalculator gasCalculator;

private GasCalculator gasCalculator;
@Mock
MessageFrame mf;

private MessageFrame frame;
@Mock
EVM evm;
private EVM evm;
@Mock
private Account account;
@Mock
private BiPredicate<Address, MessageFrame> addressValidator;

HederaSelfDestructOperation subject;
private HederaSelfDestructOperation subject;

@BeforeEach
void setUp() {
subject = new HederaSelfDestructOperation(gasCalculator, addressValidator);
given(mf.getStackItem(0)).willReturn(ethAddressInstance);

given(frame.getWorldUpdater()).willReturn(worldUpdater);
given(gasCalculator.selfDestructOperationGasCost(any(), eq(Wei.ONE))).willReturn(Gas.of(2L));
}

@Test
void executeSelfDestructToSelf() {
given(mf.getRecipientAddress()).willReturn(ethAddressInstance);
given(addressValidator.test(any(), any())).willReturn(true);
given(mf.getWorldUpdater()).willReturn(worldUpdater);
void delegatesToSuperWhenValid() {
givenRubberstampValidator();

final var beneficiaryMirror = beneficiary.toEvmAddress();
given(frame.getStackItem(0)).willReturn(beneficiaryMirror);
given(frame.popStackItem()).willReturn(beneficiaryMirror);
given(worldUpdater.priorityAddress(beneficiaryMirror)).willReturn(beneficiaryMirror);
given(frame.getRecipientAddress()).willReturn(eip1014Address);
given(worldUpdater.get(any())).willReturn(account);
given(account.getBalance()).willReturn(Wei.ONE);
given(frame.isStatic()).willReturn(true);
given(gasCalculator.getColdAccountAccessCost()).willReturn(Gas.of(1));

final var opResult = subject.execute(frame, evm);

assertEquals(Optional.of(ExceptionalHaltReason.ILLEGAL_STATE_CHANGE), opResult.getHaltReason());
assertEquals(Optional.of(Gas.of(3L)), opResult.getGasCost());
}

@Test
void rejectsSelfDestructToSelf() {
givenRubberstampValidator();

var opResult = subject.execute(mf, evm);
final var beneficiaryMirror = beneficiary.toEvmAddress();
given(frame.getStackItem(0)).willReturn(beneficiaryMirror);
given(worldUpdater.priorityAddress(beneficiaryMirror)).willReturn(eip1014Address);
given(frame.getRecipientAddress()).willReturn(eip1014Address);

final var opResult = subject.execute(frame, evm);

assertEquals(Optional.of(HederaExceptionalHaltReason.SELF_DESTRUCT_TO_SELF), opResult.getHaltReason());
assertEquals(Optional.of(Gas.of(2L)), opResult.getGasCost());
}

@Test
void executeInvalidSolidityAddress() {
givenRejectingValidator();

given(addressValidator.test(any(), any())).willReturn(false);
final var beneficiaryMirror = beneficiary.toEvmAddress();
given(frame.getStackItem(0)).willReturn(beneficiaryMirror);
given(worldUpdater.priorityAddress(beneficiaryMirror)).willReturn(eip1014Address);

var opResult = subject.execute(mf, evm);
final var opResult = subject.execute(frame, evm);

assertEquals(Optional.of(HederaExceptionalHaltReason.INVALID_SOLIDITY_ADDRESS), opResult.getHaltReason());
assertEquals(Optional.of(Gas.of(2L)), opResult.getGasCost());
}

private void givenRubberstampValidator() {
given(addressValidator.test(any(), any())).willReturn(true);
}

private void givenRejectingValidator() {
given(addressValidator.test(any(), any())).willReturn(false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ public ContractInfoAsserts addressOrAlias(final String hexedEvm) {
return this;
}

public ContractInfoAsserts balance(final long amount) {
registerProvider((spec, o) -> {
assertEquals(
amount,
object2ContractInfo(o).getBalance(),
"Bad balance");
});
return this;
}

public ContractInfoAsserts solidityAddress(String contract) {
registerProvider((spec, o) -> {
assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,27 @@ public class ContractResources {
public static final String NESTED_TRANSFER_CONTRACT_1_PATH = bytecodePath("NestedTransferContract1");
public static final String NESTED_TRANSFER_CONTRACT_2_PATH = bytecodePath("NestedTransferContract2");
public static final String OUTER_CREATOR_PATH = bytecodePath("OuterCreator");
public static final String CREATE_DONOR_PATH = bytecodePath("CreateDonor");

public static final String BUILD_THEN_REVERT_THEN_BUILD_ABI = "{\"inputs\":[{\"internalType\":\"bytes32\"," +
"\"name\":\"salt\",\"type\":\"bytes32\"}],\"name\":\"buildThenRevertThenBuild\",\"outputs\":[]," +
"\"stateMutability\":\"payable\",\"type\":\"function\"}";
public static final String RELINQUISH_FUNDS_ABI = "{\"inputs\":[{\"internalType\":\"address\"," +
"\"name\":\"beneficiary\",\"type\":\"address\"}],\"name\":\"relinquishFundsTo\",\"outputs\":[]," +
"\"stateMutability\":\"nonpayable\",\"type\":\"function\"}";
public static final String CREATE_DONOR_ABI = "{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"salt\"," +
"\"type\":\"bytes32\"}],\"name\":\"buildDonor\",\"outputs\":[],\"stateMutability\":\"nonpayable\"," +
"\"type\":\"function\"}";
public static final String CREATE_AND_RECREATE_ABI = "{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"salt\"," +
"\"type\":\"bytes32\"}],\"name\":\"createAndRecreateTest\",\"outputs\":[]," +
"\"stateMutability\":\"nonpayable\",\"type\":\"function\"}";
public static final String WRONG_REPEATED_CREATE2_ABI = "{\"inputs\":[{\"internalType\":\"bytes\"," +
"\"name\":\"bytecode\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"_salt\"," +
"\"type\":\"uint256\"}],\"name\":\"wronglyDeployTwice\",\"outputs\":[],\"stateMutability\":\"payable\"," +
"\"type\":\"function\"}";
public static final String WHAT_IS_FOO_ABI = "{\"inputs\":[],\"name\":\"whatTheFoo\"," +
"\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}]," +
"\"stateMutability\":\"view\",\"type\":\"function\"}";
public static final String START_CHAIN_ABI = "{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"logMessage\"," +
"\"type\":\"bytes\"}],\"name\":\"startChain\",\"outputs\":[],\"stateMutability\":\"nonpayable\"," +
"\"type\":\"function\"}";
Expand Down
Loading

0 comments on commit 3b542b7

Please sign in to comment.