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

Add ExecutedBlock type #1601

Merged
merged 11 commits into from
Oct 1, 2024
Merged

Add ExecutedBlock type #1601

merged 11 commits into from
Oct 1, 2024

Conversation

aaronbuchwald
Copy link
Collaborator

This PR creates an ExecutedBlock type that includes the StatelessBlock and the execution results including both []*chain.Result and the unit prices ie fee.Dimensions. This replaces passing around chain.StatefulBlock with the new type.

Addresses #1481 and #1595

@aaronbuchwald aaronbuchwald self-assigned this Sep 28, 2024
@aaronbuchwald aaronbuchwald changed the title Executed block Add ExecutedBlock type Sep 28, 2024
@aaronbuchwald aaronbuchwald marked this pull request as ready for review September 28, 2024 16:48
chain/block.go Outdated
@@ -26,6 +26,8 @@ import (
"github.com/ava-labs/hypersdk/internal/workers"
"github.com/ava-labs/hypersdk/state"
"github.com/ava-labs/hypersdk/utils"

publicfees "github.com/ava-labs/hypersdk/fees"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we just rename the existing internal/fees to internalfees instead of calling this publicfees?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yup

chain/block.go Outdated
@@ -894,6 +896,79 @@ func (b *StatefulBlock) FeeManager() *fees.Manager {
return b.feeManager
}

func (b *StatefulBlock) ExecutedBlock() *ExecutedBlock {
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be a hot take... but I feel like ExecutedBlock being built off of a StatefulBlock looks weird to me because in my mind an ExecutedBlock is literally a copy of the state-representation of the block (i.e raw block data) but we're appending execution results onto it.

I think it would make sense to actually define the ExecutedBlock + it's creation in vm as vm.Block. It might seem like a strange place to put it but I would argue that it makes more sense there because processAcceptedBlock is defined there.

Because ExecutedBlock is also supposed to be a raw data type, should we also make this a value instead of a pointer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought about the alternative ordering as well ie. when you execute a stateful block it either produces or populates the fields on an ExecutedBlock type. Will take a stab at that and see what it looks like.

As for moving to vm.Block, I think we could definitely use a re-org of the chain/vm packages, but for now I think the new block type fits more naturally in the chain/ package.

re pointers vs. values, this is following the current style, so would prefer not to start making that change in this PR.

chain/block.go Outdated
Results []*Result `json:"results"`
UnitPrices publicfees.Dimensions `json:"unitPrices"`

blockBytes []byte
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need this field since we already have a reference to the underlying block and can call Bytes on it

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This just saves marshalling it an extra time. Will switch to doing the extra serialization

chain/block.go Outdated
UnitPrices publicfees.Dimensions `json:"unitPrices"`

blockBytes []byte
id ids.ID
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can export this can get rid of the corresponding ID() function

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can un-embed StatelessBlock and switch to requiring the caller to go through that type

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This has the annoying side effect of needing to handle the error for both Marshal() and ID() since it's not stored on the type

chain/block.go Outdated
}

type ExecutedBlock struct {
*StatelessBlock `json:"block"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Any reason for embedding this struct here? I personally dislike embedding unless we always want all exported fields/functions to be shared... this allows weird stuff like b.Size(). I personally don't have too much of an issue by copying struct fields.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Will remove

return writer.Bytes(), writer.Err()
}

func UnmarshalExecutedBlock(bytes []byte, parser Parser) (*ExecutedBlock, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: should Unmarshal have a receiver on the struct too (or Marshal be static)? Seems weird that it's not consistent.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Following current style here, happy to discuss for a separate change

Copy link
Contributor

Choose a reason for hiding this comment

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

This is new code introduced as part of the diff... would it be reasonable to do it as part of this PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All of the block marshalling / unmarshalling code is consistent with this change, so I'd rather not introduce a new style for one function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm also not sure moving it to a function on the pointer receiver is better. Either way, I don't think it belongs in this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

What are changes in this file needed for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This was to support JSON encoding/decoding of the ExecutedBlock type, which includes this type now.

@@ -104,9 +104,11 @@ func (d Dimensions) Greater(o Dimensions) bool {
return true
}

const dimensionsFormatter = "(Bandwidth=%d, Compute=%d, Storage(Read)=%d, Storage(Allocate)=%d, Storage(Write)=%d)"
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: move to top of file w/ other consts

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Updated

return writer.Bytes(), writer.Err()
}

func UnmarshalExecutedBlock(bytes []byte, parser Parser) (*ExecutedBlock, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is new code introduced as part of the diff... would it be reasonable to do it as part of this PR?

return b.feeManager
}

type ExecutedBlock struct {
Block *StatelessBlock `json:"block"`
Copy link
Contributor

Choose a reason for hiding this comment

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

I would just prefer if we manually copied out the fields in the StatelessBlock type (Parent, Timestamp, Height, Txs, StateRoot). StatelessBlock has weird signatures (e.g Size is a redundant method because you can call len on the result of Marshal, and the call to ID() returns an error but we could handle that more cleanly by just initializing the ExecutedBlock with an ID) that I want to avoid propagating to upstream code.

joshua-kim
joshua-kim previously approved these changes Oct 1, 2024
@joshua-kim joshua-kim enabled auto-merge (squash) October 1, 2024 17:31
@aaronbuchwald aaronbuchwald merged commit 59c84d7 into main Oct 1, 2024
17 checks passed
@joshua-kim joshua-kim deleted the executed-block branch October 3, 2024 15:09
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