-
Notifications
You must be signed in to change notification settings - Fork 710
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
Remove stale expiries in AdvanceTimeTo #3415
Merged
Merged
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
892aedc
Remove stale expiries in AdvanceTimeTo
StephenButtolph e6dfa27
add ref
StephenButtolph e78ed4d
merged
StephenButtolph 2ca8774
nits
StephenButtolph 37f6108
Fix race
StephenButtolph d1ba1c8
Merge branch 'master' into implement-acp-77-expiry-removal
StephenButtolph 58fc67a
Document invariant
StephenButtolph f31ea17
fix unit test
StephenButtolph c6b18ec
fix unit test
StephenButtolph File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -77,12 +77,30 @@ func VerifyNewChainTime( | |
|
||
// AdvanceTimeTo applies all state changes to [parentState] resulting from | ||
// advancing the chain time to [newChainTime]. | ||
// | ||
// Returns true iff the validator set changed. | ||
func AdvanceTimeTo( | ||
backend *Backend, | ||
parentState state.Chain, | ||
newChainTime time.Time, | ||
) (bool, error) { | ||
diff, changed, err := advanceTimeTo(backend, parentState, newChainTime) | ||
if err != nil { | ||
return false, err | ||
} | ||
return changed, diff.Apply(parentState) | ||
} | ||
|
||
// advanceTimeTo returns the state diff on top of parentState resulting from | ||
// advancing the chain time to newChainTime. It also returns a boolean | ||
// indicating if the validator set changed. | ||
// | ||
// parentState is not modified. | ||
func advanceTimeTo( | ||
backend *Backend, | ||
parentState state.Chain, | ||
newChainTime time.Time, | ||
) (state.Diff, bool, error) { | ||
// We promote pending stakers to current stakers first and remove | ||
// completed stakers from the current staker set. We assume that any | ||
// promoted staker will not immediately be removed from the current staker | ||
|
@@ -93,17 +111,21 @@ func AdvanceTimeTo( | |
|
||
changes, err := state.NewDiffOn(parentState) | ||
if err != nil { | ||
return false, err | ||
return nil, false, err | ||
} | ||
|
||
// Promote any pending stakers to current if [StartTime] <= [newChainTime]. | ||
// | ||
// Invariant: It is not safe to modify the state while iterating over it, | ||
// so we use the parentState's iterator rather than the changes iterator. | ||
// ParentState must not be modified before this iterator is released. | ||
pendingStakerIterator, err := parentState.GetPendingStakerIterator() | ||
if err != nil { | ||
return false, err | ||
return nil, false, err | ||
} | ||
defer pendingStakerIterator.Release() | ||
|
||
var changed bool | ||
// Promote any pending stakers to current if [StartTime] <= [newChainTime]. | ||
for pendingStakerIterator.Next() { | ||
stakerToRemove := pendingStakerIterator.Value() | ||
if stakerToRemove.StartTime.After(newChainTime) { | ||
|
@@ -116,7 +138,7 @@ func AdvanceTimeTo( | |
|
||
if stakerToRemove.Priority == txs.SubnetPermissionedValidatorPendingPriority { | ||
if err := changes.PutCurrentValidator(&stakerToAdd); err != nil { | ||
return false, err | ||
return nil, false, err | ||
} | ||
changes.DeletePendingValidator(stakerToRemove) | ||
changed = true | ||
|
@@ -125,12 +147,12 @@ func AdvanceTimeTo( | |
|
||
supply, err := changes.GetCurrentSupply(stakerToRemove.SubnetID) | ||
if err != nil { | ||
return false, err | ||
return nil, false, err | ||
} | ||
|
||
rewards, err := GetRewardsCalculator(backend, parentState, stakerToRemove.SubnetID) | ||
if err != nil { | ||
return false, err | ||
return nil, false, err | ||
} | ||
|
||
potentialReward := rewards.Calculate( | ||
|
@@ -147,7 +169,7 @@ func AdvanceTimeTo( | |
switch stakerToRemove.Priority { | ||
case txs.PrimaryNetworkValidatorPendingPriority, txs.SubnetPermissionlessValidatorPendingPriority: | ||
if err := changes.PutCurrentValidator(&stakerToAdd); err != nil { | ||
return false, err | ||
return nil, false, err | ||
} | ||
changes.DeletePendingValidator(stakerToRemove) | ||
|
||
|
@@ -156,16 +178,20 @@ func AdvanceTimeTo( | |
changes.DeletePendingDelegator(stakerToRemove) | ||
|
||
default: | ||
return false, fmt.Errorf("expected staker priority got %d", stakerToRemove.Priority) | ||
return nil, false, fmt.Errorf("expected staker priority got %d", stakerToRemove.Priority) | ||
} | ||
|
||
changed = true | ||
} | ||
|
||
// Remove any current stakers whose [EndTime] <= [newChainTime]. | ||
// | ||
// Invariant: It is not safe to modify the state while iterating over it, | ||
// so we use the parentState's iterator rather than the changes iterator. | ||
// ParentState must not be modified before this iterator is released. | ||
currentStakerIterator, err := parentState.GetCurrentStakerIterator() | ||
if err != nil { | ||
return false, err | ||
return nil, false, err | ||
} | ||
defer currentStakerIterator.Release() | ||
|
||
|
@@ -201,8 +227,38 @@ func AdvanceTimeTo( | |
changes.SetFeeState(feeState) | ||
} | ||
|
||
// Remove all expiries whose timestamp now implies they can never be | ||
// re-issued. | ||
// | ||
// The expiry timestamp is the time at which it is no longer valid, so any | ||
// expiry with a timestamp less than or equal to the new chain time can be | ||
// removed. | ||
// | ||
// Ref: https://github.com/avalanche-foundation/ACPs/tree/main/ACPs/77-reinventing-subnets#registersubnetvalidatortx | ||
// | ||
// The expiry iterator is sorted in order of increasing timestamp. | ||
// | ||
// Invariant: It is not safe to modify the state while iterating over it, | ||
// so we use the parentState's iterator rather than the changes iterator. | ||
// ParentState must not be modified before this iterator is released. | ||
expiryIterator, err := parentState.GetExpiryIterator() | ||
if err != nil { | ||
return nil, false, err | ||
} | ||
defer expiryIterator.Release() | ||
|
||
newChainTimeUnix := uint64(newChainTime.Unix()) | ||
for expiryIterator.Next() { | ||
expiry := expiryIterator.Value() | ||
if expiry.Timestamp > newChainTimeUnix { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (No action required) Maybe add a comment indicating that |
||
break | ||
} | ||
|
||
changes.DeleteExpiry(expiry) | ||
} | ||
|
||
changes.SetTimestamp(newChainTime) | ||
return changed, changes.Apply(parentState) | ||
return changes, changed, nil | ||
} | ||
|
||
func GetRewardsCalculator( | ||
|
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was perilously close to being an existing race.
The iterator is released in a
defer
, but the changes are applied on thereturn
line. Thedefer
is executed after thereturn
line is evaluated. Which previously broke the invariant (that is now documented). However, because we only ever passed in astate.Diff
into this function and thestate.Diff
lazily allocates thebtree
's that are iterated over, the returned iterator wasn't a tree iterator, but just explicitly empty... Which does support writes during iteration.The
expiry
tree is allocated during diff initialization, which is why this function showed a race when adding the expiry iterator.By moving the state application to the parent caller, the
defer
executes beforeApply
and there is no chance of a race.