Skip to content
This repository was archived by the owner on Jul 1, 2025. It is now read-only.

Commit 5587b80

Browse files
shemnongaryschulte
authored andcommitted
Refactor BlockHashOperation block height check into Operation (#8091)
* Refactor BlockHashOperation block height check into Operation Refactor the 4 different places block height is checked across Besu into the BlockHashOperation. Add support to make the lookback range configurable in the BlockHashLookup interface, but default it to 256. Signed-off-by: Danno Ferrin <[email protected]> * spotless Signed-off-by: Danno Ferrin <[email protected]> * javadoc Signed-off-by: Danno Ferrin <[email protected]> * Update Unit Tests to use the operation. Signed-off-by: Danno Ferrin <[email protected]> * Update Unit Tests to use the operation. Signed-off-by: Danno Ferrin <[email protected]> --------- Signed-off-by: Danno Ferrin <[email protected]>
1 parent b06cc73 commit 5587b80

File tree

8 files changed

+72
-45
lines changed

8 files changed

+72
-45
lines changed

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/BlockchainBasedBlockHashLookup.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,31 +35,19 @@
3535
* all transactions within that block.
3636
*/
3737
public class BlockchainBasedBlockHashLookup implements BlockHashLookup {
38-
private static final int MAX_RELATIVE_BLOCK = 256;
39-
40-
private final long currentBlockNumber;
4138
private ProcessableBlockHeader searchStartHeader;
4239
private final Blockchain blockchain;
4340
private final Map<Long, Hash> hashByNumber = new HashMap<>();
4441

4542
public BlockchainBasedBlockHashLookup(
4643
final ProcessableBlockHeader currentBlock, final Blockchain blockchain) {
47-
this.currentBlockNumber = currentBlock.getNumber();
4844
this.searchStartHeader = currentBlock;
4945
this.blockchain = blockchain;
5046
hashByNumber.put(currentBlock.getNumber() - 1, currentBlock.getParentHash());
5147
}
5248

5349
@Override
5450
public Hash apply(final MessageFrame frame, final Long blockNumber) {
55-
// If the current block is the genesis block or the sought block is
56-
// not within the last 256 completed blocks, zero is returned.
57-
if (currentBlockNumber == 0
58-
|| blockNumber >= currentBlockNumber
59-
|| blockNumber < (currentBlockNumber - MAX_RELATIVE_BLOCK)) {
60-
return ZERO;
61-
}
62-
6351
final Hash cachedHash = hashByNumber.get(blockNumber);
6452
if (cachedHash != null) {
6553
return cachedHash;

ethereum/core/src/main/java/org/hyperledger/besu/ethereum/vm/Eip7709BlockHashLookup.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,6 @@ public Eip7709BlockHashLookup(final Address contractAddress, final long historyS
7474

7575
@Override
7676
public Hash apply(final MessageFrame frame, final Long blockNumber) {
77-
final long currentBlockNumber = frame.getBlockValues().getNumber();
78-
final long minBlockServe = Math.max(0, currentBlockNumber - blockHashServeWindow);
79-
if (blockNumber >= currentBlockNumber || blockNumber < minBlockServe) {
80-
LOG.trace("failed to read hash from system account for block {}", blockNumber);
81-
return ZERO;
82-
}
83-
8477
final Hash cachedHash = hashByNumber.get(blockNumber);
8578
if (cachedHash != null) {
8679
return cachedHash;
@@ -105,4 +98,9 @@ public Hash apply(final MessageFrame frame, final Long blockNumber) {
10598
hashByNumber.put(blockNumber, blockHash);
10699
return blockHash;
107100
}
101+
102+
@Override
103+
public long getLookback() {
104+
return blockHashServeWindow;
105+
}
108106
}

ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/BlockchainBasedBlockHashLookupTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import static org.assertj.core.api.Assertions.assertThat;
1818
import static org.mockito.ArgumentMatchers.anyLong;
19+
import static org.mockito.Mockito.clearInvocations;
1920
import static org.mockito.Mockito.mock;
2021
import static org.mockito.Mockito.never;
2122
import static org.mockito.Mockito.verify;
@@ -27,10 +28,14 @@
2728
import org.hyperledger.besu.ethereum.core.BlockHeader;
2829
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
2930
import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
31+
import org.hyperledger.besu.evm.frame.BlockValues;
3032
import org.hyperledger.besu.evm.frame.MessageFrame;
33+
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
34+
import org.hyperledger.besu.evm.operation.BlockHashOperation;
3135

3236
import java.util.Optional;
3337

38+
import org.apache.tuweni.bytes.Bytes;
3439
import org.assertj.core.api.Assertions;
3540
import org.junit.jupiter.api.AfterEach;
3641
import org.junit.jupiter.api.BeforeEach;
@@ -47,6 +52,7 @@ class BlockchainBasedBlockHashLookupTest {
4752
private BlockHeader[] headers;
4853
private BlockHashLookup lookup;
4954
private final MessageFrame messageFrameMock = mock(MessageFrame.class);
55+
private final BlockValues blockValuesMock = mock(BlockValues.class);
5056

5157
@BeforeEach
5258
void setUp() {
@@ -138,7 +144,18 @@ private void assertHashForBlockNumber(final int blockNumber) {
138144
}
139145

140146
private void assertHashForBlockNumber(final int blockNumber, final Hash hash) {
141-
Assertions.assertThat(lookup.apply(messageFrameMock, (long) blockNumber)).isEqualTo(hash);
147+
clearInvocations(messageFrameMock, blockValuesMock);
148+
149+
BlockHashOperation op = new BlockHashOperation(new CancunGasCalculator());
150+
when(messageFrameMock.getRemainingGas()).thenReturn(10_000_000L);
151+
when(messageFrameMock.popStackItem()).thenReturn(Bytes.ofUnsignedInt(blockNumber));
152+
when(messageFrameMock.getBlockValues()).thenReturn(blockValuesMock);
153+
when(messageFrameMock.getBlockHashLookup()).thenReturn(lookup);
154+
when(blockValuesMock.getNumber()).thenReturn((long) CURRENT_BLOCK_NUMBER);
155+
156+
op.execute(messageFrameMock, null);
157+
158+
verify(messageFrameMock).pushStackItem(hash);
142159
}
143160

144161
private BlockHeader createHeader(final int blockNumber, final BlockHeader parentHeader) {

ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/Eip7709BlockHashLookupTest.java

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package org.hyperledger.besu.ethereum.vm;
1616

1717
import static org.assertj.core.api.Assertions.assertThat;
18-
import static org.mockito.ArgumentMatchers.eq;
1918
import static org.mockito.Mockito.clearInvocations;
2019
import static org.mockito.Mockito.mock;
2120
import static org.mockito.Mockito.spy;
@@ -35,17 +34,19 @@
3534
import org.hyperledger.besu.evm.fluent.SimpleWorld;
3635
import org.hyperledger.besu.evm.frame.BlockValues;
3736
import org.hyperledger.besu.evm.frame.MessageFrame;
37+
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
38+
import org.hyperledger.besu.evm.operation.BlockHashOperation;
3839
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
3940

4041
import java.util.ArrayList;
4142
import java.util.List;
4243

44+
import org.apache.tuweni.bytes.Bytes;
4345
import org.apache.tuweni.units.bigints.UInt256;
44-
import org.assertj.core.api.Assertions;
4546
import org.junit.jupiter.api.BeforeEach;
4647
import org.junit.jupiter.api.Test;
4748

48-
public class Eip7709BlockHashLookupTest {
49+
class Eip7709BlockHashLookupTest {
4950
private static final long BLOCKHASH_SERVE_WINDOW = 160;
5051
private static final Address STORAGE_ADDRESS = Address.fromHexString("0x0");
5152
private static final long HISTORY_SERVE_WINDOW = 200L;
@@ -58,9 +59,9 @@ public class Eip7709BlockHashLookupTest {
5859
@BeforeEach
5960
void setUp() {
6061
headers = new ArrayList<>();
61-
frame = createMessageFrame(CURRENT_BLOCK_NUMBER, createWorldUpdater(0, CURRENT_BLOCK_NUMBER));
6262
lookup =
6363
new Eip7709BlockHashLookup(STORAGE_ADDRESS, HISTORY_SERVE_WINDOW, BLOCKHASH_SERVE_WINDOW);
64+
frame = createMessageFrame(CURRENT_BLOCK_NUMBER, createWorldUpdater(0, CURRENT_BLOCK_NUMBER));
6465
}
6566

6667
private WorldUpdater createWorldUpdater(final int fromBlockNumber, final int toBlockNumber) {
@@ -84,6 +85,8 @@ private MessageFrame createMessageFrame(
8485
final BlockValues blockValues = mock(BlockValues.class);
8586
when(blockValues.getNumber()).thenReturn(currentBlockNumber);
8687
when(messageFrame.getBlockValues()).thenReturn(blockValues);
88+
when(messageFrame.getBlockHashLookup()).thenReturn(lookup);
89+
when(messageFrame.getRemainingGas()).thenReturn(10_000_000L);
8790
when(messageFrame.getWorldUpdater()).thenReturn(worldUpdater);
8891
return messageFrame;
8992
}
@@ -100,7 +103,7 @@ void shouldGetHashOfMaxBlocksBehind() {
100103

101104
@Test
102105
void shouldReturnEmptyHashWhenRequestedBlockHigherThanHead() {
103-
assertThat(lookup.apply(frame, CURRENT_BLOCK_NUMBER + 20L)).isEqualTo(Hash.ZERO);
106+
assertHashForBlockNumber(CURRENT_BLOCK_NUMBER + 20, Hash.ZERO);
104107
}
105108

106109
@Test
@@ -138,12 +141,9 @@ void shouldCacheBlockHashes() {
138141
assertHashForBlockNumber(blockNumber3);
139142
assertHashForBlockNumber(blockNumber3);
140143

141-
verify(account, times(1))
142-
.getStorageValue(eq(UInt256.valueOf(blockNumber1 % HISTORY_SERVE_WINDOW)));
143-
verify(account, times(1))
144-
.getStorageValue(eq(UInt256.valueOf(blockNumber2 % HISTORY_SERVE_WINDOW)));
145-
verify(account, times(1))
146-
.getStorageValue(eq(UInt256.valueOf(blockNumber3 % HISTORY_SERVE_WINDOW)));
144+
verify(account, times(1)).getStorageValue(UInt256.valueOf(blockNumber1 % HISTORY_SERVE_WINDOW));
145+
verify(account, times(1)).getStorageValue(UInt256.valueOf(blockNumber2 % HISTORY_SERVE_WINDOW));
146+
verify(account, times(1)).getStorageValue(UInt256.valueOf(blockNumber3 % HISTORY_SERVE_WINDOW));
147147
verifyNoMoreInteractions(account);
148148
}
149149

@@ -177,7 +177,14 @@ private void assertHashForBlockNumber(final int blockNumber) {
177177
}
178178

179179
private void assertHashForBlockNumber(final int blockNumber, final Hash hash) {
180-
Assertions.assertThat(lookup.apply(frame, (long) blockNumber)).isEqualTo(hash);
180+
clearInvocations(frame);
181+
182+
BlockHashOperation op = new BlockHashOperation(new CancunGasCalculator());
183+
when(frame.popStackItem()).thenReturn(Bytes.ofUnsignedInt(blockNumber));
184+
185+
op.execute(frame, null);
186+
187+
verify(frame).pushStackItem(hash);
181188
}
182189

183190
private BlockHeader createHeader(final long blockNumber, final BlockHeader parentHeader) {

ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,9 +271,7 @@ private void traceTestSpecs(final String test, final List<GeneralStateTestCaseEi
271271
transaction,
272272
blockHeader.getCoinbase(),
273273
(__, blockNumber) ->
274-
blockNumber >= blockHeader.getNumber()
275-
? Hash.ZERO
276-
: Hash.hash(Bytes.wrap(Long.toString(blockNumber).getBytes(UTF_8))),
274+
Hash.hash(Bytes.wrap(Long.toString(blockNumber).getBytes(UTF_8))),
277275
false,
278276
TransactionValidationParams.processingBlock(),
279277
tracer,

ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/T8nExecutor.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -395,13 +395,8 @@ static T8nResult runTest(
395395
// from them so need to
396396
// add in a manual BlockHashLookup
397397
blockHashLookup =
398-
(__, blockNumber) -> {
399-
if (referenceTestEnv.getNumber() - blockNumber > 256L
400-
|| blockNumber >= referenceTestEnv.getNumber()) {
401-
return Hash.ZERO;
402-
}
403-
return referenceTestEnv.getBlockhashByNumber(blockNumber).orElse(Hash.ZERO);
404-
};
398+
(__, blockNumber) ->
399+
referenceTestEnv.getBlockhashByNumber(blockNumber).orElse(Hash.ZERO);
405400
}
406401
result =
407402
processor.processTransaction(

evm/src/main/java/org/hyperledger/besu/evm/blockhash/BlockHashLookup.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,14 @@
2525
* <p>Arg is the current executing message frame. The Result is the Hash, which may be zero based on
2626
* lookup rules.
2727
*/
28-
public interface BlockHashLookup extends BiFunction<MessageFrame, Long, Hash> {}
28+
public interface BlockHashLookup extends BiFunction<MessageFrame, Long, Hash> {
29+
30+
/**
31+
* How far back from the current block are hash queries valid for? Default is 256.
32+
*
33+
* @return The number of blocks before the current that should return a hash value.
34+
*/
35+
default long getLookback() {
36+
return 256L;
37+
}
38+
}

evm/src/main/java/org/hyperledger/besu/evm/operation/BlockHashOperation.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
import org.hyperledger.besu.datatypes.Hash;
1818
import org.hyperledger.besu.evm.EVM;
1919
import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
20+
import org.hyperledger.besu.evm.frame.BlockValues;
2021
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
2122
import org.hyperledger.besu.evm.frame.MessageFrame;
2223
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
2324

2425
import org.apache.tuweni.bytes.Bytes;
26+
import org.apache.tuweni.bytes.Bytes32;
2527

2628
/** The Block hash operation. */
2729
public class BlockHashOperation extends AbstractOperation {
@@ -50,9 +52,21 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) {
5052
return new OperationResult(cost, null);
5153
}
5254

55+
final long soughtBlock = blockArg.toLong();
56+
final BlockValues blockValues = frame.getBlockValues();
57+
final long currentBlockNumber = blockValues.getNumber();
5358
final BlockHashLookup blockHashLookup = frame.getBlockHashLookup();
54-
final Hash blockHash = blockHashLookup.apply(frame, blockArg.toLong());
55-
frame.pushStackItem(blockHash);
59+
60+
// If the current block is the genesis block or the sought block is
61+
// not within the lookback window, zero is returned.
62+
if (currentBlockNumber == 0
63+
|| soughtBlock >= currentBlockNumber
64+
|| soughtBlock < (currentBlockNumber - blockHashLookup.getLookback())) {
65+
frame.pushStackItem(Bytes32.ZERO);
66+
} else {
67+
final Hash blockHash = blockHashLookup.apply(frame, soughtBlock);
68+
frame.pushStackItem(blockHash);
69+
}
5670

5771
return new OperationResult(cost, null);
5872
}

0 commit comments

Comments
 (0)