Always copy create code (#2908)

Always copy create code

Always copy the create code. Since it is memory bytes it is subject to
re-writing by the caller and all data would otherwise have to be treated
 as mutable.

Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
pull/2891/head
Danno Ferrin 3 years ago committed by GitHub
parent 37ef59e24a
commit 85d7f84ba5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      CHANGELOG.md
  2. 142
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/CreateOperationTest.java
  3. 2
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java

@ -7,11 +7,12 @@
- Added support for PKCS11 keystore on PKI Block Creation. [#2865](https://github.com/hyperledger/besu/pull/2865)
### Bug Fixes
- Regression in RC1 involving LogOperation and frame memory overwrites [#2908](https://github.com/hyperledger/besu/pull/2908)
### Early Access Features
- Enable plugins to expose custom JSON-RPC / WebSocket methods [#1317](https://github.com/hyperledger/besu/issues/1317)
## 21.10.0-RC1/RC2
### Additions and Improvements
- The EVM has been factored out into a standalone module, suitable for inclusion as a library. [#2790](https://github.com/hyperledger/besu/pull/2790)
@ -20,7 +21,6 @@
- reduces need for JUMPDEST analysis via caching [#2607](https://github.com/hyperledger/besu/pull/2821)
- Add support for custom private key file for public-key export and public-key export-address commands [#2801](https://github.com/hyperledger/besu/pull/2801)
### Bug Fixes
- Allow BESU_CONFIG_FILE environment to specify TOML file [#2455](https://github.com/hyperledger/besu/issues/2455)
- Fix bug with private contracts not able to call public contracts that call public contracts [#2816](https://github.com/hyperledger/besu/pull/2816)

@ -0,0 +1,142 @@
/*
* 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.ethereum.vm.operations;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.vm.BlockHashLookup;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.Gas;
import org.hyperledger.besu.evm.MainnetEVMs;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.operation.CreateOperation;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.evm.worldstate.WrappedEvmAccount;
import java.util.ArrayDeque;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.Test;
public class CreateOperationTest {
private final WorldUpdater worldUpdater = mock(WorldUpdater.class);
private final WrappedEvmAccount account = mock(WrappedEvmAccount.class);
private final WrappedEvmAccount newAccount = mock(WrappedEvmAccount.class);
private final MutableAccount mutableAccount = mock(MutableAccount.class);
private final MutableAccount newMutableAccount = mock(MutableAccount.class);
private final CreateOperation operation = new CreateOperation(new ConstantinopleGasCalculator());
private static final String TOPIC =
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"; // 32 FFs
private static final Bytes SIMPLE_CREATE =
Bytes.fromHexString(
"0x"
+ "7f" // push32
+ TOPIC
+ "6000" // PUSH1 0x00
+ "6000" // PUSH1 0x00
+ "A1" // LOG1
+ "6000" // PUSH1 0x00
+ "6000" // PUSH1 0x00
+ "F3" // RETURN
);
public static final String SENDER = "0xdeadc0de00000000000000000000000000000000";
@Test
public void createFromMemoryMutationSafe() {
// Given: Execute a CREATE operatio with a contrat that logs in the constructor
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
when(account.getMutable()).thenReturn(mutableAccount);
ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
MessageFrame messageFrame =
MessageFrame.builder()
.type(MessageFrame.Type.CONTRACT_CREATION)
.contract(Address.ZERO)
.inputData(Bytes.EMPTY)
.sender(Address.fromHexString(SENDER))
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(new Code(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE)))
.depth(1)
.completer(__ -> {})
.address(Address.fromHexString(SENDER))
.blockHashLookup(mock(BlockHashLookup.class))
.blockValues(mock(ProcessableBlockHeader.class))
.gasPrice(Wei.ZERO)
.messageFrameStack(messageFrameStack)
.miningBeneficiary(Address.ZERO)
.originator(Address.ZERO)
.initialGas(Gas.of(100000))
.worldUpdater(worldUpdater)
.build();
messageFrame.pushStackItem(memoryLength);
messageFrame.pushStackItem(memoryOffset);
messageFrame.pushStackItem(UInt256.ZERO);
messageFrame.expandMemory(0, 500);
messageFrame.writeMemory(
memoryOffset.trimLeadingZeros().toInt(), SIMPLE_CREATE.size(), SIMPLE_CREATE);
when(account.getNonce()).thenReturn(55L);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
when(worldUpdater.get(any())).thenReturn(account);
when(worldUpdater.getSenderAccount(any())).thenReturn(account);
when(worldUpdater.getOrCreate(any())).thenReturn(newAccount);
when(newAccount.getMutable()).thenReturn(newMutableAccount);
when(newMutableAccount.getCode()).thenReturn(Bytes.EMPTY);
when(worldUpdater.updater()).thenReturn(worldUpdater);
final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT);
operation.execute(messageFrame, evm);
MessageFrame createFrame = messageFrameStack.peek();
ContractCreationProcessor ccp =
new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of());
ccp.process(createFrame, OperationTracer.NO_TRACING);
Log log = createFrame.getLogs().get(0);
String calculatedTopic = log.getTopics().get(0).toUnprefixedHexString();
assertThat(calculatedTopic).isEqualTo(TOPIC);
// WHEN the memory that the create operation was executed from is altered.
messageFrame.writeMemory(
memoryOffset.trimLeadingZeros().toInt(),
SIMPLE_CREATE.size(),
Bytes.random(SIMPLE_CREATE.size()));
// THEN the logs still have the expected topic
String calculatedTopicAfter = log.getTopics().get(0).toUnprefixedHexString();
assertThat(calculatedTopicAfter).isEqualTo(TOPIC);
}
}

@ -116,7 +116,7 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
final Wei value = Wei.wrap(frame.getStackItem(0));
final long inputOffset = clampedToLong(frame.getStackItem(1));
final long inputSize = clampedToLong(frame.getStackItem(2));
final Bytes inputData = frame.readMutableMemory(inputOffset, inputSize);
final Bytes inputData = frame.readMemory(inputOffset, inputSize);
final Address contractAddress = targetContractAddress(frame);

Loading…
Cancel
Save