-
Notifications
You must be signed in to change notification settings - Fork 784
ProposerVM Epochs POC #3746
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
base: master
Are you sure you want to change the base?
ProposerVM Epochs POC #3746
Conversation
This PR has become stale because it has been open for 30 days with no activity. Adding the |
EtnaTime time.Time `json:"etnaTime"` | ||
FortunaTime time.Time `json:"fortunaTime"` | ||
GraniteTime time.Time `json:"graniteTime"` | ||
GraniteEpochDuration time.Duration `json:"graniteEpochDuration"` |
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.
shouldn't we add this to Mainnet
and Fuji
too?
@@ -59,6 +59,9 @@ type Block interface { | |||
buildChild(context.Context) (Block, error) | |||
|
|||
pChainHeight(context.Context) (uint64, error) | |||
pChainEpochHeight(context.Context) (uint64, error) |
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.
what is the use of the context here? Isn't the implementation just memory access?
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.
The Block interface ends up getting used in postForkBlock and postForkOption. postForkOption needs to get the parent block of the "option", which requires the context.
@@ -79,6 +82,51 @@ func (p *postForkCommonComponents) Height() uint64 { | |||
return p.innerBlk.Height() | |||
} | |||
|
|||
// Calculates a block's P-Chain epoch height based on its ancestor's epoch membership | |||
func (p *postForkCommonComponents) getPChainEpoch(ctx context.Context, parentID ids.ID) (uint64, uint64, time.Time, error) { | |||
parent, err := p.vm.getBlock(ctx, parentID) |
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.
what happens if we call this on the genesis block?
@@ -163,9 +212,23 @@ func (p *postForkCommonComponents) Verify( | |||
} | |||
|
|||
var contextPChainHeight uint64 | |||
if p.vm.Upgrades.IsEtnaActivated(childTimestamp) { | |||
switch { | |||
case p.vm.Upgrades.IsGraniteActivated(childTimestamp): |
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.
what happens if the parent doesn't have granite activated but the child did? How should the system behave?
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 is the epochStartTime.IsZero()
case inside getPChainEpoch
right?
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.
Yes, that's correct. We've change the behaviour to actually check for epoch 0
instead of checking the starttime
switch { | ||
case p.vm.Upgrades.IsGraniteActivated(childTimestamp): | ||
pChainEpochHeight, _, _, err := p.getPChainEpoch(ctx, child.Parent()) | ||
if err != nil { |
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.
We're checking the PchainEpochHeight is what it should be, what about the EpochNumber and EpochStartTime? shouldn't we check that too?
…into proposervm-epochs
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.
Not done yet but left some comments in the meantime
const txAmount = 10 * units.Avax // Arbitrary amount to send and transfer | ||
|
||
ginkgo.It("should advance the proposervm epoch according to the upgrade config epoch duration", func() { | ||
// TODO: Skip this test if Granite is not activated |
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.
// TODO: Skip this test if Granite is not activated
Why? Can't we force its activation in the test?
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.
We can just do upgrades.GraniteTime = upgrade.InitiallyActiveTime
and it will make it activated.
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.
upgrades.GraniteTime
gets set outside of the test. The current test suite runs all the tests once without granite activated, and once with them activated.
@@ -79,6 +82,59 @@ func (p *postForkCommonComponents) Height() uint64 { | |||
return p.innerBlk.Height() | |||
} | |||
|
|||
// Calculates a block's P-Chain epoch height based on its ancestor's epoch membership | |||
func (p *postForkCommonComponents) getPChainEpoch(ctx context.Context, parentID ids.ID) (uint64, uint64, time.Time, error) { |
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.
can we:
- add the parent block as a parameter to this function instead of retrieving it inside the method
- move the logger to be a parameter to the method
- make this a function and not an instance method, so it can be easily tested
- make a unit test that tests this method directly
?
@@ -89,6 +145,7 @@ func (p *postForkCommonComponents) Height() uint64 { | |||
// 7) [child]'s timestamp is within its proposer's window | |||
// 8) [child] has a valid signature from its proposer | |||
// 9) [child]'s inner block is valid | |||
// 10) [child] has the expected P-Chain epoch height |
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.
what about child has the expected epoch number and epoch start time?
We need to verify every field we encode.
@@ -163,9 +212,23 @@ func (p *postForkCommonComponents) Verify( | |||
} | |||
|
|||
var contextPChainHeight uint64 | |||
if p.vm.Upgrades.IsEtnaActivated(childTimestamp) { | |||
switch { | |||
case p.vm.Upgrades.IsGraniteActivated(childTimestamp): |
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 is the epochStartTime.IsZero()
case inside getPChainEpoch
right?
p.vm.ctx.Log.Error("unexpected build verification failure", | ||
zap.String("reason", "failed to get P-Chain epoch height"), | ||
zap.Stringer("parentID", child.Parent()), | ||
zap.Error(err), |
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.
don't we need to fail fast and return an error here?
ParentID: parentID, | ||
Timestamp: timestamp.Unix(), | ||
PChainHeight: pChainHeight, | ||
PChainEpochHeight: pChainEpochHeight, |
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.
These new fields are being serialized even if we haven't activated granite yet (they'll be zero), right?
Since the validators don't all upgrade the software version at the same second - how would it work? how can a validator in the current avalanchego version deserialize a block built by a validator in the new avalanchego version?
zap.Error(err), | ||
) | ||
} | ||
if childHeight := child.PChainEpochHeight(); pChainEpochHeight != childHeight { |
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.
can we extract the ICM epoch attribute validation into a separate function and then just check everything we need there, and not in this function?
zap.String("method", "getEpoch"), | ||
) | ||
|
||
lastAccepted, err := p.vm.LastAccepted(r.Context()) |
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.
Isn't this struct supposed to get accessed concurrently? I think we need some kind of synchronization if so.
zap.String("encoding", args.Encoding.String()), | ||
) | ||
|
||
block, err := p.vm.GetBlock(r.Context(), args.ProposerID) |
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.
I am not sure VM access is thread safe
import ( | ||
"testing" | ||
"time" | ||
) |
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.
seems like the tests are missing here.
Why this should be merged
Implements ProposerVM epochs as described in avalanche-foundation/ACPs#181
How this works
Adds a field
PChainEpochHeight
to proposervm blocks that remains fixed for the epoch's duration. This height is passed to the innerVM block, rather than the windowed P-Chain height.How this was tested
Added unit test for the epoch calculation, but unit/integration tests for the rest of the changed are still TODO
Need to be documented in RELEASES.md?
Yes