-
Notifications
You must be signed in to change notification settings - Fork 20.2k
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
all: refactor so NewBlock
, WithBody
take types.Body
#29482
Conversation
NewBlock
, NewBody
take types.Body
NewBlock
, WithBody
take types.Body
3f67698
to
23edbea
Compare
generally i think the idea is pretty good. |
Thanks @rjl493456442 and @fjl. PR updated based on your feedback. |
733e51c
to
dc5c96c
Compare
@@ -220,10 +221,20 @@ type extblock struct { | |||
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header | |||
// are ignored and set to values derived from the given txs, uncles | |||
// and receipts. | |||
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block { | |||
b := &Block{header: CopyHeader(header)} | |||
func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher) *Block { |
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.
@rjl493456442 noted that in WithBody, you passed a non-pointer Body. Either case might be fine, but maybe it should be consistent across the two methods.
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.
Given that a Body is fairly small containing only slices, and you also have this strange check below to handle nil,it might be simpler to go with the struct.
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.
Unrelated: it's a bit confusing when you should use NewBlock and when NewBlockWithHeader.WithBody. Perhaps we could consider tweaking the names of one or the other a bit to make it clearer that they do different things and serve different purposes. Otherwise IMO it's asking for it.
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.
Since we already handle Body
as a pointer value in other places, I think making it a struct in NewBlock
is going to be a bit annoying. This is seen mostly in implementations of consensus.Engine
where FinalizeAndAssemble
takes a pointer Body
.
If you think it is worth turning all uses of Body
over to struct values, I can do it. It just seems like it might actually be better to keep the pointer value given this.
Similarly with NewBlock
and WithBody
interfaces matching, if they need to match, they should probably both be *Body
.
cc @fjl since I think it was his thought to convert to struct val.
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.
Also agree on it being confusing when to use NewBlock
vs. NewBlockWithHeader.WithBody
. I think the answer is you use NewBlock
when you need to compute the header accumulators with the body values and NewBlockWithHeader.WithBody
when you need to recreate the block which has been disassembled (written to disk, received over the wire, etc).
Naming isn't perfect, but struggling to come up with something markedly better.
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 still think it's a bit non-obvious, but it might suffice to update some method-docs.
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 difference with NewBlock
is that it would deep copy all inputs. It's meant to be used in tests, and that's also why it has a short name. Across production code, very few places actually create blocks, so we can live with a longer function name and we can also avoid some of the copying.
block := types.NewBlockWithHeader(&header) | ||
block = block.WithBody(transactions, nil) | ||
block = block.WithWithdrawals(withdrawals) | ||
block := types.NewBlockWithHeader(&header).WithBody(types.Body{Transactions: transactions, Withdrawals: withdrawals}) |
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.
Why not the new NewBlock (with everything)?
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 guess you don't have the receipts to recalculate header fields.
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.
Exactly. Plus it might be a little weird to recalc the header fields when here we're trying to tease out our block def from a beacon block's execution payload. Should probably 1:1 the data and do a hash comparison somewhere to ensure integrity.
PR and general directions seems ok, just some open ended questions to debate. |
dc5c96c
to
a138dee
Compare
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.
LGTM
@@ -220,10 +221,20 @@ type extblock struct { | |||
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header | |||
// are ignored and set to values derived from the given txs, uncles | |||
// and receipts. | |||
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher TrieHasher) *Block { | |||
b := &Block{header: CopyHeader(header)} | |||
func NewBlock(header *Header, body *Body, receipts []*Receipt, hasher TrieHasher) *Block { |
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 still think it's a bit non-obvious, but it might suffice to update some method-docs.
core/types/block.go
Outdated
// WithBody returns a copy of the block with the given transaction and uncle contents. | ||
func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { | ||
block := &Block{ | ||
header: b.header, | ||
transactions: make([]*Transaction, len(transactions)), | ||
uncles: make([]*Header, len(uncles)), | ||
withdrawals: b.withdrawals, | ||
} | ||
copy(block.transactions, transactions) | ||
for i := range uncles { | ||
block.uncles[i] = CopyHeader(uncles[i]) | ||
} | ||
return block | ||
} | ||
|
||
// WithWithdrawals returns a copy of the block containing the given withdrawals. | ||
func (b *Block) WithWithdrawals(withdrawals []*Withdrawal) *Block { | ||
func (b *Block) WithBody(body Body) *Block { |
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.
Method doc should expand a bit. E.g.
// WithBody returns a new block, consisting of the orginal header (not copied) along
// with the body contents (which are deep-copied).
// OBS: The header-fields based on body derivatives (e..g txhash) are _not_ updated, and will
// mismatch if the body does not match the header.
It's a bit funny / curious that this method avoids copying the original header, but conscientiously copies the incoming body.
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.
So the idea here is, header fields inside types.Block
can't leak out because we copy their values in block accessors. We do need to copy the fields in the passed Body
here because a later modification of the input data (e.g. uncle header values) could mess up the internal caches in Block
.
This PR has a pretty subtle but scary bug, surfaced by this test (well, and many others):
Empty if withdrawals == nil {
b.header.WithdrawalsHash = nil
} else if len(withdrawals) == 0 {
b.header.WithdrawalsHash = &EmptyWithdrawalsHash
} else {
hash := DeriveSha(Withdrawals(withdrawals), hasher)
b.header.WithdrawalsHash = &hash
b.withdrawals = slices.Clone(withdrawals)
} Previously, it would (after some call dispatching), come to
|
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.
un-approved for now :)
6139f75
to
81a0ae5
Compare
That was a sneaky one! I updated so that |
Needs a rebase |
b.header.WithdrawalsHash = &h | ||
hash := DeriveSha(Withdrawals(withdrawals), hasher) | ||
b.header.WithdrawalsHash = &hash | ||
b.withdrawals = slices.Clone(withdrawals) |
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't you just do this unconditioally? IIUC, slices.Clone
respects nil
vs empty
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 ok to me
81a0ae5
to
9538959
Compare
9538959
to
bcea950
Compare
…9482) * all: refactor so NewBlock(..) and WithBody(..) take a types.Body * core: fixup comments, remove txs != receipts panic * core/types: add empty withdrawls to body if len == 0
…9482) * all: refactor so NewBlock(..) and WithBody(..) take a types.Body * core: fixup comments, remove txs != receipts panic * core/types: add empty withdrawls to body if len == 0
Something we've discussed a bit in the past; thought I would see if there is interest in it now before we look into merging PRs for 6110 and 7002 -- they both add new quantities to the body.