forked from PegaSysEng/pantheon
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[NC-1685] Improve tests for BlockHashOperation (PegaSysEng#47)
* Introduce MessageFrameTestFixture to make it easier to test EVM operations. * Add unit tests for BlockHashOperation.
- Loading branch information
Showing
7 changed files
with
326 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
184 changes: 184 additions & 0 deletions
184
...e/src/test-support/java/net/consensys/pantheon/ethereum/core/MessageFrameTestFixture.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package net.consensys.pantheon.ethereum.core; | ||
|
||
import tech.pegasys.pantheon.ethereum.chain.Blockchain; | ||
import tech.pegasys.pantheon.ethereum.core.Address; | ||
import tech.pegasys.pantheon.ethereum.core.AddressHelpers; | ||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; | ||
import tech.pegasys.pantheon.ethereum.core.ExecutionContextTestFixture; | ||
import tech.pegasys.pantheon.ethereum.core.Gas; | ||
import tech.pegasys.pantheon.ethereum.core.MutableWorldState; | ||
import tech.pegasys.pantheon.ethereum.core.Wei; | ||
import tech.pegasys.pantheon.ethereum.core.WorldUpdater; | ||
import tech.pegasys.pantheon.ethereum.vm.BlockHashLookup; | ||
import tech.pegasys.pantheon.ethereum.vm.Code; | ||
import tech.pegasys.pantheon.ethereum.vm.MessageFrame; | ||
import tech.pegasys.pantheon.ethereum.vm.MessageFrame.Type; | ||
import tech.pegasys.pantheon.util.bytes.Bytes32; | ||
import tech.pegasys.pantheon.util.bytes.BytesValue; | ||
|
||
import java.util.ArrayDeque; | ||
import java.util.ArrayList; | ||
import java.util.Deque; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
public class MessageFrameTestFixture { | ||
|
||
private static final Address DEFAUT_ADDRESS = AddressHelpers.ofValue(244259721); | ||
|
||
private Type type = Type.MESSAGE_CALL; | ||
private Deque<MessageFrame> messageFrameStack = new ArrayDeque<>(); | ||
private Optional<Blockchain> blockchain = Optional.empty(); | ||
private Optional<WorldUpdater> worldState = Optional.empty(); | ||
private Gas initialGas = Gas.MAX_VALUE; | ||
private Address address = DEFAUT_ADDRESS; | ||
private Address sender = DEFAUT_ADDRESS; | ||
private Address originator = DEFAUT_ADDRESS; | ||
private Address contract = DEFAUT_ADDRESS; | ||
private Wei gasPrice = Wei.ZERO; | ||
private Wei value = Wei.ZERO; | ||
private BytesValue inputData = BytesValue.EMPTY; | ||
private Code code = new Code(BytesValue.EMPTY); | ||
private final List<Bytes32> stackItems = new ArrayList<>(); | ||
private Optional<BlockHeader> blockHeader = Optional.empty(); | ||
private int depth = 0; | ||
private Optional<BlockHashLookup> blockHashLookup = Optional.empty(); | ||
private ExecutionContextTestFixture executionContextTestFixture; | ||
|
||
public MessageFrameTestFixture type(final Type type) { | ||
this.type = type; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture messageFrameStack(final Deque<MessageFrame> messageFrameStack) { | ||
this.messageFrameStack = messageFrameStack; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture blockchain(final Blockchain blockchain) { | ||
this.blockchain = Optional.of(blockchain); | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture worldState(final WorldUpdater worldState) { | ||
this.worldState = Optional.of(worldState); | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture worldState(final MutableWorldState worldState) { | ||
this.worldState = Optional.of(worldState.updater()); | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture initialGas(final Gas initialGas) { | ||
this.initialGas = initialGas; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture sender(final Address sender) { | ||
this.sender = sender; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture address(final Address address) { | ||
this.address = address; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture originator(final Address originator) { | ||
this.originator = originator; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture contract(final Address contract) { | ||
this.contract = contract; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture gasPrice(final Wei gasPrice) { | ||
this.gasPrice = gasPrice; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture value(final Wei value) { | ||
this.value = value; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture inputData(final BytesValue inputData) { | ||
this.inputData = inputData; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture code(final Code code) { | ||
this.code = code; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture blockHeader(final BlockHeader blockHeader) { | ||
this.blockHeader = Optional.of(blockHeader); | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture depth(final int depth) { | ||
this.depth = depth; | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture pushStackItem(final Bytes32 item) { | ||
stackItems.add(item); | ||
return this; | ||
} | ||
|
||
public MessageFrameTestFixture blockHashLookup(final BlockHashLookup blockHashLookup) { | ||
this.blockHashLookup = Optional.of(blockHashLookup); | ||
return this; | ||
} | ||
|
||
public MessageFrame build() { | ||
final Blockchain blockchain = this.blockchain.orElseGet(this::createDefaultBlockchain); | ||
final BlockHeader blockHeader = | ||
this.blockHeader.orElseGet(() -> blockchain.getBlockHeader(0).get()); | ||
final MessageFrame frame = | ||
MessageFrame.builder() | ||
.type(type) | ||
.messageFrameStack(messageFrameStack) | ||
.blockchain(blockchain) | ||
.worldState(worldState.orElseGet(this::createDefaultWorldState)) | ||
.initialGas(initialGas) | ||
.address(address) | ||
.originator(originator) | ||
.gasPrice(gasPrice) | ||
.inputData(inputData) | ||
.sender(sender) | ||
.value(value) | ||
.apparentValue(value) | ||
.contract(contract) | ||
.code(code) | ||
.blockHeader(blockHeader) | ||
.depth(depth) | ||
.completer(c -> {}) | ||
.miningBeneficiary(blockHeader.getCoinbase()) | ||
.blockHashLookup( | ||
blockHashLookup.orElseGet(() -> new BlockHashLookup(blockHeader, blockchain))) | ||
.build(); | ||
stackItems.forEach(frame::pushStackItem); | ||
return frame; | ||
} | ||
|
||
private WorldUpdater createDefaultWorldState() { | ||
return getOrCreateExecutionContextTestFixture().getStateArchive().getMutable().updater(); | ||
} | ||
|
||
private Blockchain createDefaultBlockchain() { | ||
return getOrCreateExecutionContextTestFixture().getBlockchain(); | ||
} | ||
|
||
private ExecutionContextTestFixture getOrCreateExecutionContextTestFixture() { | ||
// Avoid creating a test fixture if the test supplies the blockchain and worldstate. | ||
if (executionContextTestFixture == null) { | ||
executionContextTestFixture = new ExecutionContextTestFixture(); | ||
} | ||
return executionContextTestFixture; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
96 changes: 96 additions & 0 deletions
96
...e/src/test/java/net/consensys/pantheon/ethereum/vm/operations/BlockHashOperationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
package net.consensys.pantheon.ethereum.vm.operations; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.verify; | ||
import static org.mockito.Mockito.verifyNoMoreInteractions; | ||
import static org.mockito.Mockito.when; | ||
|
||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; | ||
import tech.pegasys.pantheon.ethereum.core.BlockHeaderTestFixture; | ||
import tech.pegasys.pantheon.ethereum.core.Hash; | ||
import tech.pegasys.pantheon.ethereum.mainnet.FrontierGasCalculator; | ||
import tech.pegasys.pantheon.ethereum.vm.BlockHashLookup; | ||
import tech.pegasys.pantheon.ethereum.vm.MessageFrame; | ||
import tech.pegasys.pantheon.ethereum.vm.operations.BlockHashOperation; | ||
import tech.pegasys.pantheon.util.bytes.Bytes32; | ||
import tech.pegasys.pantheon.util.bytes.BytesValue; | ||
import tech.pegasys.pantheon.util.uint.UInt256; | ||
|
||
import com.google.common.base.Strings; | ||
import net.consensys.pantheon.ethereum.core.MessageFrameTestFixture; | ||
import org.junit.After; | ||
import org.junit.Test; | ||
|
||
public class BlockHashOperationTest { | ||
|
||
private static final int MAXIMUM_COMPLETE_BLOCKS_BEHIND = 256; | ||
private final BlockHashLookup blockHashLookup = mock(BlockHashLookup.class); | ||
private final BlockHashOperation blockHashOperation = | ||
new BlockHashOperation(new FrontierGasCalculator()); | ||
|
||
@After | ||
public void verifyNoUnexpectedHashLookups() { | ||
verifyNoMoreInteractions(blockHashLookup); | ||
} | ||
|
||
@Test | ||
public void shouldReturnZeroWhenArgIsBiggerThanALong() { | ||
assertBlockHash(Bytes32.fromHexString(Strings.repeat("F", 64)), Bytes32.ZERO, 100); | ||
} | ||
|
||
@Test | ||
public void shouldReturnZeroWhenCurrentBlockIsGenesis() { | ||
assertBlockHash(Bytes32.ZERO, Bytes32.ZERO, BlockHeader.GENESIS_BLOCK_NUMBER); | ||
} | ||
|
||
@Test | ||
public void shouldReturnZeroWhenRequestedBlockAheadOfCurrent() { | ||
assertBlockHash(250, Bytes32.ZERO, 100); | ||
} | ||
|
||
@Test | ||
public void shouldReturnZeroWhenRequestedBlockTooFarBehindCurrent() { | ||
final int requestedBlock = 10; | ||
// Our block is the one after the chain head (it's a new block), hence the + 1. | ||
final int importingBlockNumber = MAXIMUM_COMPLETE_BLOCKS_BEHIND + requestedBlock + 1; | ||
assertBlockHash(requestedBlock, Bytes32.ZERO, importingBlockNumber); | ||
} | ||
|
||
@Test | ||
public void shouldReturnZeroWhenRequestedBlockGreaterThanImportingBlock() { | ||
assertBlockHash(101, Bytes32.ZERO, 100); | ||
} | ||
|
||
@Test | ||
public void shouldReturnZeroWhenRequestedBlockEqualToImportingBlock() { | ||
assertBlockHash(100, Bytes32.ZERO, 100); | ||
} | ||
|
||
@Test | ||
public void shouldReturnBlockHashUsingLookupFromFrameWhenItIsWithinTheAllowedRange() { | ||
final Hash blockHash = Hash.hash(BytesValue.fromHexString("0x1293487297")); | ||
when(blockHashLookup.getBlockHash(100)).thenReturn(blockHash); | ||
assertBlockHash(100, blockHash, 200); | ||
verify(blockHashLookup).getBlockHash(100); | ||
} | ||
|
||
private void assertBlockHash( | ||
final long requestedBlock, final Bytes32 expectedOutput, final long currentBlockNumber) { | ||
assertBlockHash(UInt256.of(requestedBlock).getBytes(), expectedOutput, currentBlockNumber); | ||
} | ||
|
||
private void assertBlockHash( | ||
final Bytes32 input, final Bytes32 expectedOutput, final long currentBlockNumber) { | ||
final MessageFrame frame = | ||
new MessageFrameTestFixture() | ||
.blockHashLookup(blockHashLookup) | ||
.blockHeader(new BlockHeaderTestFixture().number(currentBlockNumber).buildHeader()) | ||
.pushStackItem(input) | ||
.build(); | ||
blockHashOperation.execute(frame); | ||
final Bytes32 result = frame.popStackItem(); | ||
assertThat(result).isEqualTo(expectedOutput); | ||
assertThat(frame.stackSize()).isZero(); | ||
} | ||
} |
Oops, something went wrong.