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

When on PoS the head can be only be updated by ForkchoiceUpdate #4013

Merged
merged 39 commits into from
Jul 1, 2022
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
e5f90a0
block proposal temporary workaround to roll back executed block propo…
garyschulte Jun 7, 2022
ab920dd
Trying execute without save
gezero Jun 9, 2022
fb8f591
custom version of build
gezero Jun 9, 2022
8f660be
some fix
gezero Jun 9, 2022
8a64460
do not append block when not needed
gezero Jun 9, 2022
4a13932
Do not update chain state and world state in MergeCoordinator.execute…
fab-10 Jun 20, 2022
b7e0e34
When proposing a block, use a lightweight validation, without storing…
fab-10 Jun 20, 2022
430f3cc
Update unit test
fab-10 Jun 20, 2022
116f16c
Update CHANGELOG
fab-10 Jun 20, 2022
b02de74
Introduce forwardToBlock
fab-10 Jun 22, 2022
0b39953
Merge remote-tracking branch 'gezero/jiri-on-top-of-gary' into valida…
fab-10 Jun 22, 2022
5153211
Do not persist prepared blocks
fab-10 Jun 22, 2022
64f6fac
Merge branch 'main' into validate-without-saving2
fab-10 Jun 23, 2022
5efca4b
refactor how trie log is generated and persisted
fab-10 Jun 23, 2022
da0eeb2
clean log
fab-10 Jun 23, 2022
c2a9f3b
try safe copy
matkt Jun 24, 2022
66a7116
fix build
matkt Jun 24, 2022
d4603fb
fix nullpointer
matkt Jun 24, 2022
da3eef9
Merge remote-tracking branch 'matkt/update-protection' into validate-…
fab-10 Jun 24, 2022
b21e937
update code
matkt Jun 24, 2022
0feeedd
Version with the last modification from Karim and the use of BonsaiIn…
fab-10 Jun 24, 2022
f257282
fix build
matkt Jun 24, 2022
9fb37d6
fix build
matkt Jun 24, 2022
a6c09a9
Cleanup and fixes
fab-10 Jun 24, 2022
e57ee28
Merge remote-tracking branch 'matkt/update-protection' into validate-…
fab-10 Jun 24, 2022
c35388f
Merge branch 'main' into validate-without-saving2
fab-10 Jun 24, 2022
0acf581
spotless apply
fab-10 Jun 24, 2022
aa0ae0f
Adapt unit tests to the new newPayload FcU flow
fab-10 Jun 24, 2022
b92e3cf
Remove unused parameter
fab-10 Jun 24, 2022
f7c0d63
Merge branch 'main' into validate-without-saving2
fab-10 Jun 28, 2022
c4a527f
Fix null pointer exception
fab-10 Jun 28, 2022
42db3ce
Fix refactoring
fab-10 Jun 28, 2022
f77977c
revert updater protection pr
garyschulte Jun 28, 2022
bb85876
update bonsai worldstate archive tests
garyschulte Jun 28, 2022
f064d3b
Merge branch 'main' into validate-without-saving2
garyschulte Jun 29, 2022
065373b
Merge branch 'main' into validate-without-saving2
garyschulte Jul 1, 2022
672d097
Merge branch 'main' into validate-without-saving2
garyschulte Jul 1, 2022
4f5d203
remove jetbrains static analysis annotation
garyschulte Jul 1, 2022
c102ddc
Merge branch 'main' into validate-without-saving2
garyschulte Jul 1, 2022
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## 22.7.1

### Additions and Improvements
- Do not require a minimum block height when downloading headers or blocks [#3911](https://github.com/hyperledger/besu/pull/3911)
- When on PoS the head can be only be updated by ForkchoiceUpdate [#3994](https://github.com/hyperledger/besu/pull/3994)
- Version information available in metrics [#3997](https://github.com/hyperledger/besu/pull/3997)
- Add TTD and DNS to Sepolia config [#4024](https://github.com/hyperledger/besu/pull/4024)
- Return `type` with value `0x0` when serializing legacy transactions [#4027](https://github.com/hyperledger/besu/pull/4027)
Expand All @@ -18,7 +20,6 @@
- Support `finalized` and `safe` as tags for the block parameter in RPC APIs [#3950](https://github.com/hyperledger/besu/pull/3950)
- Added verification of payload attributes in ForkchoiceUpdated [#3837](https://github.com/hyperledger/besu/pull/3837)
- Add support for Gray Glacier hardfork [#3961](https://github.com/hyperledger/besu/issues/3961)
- Do not require a minimum block height when downloading headers or blocks [#3911](https://github.com/hyperledger/besu/pull/3911)

### Bug Fixes
- alias engine-rpc-port parameter with the former rpc param name [#3958](https://github.com/hyperledger/besu/pull/3958)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public PayloadIdentifier preparePayload(
final Block emptyBlock =
mergeBlockCreator.createBlock(Optional.of(Collections.emptyList()), random, timestamp);

Result result = executeBlock(emptyBlock);
Result result = validateBlock(emptyBlock);
if (result.blockProcessingOutputs.isPresent()) {
mergeContext.putPayloadById(payloadIdentifier, emptyBlock);
} else {
Expand All @@ -193,7 +193,7 @@ public PayloadIdentifier preparePayload(
if (throwable != null) {
LOG.warn("something went wrong creating block", throwable);
} else {
final var resultBest = executeBlock(bestBlock);
final var resultBest = validateBlock(bestBlock);
if (resultBest.blockProcessingOutputs.isPresent()) {
mergeContext.putPayloadById(payloadIdentifier, bestBlock);
} else {
Expand Down Expand Up @@ -239,7 +239,7 @@ public Optional<BlockHeader> getOrSyncHeaderByHash(
}

@Override
public Result executeBlock(final Block block) {
public Result validateBlock(final Block block) {

final var chain = protocolContext.getBlockchain();
chain
Expand All @@ -254,11 +254,14 @@ public Result executeBlock(final Block block) {
.getByBlockNumber(block.getHeader().getNumber())
.getBlockValidator()
.validateAndProcessBlock(
protocolContext, block, HeaderValidationMode.FULL, HeaderValidationMode.NONE);
protocolContext,
block,
HeaderValidationMode.FULL,
HeaderValidationMode.NONE,
false);

validationResult.blockProcessingOutputs.ifPresentOrElse(
result -> chain.appendBlock(block, result.receipts),
() ->
validationResult.errorMessage.ifPresent(
errMsg ->
protocolSchedule
.getByBlockNumber(chain.getChainHeadBlockNumber())
.getBadBlocksManager()
Expand All @@ -267,6 +270,16 @@ public Result executeBlock(final Block block) {
return validationResult;
}

@Override
public Result rememberBlock(final Block block) {
debugLambda(LOG, "Remember block {}", block::toLogString);
final var chain = protocolContext.getBlockchain();
final var validationResult = validateBlock(block);
validationResult.blockProcessingOutputs.ifPresent(
result -> chain.storeBlock(block, result.receipts));
return validationResult;
}

@Override
public ForkchoiceResult updateForkChoice(
final BlockHeader newHead,
Expand All @@ -284,8 +297,8 @@ public ForkchoiceResult updateForkChoice(
return ForkchoiceResult.withFailure(
INVALID, "new head timestamp not greater than parent", latestValid);
}
// set the new head
blockchain.rewindToBlock(newHead.getHash());

setNewHead(blockchain, newHead);

// set and persist the new finalized block if it is present
newFinalized.ifPresent(
Expand All @@ -310,6 +323,44 @@ public ForkchoiceResult updateForkChoice(
return ForkchoiceResult.withResult(newFinalized, Optional.of(newHead));
}

private boolean setNewHead(final MutableBlockchain blockchain, final BlockHeader newHead) {

if (newHead.getHash().equals(blockchain.getChainHeadHash())) {
debugLambda(LOG, "Nothing to do new head {} is already chain head", newHead::toLogString);
return true;
}

if (newHead.getParentHash().equals(blockchain.getChainHeadHash())) {
debugLambda(
LOG,
"Forwarding chain head to the block {} saved from a previous newPayload invocation",
newHead::toLogString);
forwardWorldStateTo(newHead);
return blockchain.forwardToBlock(newHead);
}

debugLambda(LOG, "New head {} is a chain reorg, rewind chain head to it", newHead::toLogString);
return blockchain.rewindToBlock(newHead.getHash());
}

private void forwardWorldStateTo(final BlockHeader newHead) {
protocolContext
.getWorldStateArchive()
.getMutable(newHead.getStateRoot(), newHead.getHash())
.ifPresentOrElse(
mutableWorldState ->
debugLambda(
LOG,
"World state for state root hash {} and block hash {} persisted successfully",
mutableWorldState::rootHash,
newHead::getHash),
() ->
LOG.error(
"Could not persist world for root hash {} and block hash {}",
newHead.getStateRoot(),
newHead.getHash()));
}

@Override
public boolean latestValidAncestorDescendsFromTerminal(final BlockHeader blockHeader) {
if (blockHeader.getNumber() <= 1L) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ PayloadIdentifier preparePayload(
final Bytes32 random,
final Address feeRecipient);

Result executeBlock(final Block block);
Result rememberBlock(final Block block);

Result validateBlock(final Block block);

ForkchoiceResult updateForkChoice(
final BlockHeader newHead,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,13 @@ public PayloadIdentifier preparePayload(
}

@Override
public Result executeBlock(final Block block) {
return mergeCoordinator.executeBlock(block);
public Result rememberBlock(final Block block) {
return mergeCoordinator.rememberBlock(block);
}

@Override
public Result validateBlock(final Block block) {
return mergeCoordinator.validateBlock(block);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,17 @@ public void coinbaseShouldMatchSuggestedFeeRecipient() {

@Test
public void childTimestampExceedsParentsFails() {

BlockHeader terminalHeader = terminalPowBlock();
coordinator.executeBlock(new Block(terminalHeader, BlockBody.empty()));
sendNewPayloadAndForkchoiceUpdate(
new Block(terminalHeader, BlockBody.empty()), Optional.empty(), Hash.ZERO);

BlockHeader parentHeader = nextBlockHeader(terminalHeader);

Block parent = new Block(parentHeader, BlockBody.empty());
coordinator.executeBlock(parent);
sendNewPayloadAndForkchoiceUpdate(parent, Optional.empty(), terminalHeader.getHash());

BlockHeader childHeader = nextBlockHeader(parentHeader, parentHeader.getTimestamp());
Block child = new Block(childHeader, BlockBody.empty());
coordinator.executeBlock(child);
coordinator.rememberBlock(child);

ForkchoiceResult result =
coordinator.updateForkChoice(
Expand All @@ -161,42 +160,39 @@ public void childTimestampExceedsParentsFails() {

@Test
public void latestValidAncestorDescendsFromTerminal() {

BlockHeader terminalHeader = terminalPowBlock();
coordinator.executeBlock(new Block(terminalHeader, BlockBody.empty()));
sendNewPayloadAndForkchoiceUpdate(
new Block(terminalHeader, BlockBody.empty()), Optional.empty(), Hash.ZERO);

BlockHeader parentHeader = nextBlockHeader(terminalHeader);

Block parent = new Block(parentHeader, BlockBody.empty());
coordinator.executeBlock(parent);
sendNewPayloadAndForkchoiceUpdate(parent, Optional.empty(), terminalHeader.getHash());

BlockHeader childHeader = nextBlockHeader(parentHeader);
Block child = new Block(childHeader, BlockBody.empty());
coordinator.executeBlock(child);
coordinator.validateBlock(child);
assertThat(this.coordinator.latestValidAncestorDescendsFromTerminal(child.getHeader()))
.isTrue();
}

@Test
public void latestValidAncestorDescendsFromFinalizedBlock() {

BlockHeader terminalHeader = terminalPowBlock();
coordinator.executeBlock(new Block(terminalHeader, BlockBody.empty()));
sendNewPayloadAndForkchoiceUpdate(
new Block(terminalHeader, BlockBody.empty()), Optional.empty(), Hash.ZERO);

BlockHeader grandParentHeader = nextBlockHeader(terminalHeader);

Block grandParent = new Block(grandParentHeader, BlockBody.empty());
coordinator.executeBlock(grandParent);
when(mergeContext.getFinalized()).thenReturn(Optional.of(grandParentHeader));
sendNewPayloadAndForkchoiceUpdate(grandParent, Optional.empty(), terminalHeader.getHash());

BlockHeader parentHeader = nextBlockHeader(grandParentHeader);

Block parent = new Block(parentHeader, BlockBody.empty());
coordinator.executeBlock(parent);
sendNewPayloadAndForkchoiceUpdate(
parent, Optional.of(grandParentHeader), grandParentHeader.getHash());

BlockHeader childHeader = nextBlockHeader(parentHeader);
Block child = new Block(childHeader, BlockBody.empty());
coordinator.executeBlock(child);
coordinator.validateBlock(child);

assertThat(this.coordinator.latestValidAncestorDescendsFromTerminal(child.getHeader()))
.isTrue();
Expand All @@ -205,25 +201,19 @@ public void latestValidAncestorDescendsFromFinalizedBlock() {

@Test
public void updateForkChoiceShouldPersistFirstFinalizedBlockHash() {

when(mergeContext.getFinalized()).thenReturn(Optional.empty());

BlockHeader terminalHeader = terminalPowBlock();
coordinator.executeBlock(new Block(terminalHeader, BlockBody.empty()));
sendNewPayloadAndForkchoiceUpdate(
new Block(terminalHeader, BlockBody.empty()), Optional.empty(), Hash.ZERO);

BlockHeader firstFinalizedHeader = nextBlockHeader(terminalHeader);
Block firstFinalizedBlock = new Block(firstFinalizedHeader, BlockBody.empty());
coordinator.executeBlock(firstFinalizedBlock);
sendNewPayloadAndForkchoiceUpdate(
firstFinalizedBlock, Optional.empty(), terminalHeader.getHash());

BlockHeader headBlockHeader = nextBlockHeader(firstFinalizedHeader);
Block headBlock = new Block(headBlockHeader, BlockBody.empty());
coordinator.executeBlock(headBlock);

coordinator.updateForkChoice(
headBlockHeader,
firstFinalizedBlock.getHash(),
firstFinalizedBlock.getHash(),
Optional.empty());
sendNewPayloadAndForkchoiceUpdate(
headBlock, Optional.of(firstFinalizedHeader), firstFinalizedHeader.getHash());

verify(blockchain).setFinalized(firstFinalizedBlock.getHash());
verify(mergeContext).setFinalized(firstFinalizedHeader);
Expand All @@ -234,27 +224,23 @@ public void updateForkChoiceShouldPersistFirstFinalizedBlockHash() {
@Test
public void updateForkChoiceShouldPersistLastFinalizedBlockHash() {
BlockHeader terminalHeader = terminalPowBlock();
coordinator.executeBlock(new Block(terminalHeader, BlockBody.empty()));
sendNewPayloadAndForkchoiceUpdate(
new Block(terminalHeader, BlockBody.empty()), Optional.empty(), Hash.ZERO);

BlockHeader prevFinalizedHeader = nextBlockHeader(terminalHeader);
Block prevFinalizedBlock = new Block(prevFinalizedHeader, BlockBody.empty());
coordinator.executeBlock(prevFinalizedBlock);

when(mergeContext.getFinalized()).thenReturn(Optional.of(prevFinalizedHeader));
sendNewPayloadAndForkchoiceUpdate(
prevFinalizedBlock, Optional.empty(), terminalHeader.getHash());

BlockHeader lastFinalizedHeader = nextBlockHeader(prevFinalizedHeader);
Block lastFinalizedBlock = new Block(lastFinalizedHeader, BlockBody.empty());
coordinator.executeBlock(lastFinalizedBlock);
sendNewPayloadAndForkchoiceUpdate(
lastFinalizedBlock, Optional.of(prevFinalizedHeader), prevFinalizedHeader.getHash());

BlockHeader headBlockHeader = nextBlockHeader(lastFinalizedHeader);
Block headBlock = new Block(headBlockHeader, BlockBody.empty());
coordinator.executeBlock(headBlock);

coordinator.updateForkChoice(
headBlockHeader,
lastFinalizedBlock.getHash(),
lastFinalizedBlock.getHash(),
Optional.empty());
sendNewPayloadAndForkchoiceUpdate(
headBlock, Optional.of(lastFinalizedHeader), lastFinalizedHeader.getHash());

verify(blockchain).setFinalized(lastFinalizedBlock.getHash());
verify(mergeContext).setFinalized(lastFinalizedHeader);
Expand Down Expand Up @@ -427,21 +413,23 @@ public void assertMergeAtGenesisSatisifiesTerminalPoW() {
@Test
public void invalidPayloadShouldReturnErrorAndUpdateForkchoiceState() {
BlockHeader terminalHeader = terminalPowBlock();
coordinator.executeBlock(new Block(terminalHeader, BlockBody.empty()));
sendNewPayloadAndForkchoiceUpdate(
new Block(terminalHeader, BlockBody.empty()), Optional.empty(), Hash.ZERO);

BlockHeader prevFinalizedHeader = nextBlockHeader(terminalHeader);
Block prevFinalizedBlock = new Block(prevFinalizedHeader, BlockBody.empty());
coordinator.executeBlock(prevFinalizedBlock);

when(mergeContext.getFinalized()).thenReturn(Optional.of(prevFinalizedHeader));
sendNewPayloadAndForkchoiceUpdate(
prevFinalizedBlock, Optional.empty(), terminalHeader.getHash());

BlockHeader lastFinalizedHeader = nextBlockHeader(prevFinalizedHeader);
Block lastFinalizedBlock = new Block(lastFinalizedHeader, BlockBody.empty());
coordinator.executeBlock(lastFinalizedBlock);

sendNewPayloadAndForkchoiceUpdate(
lastFinalizedBlock, Optional.of(prevFinalizedHeader), prevFinalizedHeader.getHash());

BlockHeader headBlockHeader = nextBlockHeader(lastFinalizedHeader);
Block headBlock = new Block(headBlockHeader, BlockBody.empty());
coordinator.executeBlock(headBlock);
assertThat(coordinator.rememberBlock(headBlock).blockProcessingOutputs).isPresent();

var res =
coordinator.updateForkChoice(
Expand All @@ -461,6 +449,23 @@ public void invalidPayloadShouldReturnErrorAndUpdateForkchoiceState() {
verify(mergeContext).setSafeBlock(lastFinalizedHeader);
}

private void sendNewPayloadAndForkchoiceUpdate(
final Block block, final Optional<BlockHeader> finalizedHeader, final Hash safeHash) {

assertThat(coordinator.rememberBlock(block).blockProcessingOutputs).isPresent();
assertThat(
coordinator
.updateForkChoice(
block.getHeader(),
finalizedHeader.map(BlockHeader::getHash).orElse(Hash.ZERO),
safeHash,
Optional.empty())
.isValid())
.isTrue();

when(mergeContext.getFinalized()).thenReturn(finalizedHeader);
}

private BlockHeader terminalPowBlock() {
return headerGenerator
.difficulty(Difficulty.MAX_VALUE)
Expand All @@ -472,6 +477,7 @@ private BlockHeader terminalPowBlock() {
genesisState.getBlock().getHeader().getBaseFee().orElse(Wei.of(0x3b9aca00)),
0,
15000000l))
.timestamp(1)
.gasLimit(genesisState.getBlock().getHeader().getGasLimit())
.stateRoot(genesisState.getBlock().getHeader().getStateRoot())
.buildHeader();
Expand Down
Loading