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

Remove flattenNamespacedEDS #338

Merged
merged 9 commits into from
May 20, 2021
Merged

Conversation

evan-forbes
Copy link
Member

@evan-forbes evan-forbes commented May 17, 2021

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 moving PutBlock. 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

@evan-forbes evan-forbes self-assigned this May 17, 2021
@codecov-commenter

This comment has been minimized.

@liamsi
Copy link
Member

liamsi commented May 17, 2021

This is cool!

this PR still needs to include a test to ensure that this actually fixes the issue plaguing the light client in #323.

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.
Here is the very hacky way with which I've noticed that calling flattenNamespacedEDS twice modified the data: https://github.com/lazyledger/lazyledger-core/blob/437b8bf0d8f90f9eab7d034f95a4b207c93a316d/types/block.go#L343-L356

@evan-forbes

This comment has been minimized.

@liamsi

This comment has been minimized.

@liamsi

This comment has been minimized.

@liamsi
Copy link
Member

liamsi commented May 19, 2021

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)

}

Comment on lines +237 to +240
// create the nmt wrapper to generate row and col commitments
squareSize := uint32(math.Sqrt(float64(len(shares))))
tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(squareSize))

Copy link
Member Author

@evan-forbes evan-forbes May 20, 2021

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.

Copy link
Member

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.

Copy link
Member

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

Copy link
Member

Choose a reason for hiding this comment

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

Same for consts actually

Copy link
Member

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.

Copy link
Member

@liamsi liamsi May 20, 2021

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.

@evan-forbes
Copy link
Member Author

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.

@evan-forbes evan-forbes marked this pull request as ready for review May 20, 2021 20:25
@evan-forbes evan-forbes requested review from Wondertan and removed request for tac0turtle May 20, 2021 20:26
@liamsi

This comment has been minimized.

Copy link
Member

@liamsi liamsi left a 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
}

Copy link
Member

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)
Copy link
Member

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...

Copy link
Member

@liamsi liamsi May 20, 2021

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.

Copy link
Member

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

Copy link
Member Author

@evan-forbes evan-forbes May 20, 2021

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

Copy link
Member

@liamsi liamsi May 20, 2021

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.

Comment on lines +237 to +240
// create the nmt wrapper to generate row and col commitments
squareSize := uint32(math.Sqrt(float64(len(shares))))
tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(squareSize))

Copy link
Member

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

Comment on lines +237 to +240
// create the nmt wrapper to generate row and col commitments
squareSize := uint32(math.Sqrt(float64(len(shares))))
tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(squareSize))

Copy link
Member

Choose a reason for hiding this comment

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

Same for consts actually

Comment on lines +237 to +240
// create the nmt wrapper to generate row and col commitments
squareSize := uint32(math.Sqrt(float64(len(shares))))
tree := wrapper.NewErasuredNamespacedMerkleTree(uint64(squareSize))

Copy link
Member

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) {
Copy link
Member

Choose a reason for hiding this comment

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

👍🏻

Copy link
Member Author

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.

Copy link
Member

@Wondertan Wondertan left a 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!

@evan-forbes evan-forbes merged commit 280cc82 into master May 20, 2021
@evan-forbes evan-forbes deleted the evan/remove-flattenNamespacedEDS branch May 20, 2021 22:24
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.

flattenNamespacedEDS overwrites some block data
4 participants