-
Notifications
You must be signed in to change notification settings - Fork 264
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 flattenNamespacedEDS
#338
Conversation
This comment has been minimized.
This comment has been minimized.
This is cool!
The test I had in mind would basically create a block, call block.Hash() and PutBlock and validate that nothing in that block changed. Or maybe even simpler calling block.DataAvailabilityHeader.Hash() twice by resetting the hash field(s) and see if both hashes match. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
And here is the most minimal test I could come up with, that also shows the problem: type preprocessingApp struct {
types.BaseApplication
}
func (app *preprocessingApp) PreprocessTxs(
req types.RequestPreprocessTxs) types.ResponsePreprocessTxs {
time.Sleep(time.Second * 2)
randTxs := generateRandTxs(64, 256)
randMsgs := generateRandNamespacedRawData(128, nmt.DefaultNamespaceIDLen, 256)
randMessages := toMessageSlice(randMsgs)
return types.ResponsePreprocessTxs{Txs: append(req.Txs, randTxs...), Messages: &tmproto.Messages{MessagesList: randMessages}}
}
func generateRandTxs(num int, size int) [][]byte {
randMsgs := generateRandNamespacedRawData(num, nmt.DefaultNamespaceIDLen, size)
for _, msg := range randMsgs {
copy(msg[:nmt.DefaultNamespaceIDLen], TxNamespaceID)
}
return randMsgs
}
func toMessageSlice(msgs [][]byte) []*tmproto.Message {
res := make([]*tmproto.Message, len(msgs))
for i := 0; i < len(msgs); i++ {
res[i] = &tmproto.Message{NamespaceId: msgs[i][:nmt.DefaultNamespaceIDLen], Data: msgs[i][nmt.DefaultNamespaceIDLen:]}
}
return res
}
func TestDataAvailabilityHeaderRewriteBug2(t *testing.T) {
ipfsNode, err := coremock.NewMockNode()
if err != nil {
t.Error(err)
}
ipfsAPI, err := coreapi.NewCoreAPI(ipfsNode)
if err != nil {
t.Error(err)
}
txs := Txs{}
l := len(txs)
bzs := make([][]byte, l)
for i := 0; i < l; i++ {
bzs[i] = txs[i]
}
app := &preprocessingApp{}
// See state.CreateProposalBlock to understand why we do this here:
processedBlockTxs := app.PreprocessTxs(types.RequestPreprocessTxs{bzs})
ppt := processedBlockTxs.GetTxs()
pbmessages := processedBlockTxs.GetMessages()
lp := len(ppt)
processedTxs := make(Txs, lp)
if lp > 0 {
for i := 0; i < l; i++ {
processedTxs[i] = ppt[i]
}
}
messages := MessagesFromProto(pbmessages)
lastID := makeBlockIDRandom()
h := int64(3)
voteSet, _, vals := randVoteSet(h-1, 1, tmproto.PrecommitType, 10, 1)
commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals, time.Now())
assert.NoError(t, err)
block := MakeBlock(1, processedTxs, nil, nil, messages, commit)
block.Hash()
hash1 := block.DataAvailabilityHeader.Hash()
ctx := context.TODO()
err = block.PutBlock(ctx, ipfsAPI.Dag())
if err != nil {
t.Fatal(err)
}
block.fillDataAvailabilityHeader()
hash2 := block.DataAvailabilityHeader.Hash()
assert.Equal(t, hash1, hash2)
} |
Co-authored-by: Ismail Khoffi <Ismail.Khoffi@gmail.com>
// create the nmt wrapper to generate row and col commitments | ||
squareSize := uint32(math.Sqrt(float64(len(shares)))) | ||
tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(squareSize)) | ||
|
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 major reason why we had to move the wrapper and constants into their own packages.
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 do think that once we move DAHedear calculation to p2p/ipld
we can take the wrapper back to where it exists now in master.
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.
Having one more package for wrapper makes no sense for me in general
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.
Same for consts actually
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.
From experience I have in Golang: If I have an import cycle, it means I messed up proper logic segregation between packages architecturally, and simply extracting specific the piece of code causing the cycle is a simple hack, not a complete solution.
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 think you are right about the import cycles and extraction into a separate package @Wondertan (in this case, not necessarily in general).
IMO, we can simply revisit this when we move the computation of the DAHeader into PutBlock.
I'm still trying to debug why the e2e test is failing after merging the recent IPFS refactor with this PR. It was passing on both before merging. The good (?!) news is that at least this time it's failing locally as well as in the CI. |
This comment has been minimized.
This comment has been minimized.
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 looks much better and consistently uses the same API in fillDAheader and PutBlock 👍🏼 great Change!
There is absolutely nothing related to the e2e tests really. So I assume they are still just a bit flaky and we still need to increase some timeouts. but this is orthogonal to this PR.
copy(out, ParitySharesNamespaceID) | ||
return out | ||
} | ||
|
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 good!
// TODO(ismail): for better efficiency and a larger number shares | ||
// we should switch to the rsmt2d.LeopardFF16 codec: | ||
extendedDataSquare, err := rsmt2d.ComputeExtendedDataSquare(shares, rsmt2d.NewRSGF8Codec(), rsmt2d.NewDefaultTree) | ||
extendedDataSquare, err := rsmt2d.ComputeExtendedDataSquare(shares, rsmt2d.NewRSGF8Codec(), tree.Constructor) |
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 starts to remind me Java 😬
extendedAbstractFactoryForWrappedCodedDataSqaure...
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.
Do you have any concrete suggestion how to make this more go-idiomatic then? Also, I don't think it's too java-like tbh.
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.
Yep, renaming it to eds
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.
yeah, I'm not the biggest fan of in object data processing either. 🤷♂️ We have two nested layers of abstraction here. I prefer everything in an explicit pipeline. We discussed this specific topic a few times already, and we decided to leave it as is. ref
if it works, it works
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.
Yep, renaming it to eds
Yeah, I'm not opposed to short variable names if the code is obvious, a few lines long (the smaller the var's scope the shorter the var name could be). Here I'd say it is a matter of taste mostly. I think the name extendedDataSquare
has little in common with extendedAbstractFactoryForWrappedCodedDataSqaure
.
I prefer everything in an explicit pipeline.
Me too but I was also in favour of retiring rsmt2d and make just one concrete instantiation of it (with the only tree we want and everything we need) inside of this repo (one function creates the eds, another returns the row/col roots, done). For various reasons that were resolved for the most part in the meantime. Let's not get distracted by this right now.
// create the nmt wrapper to generate row and col commitments | ||
squareSize := uint32(math.Sqrt(float64(len(shares)))) | ||
tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(squareSize)) | ||
|
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.
Having one more package for wrapper makes no sense for me in general
// create the nmt wrapper to generate row and col commitments | ||
squareSize := uint32(math.Sqrt(float64(len(shares)))) | ||
tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(squareSize)) | ||
|
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.
Same for consts actually
// create the nmt wrapper to generate row and col commitments | ||
squareSize := uint32(math.Sqrt(float64(len(shares)))) | ||
tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(squareSize)) | ||
|
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.
From experience I have in Golang: If I have an import cycle, it means I messed up proper logic segregation between packages architecturally, and simply extracting specific the piece of code causing the cycle is a simple hack, not a complete solution.
return res | ||
} | ||
|
||
func TestDataAvailabilityHeaderRewriteBug(t *testing.T) { |
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.
👍🏻
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.
From experience I have in Golang: If I have an import cycle, it means I messed up proper logic segregation between packages architecturally, and simply extracting specific the piece of code causing the cycle is a simple hack, not a complete solution.
same. It's a "code smell" for sure. I think I'll leave it the way it is for now, but we could totally move the contents of the wrapper
package to the types
package. Besides a massive refactor, I don't think we have other better options. I'm certainly not attached to the current structure by any means, and am open to suggestions.
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 I added just a bunch of minor thoughts, but overall looks good!
Description
This PR removes
flattenNamespacedEDS
and instead uses the nmt wrapper. It should probably wait to be merged until after #334, as that PR will simplify things even further by movingPutBlock
. However, this is not strictly necessary.In order to successfully close #336, this PR still needs to include a test to ensure that this actually fixes the issue plaguing the light client in #323.
Closes: #336