Skip to content

Followup pre populate chain validity window #2012

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

Merged
merged 39 commits into from
May 16, 2025

Conversation

Elvis339
Copy link
Contributor

@Elvis339 Elvis339 commented Mar 28, 2025

This PR is closing #2003 by addressing several issues related to the validity window initialization flow.

  1. Improved Validity Window API:

    • Added NewPopulatedTimeValidityWindow as a factory method to create and populate a validity window in one step
    • Made populateValidityWindow private (formerly PopulateValidityWindow) to reduce API surface area
    • Simplified timestamp comparison logic within populateValidityWindow
  2. Cleaner VM Initialization Flow:

    • Removed the circular dependency between chain store and validity window
  3. Validity Window Initialization

    • Enforce populated validity window before starting normal operation
    • Save historical blocks on disk

Elvis S added 2 commits March 27, 2025 22:53
* Add NewPopulatedTimeValidityWindow for direct initialization
* Simplify validity window population with consistent timestamp comparison
* Improve VM initialization flow to handle validity window during restart
* Unify calculateOldestAllowed function implementation
* Update component initialization order to break circular dependencies
@Elvis339 Elvis339 self-assigned this Mar 28, 2025
@Elvis339 Elvis339 requested a review from aaronbuchwald as a code owner March 28, 2025 10:52
@Elvis339
Copy link
Contributor Author

#2005 (comment) ->

// backfillFromExisting attempts to build a validity window from existing blocks

#2005 (comment) ->

oldestAllowed = v.calculateOldestAllowed(block.GetTimestamp())

#2005 (comment) ->

if vm.consensusIndex != nil {

#2005 (comment) ->

if err == database.ErrNotFound || !normalOp {

#2005 (comment) ->

func (vm *VM) initValidityWindow(ctx context.Context) error {

#2005 (comment) ->

slices.Reverse(parents)

RodrigoVillar
RodrigoVillar previously approved these changes Mar 28, 2025
Copy link
Contributor

@RodrigoVillar RodrigoVillar left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Collaborator

@aaronbuchwald aaronbuchwald left a comment

Choose a reason for hiding this comment

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

Can we define a function within startNormalOp (always called before transitioning to normal operation) that will return an error if the validity window does not report as fully populated yet?

Elvis S added 4 commits April 11, 2025 17:10
…p safety checks enhance VW implementation with a populated flag that tracks whether a complete validity window has been observed. Reducing API surface area
…uring consistent view of state across the network
log: log,
tracer: tracer,
chainIndex: chainIndex,
seen: emap.NewEMap[T](),
getTimeValidityWindow: getTimeValidityWindowF,
populated: false,
}
if lastAcceptedBlock != nil {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Allowing nil lastAcceptedBlock for testing purposes. While not ideal for production use, passing nil signals the creation of an empty validity window. The constructor handles this case gracefully. I'm open to improvements.

vw := NewTimeValidityWindow(ctx, log, tracer, chainIndex, nil, ...)

@Elvis339 Elvis339 requested a review from aaronbuchwald April 29, 2025 17:58
Comment on lines 59 to 68
// It maintains a record of transactions that have been seen within a
// configurable time window and rejects any duplicates.
//
// This component is critical as it:
// 1. Prevents transaction replay attacks
// 2. Enforces double-spend protection
// 3. Provides temporal validation boundaries
// 4. Maintains consensus safety across nodes i.e.;
// if different nodes had different rules for transaction uniqueness,
// they would disagree about the state of the blockchain.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we could improve this comment by focusing on what it does as opposed to how it's used. For example, enforces double-spend protection is a function of preventing replay attacks, but these are not really the same thing. Provides temporal validation boundaries is a bit vague and doesn't tell me much additional information.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// It maintains a record of transactions that have been seen within a
// configurable time window and rejects any duplicates.
//
// This component is critical as it:
// 1. Prevents transaction replay attacks
// 2. Enforces double-spend protection
// 3. Provides temporal validation boundaries
// 4. Maintains consensus safety across nodes i.e.;
// if different nodes had different rules for transaction uniqueness,
// they would disagree about the state of the blockchain.
// Maintains a window of all transactions included onchain, which have an expiry time that would
// allow them to be included a second time. Once their expiry time passes, transactions can be pruned
// from the window, since their expiry will mark them as invalid.

Copy link
Contributor Author

@Elvis339 Elvis339 May 5, 2025

Choose a reason for hiding this comment

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

I updated the comment

8d00108

RodrigoVillar
RodrigoVillar previously approved these changes May 5, 2025
Copy link
Contributor

@RodrigoVillar RodrigoVillar left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines 91 to 94
if head == nil {
return nil, fmt.Errorf("cannot construct time validity window: %w", ErrNilInitialBlock)
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we remove this check in favor of requiring that the caller passes in a fully populated value?

The nil check won't work reliably for generic types and we commonly make this assumption rather than include defensive checks, so I'd lean towards removing it.

Copy link
Contributor Author

@Elvis339 Elvis339 May 12, 2025

Choose a reason for hiding this comment

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

I agree with removing the nil check - it makes sense to follow the common practice of assuming callers pass valid values rather than adding defensive checks.

My original thinking was that having an explicit check with a clear error message would make debugging easier if we ever had an inconsistency. When something goes wrong in a system like this, having specific error messages that point directly to the source of the problem (like explicitly calling out a nil head block) can save time during troubleshooting.

But you're right that for generic types, nil checks aren't reliable, and it's better to be consistent with our approach of requiring callers to pass fully populated values. I'll remove this check.

1819aad

@Elvis339 Elvis339 enabled auto-merge (squash) May 16, 2025 13:21
@aaronbuchwald aaronbuchwald disabled auto-merge May 16, 2025 14:07
@aaronbuchwald aaronbuchwald merged commit d6a9726 into main May 16, 2025
11 checks passed
@aaronbuchwald aaronbuchwald deleted the followup-pre-populate-chain-validity-window branch May 16, 2025 14:07
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.

3 participants