Skip to content

Build block immediately when transitioning epoch - #514

Merged
yacovm merged 3 commits into
mainfrom
waitForPendingMSM
Aug 20, 2026
Merged

Build block immediately when transitioning epoch#514
yacovm merged 3 commits into
mainfrom
waitForPendingMSM

Conversation

@yacovm

@yacovm yacovm commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

@yacovm
yacovm marked this pull request as draft August 14, 2026 18:00
This commit adds WaitForPendingBlock() for the MSM that intercepts the VM's WaitForPendingBlock call
and returns early in case the VM has no pending transaction but an epoch change needs to occur or is taking place.

Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>
@yacovm
yacovm force-pushed the waitForPendingMSM branch from 83f6cd3 to 7d3b99a Compare August 19, 2026 23:53
@yacovm
yacovm marked this pull request as ready for review August 19, 2026 23:53
Comment thread msm/msm.go Outdated
sealingBlock, finalization, err = sm.GetBlock(sealingBlockSeq, common.Digest{})
// The only two reasons why we would not be able to retrieve the sealing block are:
// (1) The sealing block is finalized and there is a storage failure, or
// (2) The sealing block is not finalized yet, in which case we are still transitioning to a new epoch.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this comment is not true, if we have advanced to a round caused by a telock then we have verified the sealing block. This means it's in the cached storage and GetBlock should return it regardless of finalization status.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The block is in the cache, correct. However, notice that we fetch it with a wildcard digest (any digest) becauseit might be buried several Telocks under.

When we fetch by a wildcard digest, we iterate over the cache and we might return a block that is on a different chain than we intended to.

That's why I said that only if it's finalized this does not happen, because then we will delete the block from the cache.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Slightly changed the code, please check, I think it's more clear now: 8d8d5e3

Comment thread msm/msm.go Outdated
// The only two reasons why we would not be able to retrieve the sealing block are:
// (1) The sealing block is finalized and there is a storage failure, or
// (2) The sealing block is not finalized yet, in which case we are still transitioning to a new epoch.
// Either way and in case the sealing block isn't finalized, we shouldn't wait indefinitely for the VM to build a block.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why shouldn't we wait indefinetly? if the sealing block is not finalized, the epoch will either keep broadcasting empty votes to advance the round or it will eventually notarize/finalize it.

If we empty notarize the round, then the next block that will be proposed may be another sealing block. So I think waiting like normal is fine here, the BlockBuilder & epoch should properly cancel the WaitForPendingBlock context?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

why shouldn't we wait indefinitely? if the sealing block is not finalized, the epoch will either keep broadcasting empty votes to advance the round or it will eventually notarize/finalize it.

If we wait indefinitely and the leader is stuck, we will not send an empty vote, right?

Comment thread msm/msm.go Outdated
// (2) The sealing block is not finalized yet, in which case we are still transitioning to a new epoch.
// Either way and in case the sealing block isn't finalized, we shouldn't wait indefinitely for the VM to build a block.
if err != nil || sealingBlock.Type() != BlockTypeSealing || finalization == nil {
if err != nil {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

can we move this error outside of the if?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Comment thread msm/msm.go
Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>
Comment thread msm/msm.go Outdated
Comment on lines +309 to +310
// If the sealing block isn't finalized, we need to build a Telock immediately to extend the epoch,
// so we wait up to MaxBlockBuildingWaitTime and then return.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this comment doesn't make sense. It says we need to build a telock immediately, but then we wait up to MaxBlockBuildingWaitTime.

Also, why do we need to wait? Telocks don't have any inner blocks

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

yeah you're right we don't need to wait in such a case.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@samliok samliok left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

looks good to me, the function seems a bit complex & can stray away if we ever change things about BuildBlock, but i'm not sure how to make it cleaner

Comment thread msm/msm.go
currentState := parentBlock.Metadata.SimplexEpochInfo.NextState()

// We first check if we have obvious signs that we need to build a block immediately:
var shouldObviouslyBuildBlockImmediately bool

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

no action but seems weird naming a variable with shouldObviously..... It should be obvious via the code, not the variable name 🤷

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

so, anything that isn't shouldObviouslyBuildBlockImmediately == true means we might need to epoch change but we might not.

Comment thread msm/msm_test.go

// blockingBlockBuilder waits in WaitForPendingBlock until it is handed a pending block, the way a
// real VM waits on its mempool. The notification is consumed by a single waiter.
type blockingBlockBuilder struct {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

i think we have this in testutil.TestBlockBuilder

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

they have a different interface, we can't re-use it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

BuildBlock(context.Context, uint64) (avalanchego.VMBlock, error)

vs

BuildBlock(_ context.Context, metadata common.ProtocolMetadata, blacklist common.Blacklist) (common.VerifiedBlock, bool)

Comment thread msm/msm.go Outdated
pChainReferenceHeight := parentBlock.Metadata.SimplexEpochInfo.PChainReferenceHeight
if parentBlock.Type() == BlockTypeSealing {
// We've moved to a new epoch, so we need to use the next P-chain reference height of the sealing block,
// because the P-chain reference height is of the next epoch is inherited from the P-chain reference height of the sealing block.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// because the P-chain reference height is of the next epoch is inherited from the P-chain reference height of the sealing block.
// because the P-chain reference height of the next epoch is inherited from the P-chain reference height of the sealing block.

Comment thread msm/msm_test.go Outdated
Comment on lines +1978 to +1979
// TestMSMWaitForPendingBlock checks when WaitForPendingBlock stops waiting for the VM. It must
// return on its if BuildBlock would have produced a block.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// TestMSMWaitForPendingBlock checks when WaitForPendingBlock stops waiting for the VM. It must
// return on its if BuildBlock would have produced a block.
// TestMSMWaitForPendingBlock checks when WaitForPendingBlock stops waiting for the VM. It must
// return on its own if BuildBlock would have produced a block.

Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>
@yacovm
yacovm merged commit 77183df into main Aug 20, 2026
7 checks passed
samliok pushed a commit that referenced this pull request Aug 21, 2026
* Build block immediately when transitioning epoch

This commit adds WaitForPendingBlock() for the MSM that intercepts the VM's WaitForPendingBlock call
and returns early in case the VM has no pending transaction but an epoch change needs to occur or is taking place.

Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>

* Address code review comments

Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>

* Address code review comments II

Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>

---------

Signed-off-by: Yacov Manevich <yacov.manevich@avalabs.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants