Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce virtual operation. #45

Merged
merged 2 commits into from
Sep 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,7 @@ allprojects {
}

// Below this line are currently only license header tasks
format 'groovy', {
target '**/src/*/grovy/**/*.groovy'
}
format 'groovy', { target '**/src/*/grovy/**/*.groovy' }

// Currently disabled due to referencetest issues
// format 'bash', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,39 +108,39 @@ static EVM frontier(final GasCalculator gasCalculator) {

registerFrontierOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);

return new EVM(registry, new InvalidOperation(gasCalculator));
return new EVM(registry, gasCalculator);
}

static EVM homestead(final GasCalculator gasCalculator) {
final OperationRegistry registry = new OperationRegistry();

registerHomesteadOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);

return new EVM(registry, new InvalidOperation(gasCalculator));
return new EVM(registry, gasCalculator);
}

static EVM byzantium(final GasCalculator gasCalculator) {
final OperationRegistry registry = new OperationRegistry();

registerByzantiumOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);

return new EVM(registry, new InvalidOperation(gasCalculator));
return new EVM(registry, gasCalculator);
}

static EVM constantinople(final GasCalculator gasCalculator) {
final OperationRegistry registry = new OperationRegistry();

registerConstantinopleOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION);

return new EVM(registry, new InvalidOperation(gasCalculator));
return new EVM(registry, gasCalculator);
}

static EVM istanbul(final GasCalculator gasCalculator, final BigInteger chainId) {
final OperationRegistry registry = new OperationRegistry();

registerIstanbulOpcodes(registry, gasCalculator, Account.DEFAULT_VERSION, chainId);

return new EVM(registry, new InvalidOperation(gasCalculator));
return new EVM(registry, gasCalculator);
}

private static void registerFrontierOpcodes(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,29 @@
import org.hyperledger.besu.ethereum.vm.MessageFrame.State;
import org.hyperledger.besu.ethereum.vm.ehalt.ExceptionalHaltException;
import org.hyperledger.besu.ethereum.vm.ehalt.ExceptionalHaltManager;
import org.hyperledger.besu.ethereum.vm.operations.InvalidOperation;
import org.hyperledger.besu.ethereum.vm.operations.StopOperation;
import org.hyperledger.besu.ethereum.vm.operations.VirtualOperation;
import org.hyperledger.besu.util.bytes.BytesValue;

import java.util.EnumSet;
import java.util.Optional;
import java.util.function.BiConsumer;

import com.google.common.annotations.VisibleForTesting;
import org.apache.logging.log4j.Logger;

public class EVM {
private static final Logger LOG = getLogger();

private static final int STOP_OPCODE = 0x00;
private final OperationRegistry operations;
private final Operation invalidOperation;
private final Operation endOfScriptStop;

public EVM(final OperationRegistry operations, final Operation invalidOperation) {
public EVM(final OperationRegistry operations, final GasCalculator gasCalculator) {
this.operations = operations;
this.invalidOperation = invalidOperation;
this.invalidOperation = new InvalidOperation(gasCalculator);
this.endOfScriptStop = new VirtualOperation(new StopOperation(gasCalculator));
}

public void runToHalt(final MessageFrame frame, final OperationTracer operationTracer)
Expand Down Expand Up @@ -142,12 +147,12 @@ private static void logState(final MessageFrame frame, final Optional<Gas> curre
}
}

private Operation operationAtOffset(
final Code code, final int contractAccountVersion, final int offset) {
@VisibleForTesting
Operation operationAtOffset(final Code code, final int contractAccountVersion, final int offset) {
final BytesValue bytecode = code.getBytes();
// If the length of the program code is shorter than the required offset, halt execution.
if (offset >= bytecode.size()) {
return operations.get(STOP_OPCODE, contractAccountVersion);
return endOfScriptStop;
}

return operations.getOrDefault(bytecode.get(offset), contractAccountVersion, invalidOperation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,15 @@ default int getStackSizeChange() {
boolean getUpdatesProgramCounter();

int getOpSize();

/**
* Determines whether or not this operation has been virtually added to the contract code. For
* instance if the contract is not ended by a STOP opcode the {@link EVM} adds an explicit end of
* script stop which can be considered as virtual.
*
* @return a boolean indicating if the operation is virtual.
*/
default boolean isVirtualOperation() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright ConsenSys AG.
*
* 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 org.hyperledger.besu.ethereum.core.Gas;
import org.hyperledger.besu.ethereum.vm.MessageFrame;
import org.hyperledger.besu.ethereum.vm.Operation;

public class VirtualOperation implements Operation {

private final Operation delegate;

public VirtualOperation(final Operation delegate) {
this.delegate = delegate;
}

@Override
public Gas cost(final MessageFrame frame) {
return delegate.cost(frame);
}

@Override
public void execute(final MessageFrame frame) {
delegate.execute(frame);
}

@Override
public int getOpcode() {
return delegate.getOpcode();
}

@Override
public String getName() {
return delegate.getName();
}

@Override
public int getStackItemsConsumed() {
return delegate.getStackItemsConsumed();
}

@Override
public int getStackItemsProduced() {
return delegate.getStackItemsProduced();
}

@Override
public boolean getUpdatesProgramCounter() {
return delegate.getUpdatesProgramCounter();
}

@Override
public int getOpSize() {
return delegate.getOpSize();
}

@Override
public boolean isVirtualOperation() {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright ConsenSys AG.
*
* 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;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyByte;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.ethereum.vm.operations.StopOperation;
import org.hyperledger.besu.util.bytes.BytesValue;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class EVMTest {

private static final int CONTRACT_ACCOUNT_VERSION = 1;
@Mock private OperationRegistry operationRegistry;
@Mock private GasCalculator gasCalculator;
private EVM evm;

@Before
public void setup() {
evm = new EVM(operationRegistry, gasCalculator);
}

@Test
public void assertThatEndOfScriptNotExplicitlySetInCodeReturnsAVirtualOperation() {
final Code code = new Code(BytesValue.fromHexString("0x60203560003555606035604035556000"));
final Operation operation =
evm.operationAtOffset(code, CONTRACT_ACCOUNT_VERSION, code.getSize());
assertThat(operation).isNotNull();
assertThat(operation.isVirtualOperation()).isTrue();
}

@Test
public void assertThatEndOfScriptExplicitlySetInCodeDoesNotReturnAVirtualOperation() {
final Code code = new Code(BytesValue.fromHexString("0x6020356000355560603560403555600000"));
when(operationRegistry.getOrDefault(
anyByte(), eq(CONTRACT_ACCOUNT_VERSION), any(Operation.class)))
.thenReturn(new StopOperation(gasCalculator));
final Operation operation =
evm.operationAtOffset(code, CONTRACT_ACCOUNT_VERSION, code.getSize() - 1);
assertThat(operation).isNotNull();
assertThat(operation.isVirtualOperation()).isFalse();
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
org.gradle.jvmargs=-Xmx1g
version=1.2.4
version=1.2.5-SNAPSHOT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you have an extra commit here from Ed - can you remove this commit from your branch history?