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

feat!: adapt tx inclusion proofs to use share inclusion proofs #1276

Merged
merged 44 commits into from
Jan 30, 2023
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
55073d1
chore: delete txShareIndex
rootulp Jan 20, 2023
3e37857
refactor: rename stripCompactShares to stripPrefix
rootulp Jan 20, 2023
a52a05b
chore: rename to NewShareInclusionProof
rootulp Jan 20, 2023
4f0dd16
feat!: keep track of share indexes when splitting txs
rootulp Jan 20, 2023
20e3a41
fix lint
rootulp Jan 20, 2023
b48b27a
wip: test write tx
rootulp Jan 20, 2023
b6a7424
Merge branch 'main' into rp/transaction-inclusion-proofs
rootulp Jan 23, 2023
e10b250
feat: compact share splitter tracks tx share ranges
rootulp Jan 23, 2023
aee13b6
test: TestExport_txKeyToShareIndex
rootulp Jan 23, 2023
46ae8c8
extract mergeMaps
rootulp Jan 23, 2023
6d0d270
fix lint
rootulp Jan 23, 2023
8db8751
test: TxSharePosition
rootulp Jan 23, 2023
5db7631
test NewTxInclusionProof
rootulp Jan 23, 2023
5d91076
fix comment
rootulp Jan 23, 2023
210d3fb
delete unused file
rootulp Jan 23, 2023
5507f8f
wip: TestSplitTxs
rootulp Jan 23, 2023
3b8daf4
improve largeTx test case
rootulp Jan 24, 2023
fd51505
fix: shift pfbTxShare range by len of txShares
rootulp Jan 24, 2023
8f71dcc
refactor: use css.Count() for end share index
rootulp Jan 24, 2023
993f26b
refactor: rename to shareRanges
rootulp Jan 24, 2023
d491fed
Merge branch 'main' into rp/transaction-inclusion-proofs
rootulp Jan 24, 2023
bbbe88b
sanity check that raw data is in shares
rootulp Jan 24, 2023
cee9d3e
fix lint
rootulp Jan 24, 2023
71faf58
NewTxInclusionProof returns a TxProof
rootulp Jan 24, 2023
8f636ba
fix: lint
rootulp Jan 24, 2023
acce30c
Update pkg/prove/proof.go
rootulp Jan 25, 2023
1a83e81
Update pkg/shares/split_compact_shares.go
rootulp Jan 25, 2023
c413d7b
refactor: remove err return param
rootulp Jan 25, 2023
096945f
Merge branch 'main' into rp/transaction-inclusion-proofs
rootulp Jan 25, 2023
9378d29
refactor: use WriteTx
rootulp Jan 25, 2023
4e557a0
refactor: remove named return params
rootulp Jan 25, 2023
a2816b3
refactor: use shareRangeOffset
rootulp Jan 25, 2023
ea2b6f1
update to local celestia-core
rootulp Jan 26, 2023
de2e3f4
test: integ tests to use prove=true
rootulp Jan 26, 2023
d419b5f
fix godoc
rootulp Jan 26, 2023
76ee56b
chore: rename sharesProofs to shareProofs
rootulp Jan 29, 2023
2b89269
Update pkg/shares/share_splitting.go
rootulp Jan 29, 2023
24f3d42
docs: describe mergeMaps
rootulp Jan 29, 2023
2097f2f
Merge branch 'main' into rp/transaction-inclusion-proofs
rootulp Jan 29, 2023
92225b8
chore: rename variable to shareProof
rootulp Jan 29, 2023
28170a3
depend on snapshot of celestia-core
rootulp Jan 29, 2023
3b238c7
fix godoc and variable name in querier
rootulp Jan 30, 2023
be1374b
chore: upgrade to celestia-core v1.14.0-tm-v0.34.23
rootulp Jan 30, 2023
d1a8c89
chore: downgrade to nmt v0.12.0
rootulp Jan 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/estimate_square_size_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func Test_estimatePFBTxSharesUsed(t *testing.T) {
require.NoError(t, err)
txs[i] = wPFBTx
}
_, pfbTxShares := shares.SplitTxs(txs)
_, pfbTxShares, _ := shares.SplitTxs(txs)
assert.LessOrEqual(t, len(pfbTxShares), got)
})
}
Expand Down
5 changes: 2 additions & 3 deletions app/test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,7 @@ func (s *IntegrationTestSuite) TestMaxBlockSize() {

heights := make(map[int64]int)
for _, hash := range hashes {
// TODO: reenable fetching and verifying proofs
resp, err := queryTx(val.ClientCtx, hash, false)
resp, err := queryTx(val.ClientCtx, hash, true)
assert.NoError(err)
assert.NotNil(resp)
if resp == nil {
Expand Down Expand Up @@ -349,7 +348,7 @@ func (s *IntegrationTestSuite) TestShareInclusionProof() {
}

for _, hash := range hashes {
txResp, err := queryTx(val.ClientCtx, hash, false)
txResp, err := queryTx(val.ClientCtx, hash, true)
require.NoError(err)
require.Equal(abci.CodeTypeOK, txResp.TxResult.Code)

Expand Down
2 changes: 1 addition & 1 deletion app/test/std_sdk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ func (s *StandardSDKIntegrationTestSuite) TestStandardSDK() {
require.NoError(s.T(), s.cctx.WaitForNextBlock())

for _, tt := range tests {
res, err := queryTx(s.cctx.Context, tt.hash, false)
res, err := queryTx(s.cctx.Context, tt.hash, true)
require.NoError(t, err)
assert.Equal(t, abci.CodeTypeOK, res.TxResult.Code, tt.name)
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/celestiaorg/celestia-app
go 1.18

require (
github.com/celestiaorg/nmt v0.12.0
github.com/celestiaorg/nmt v0.13.0
github.com/celestiaorg/quantum-gravity-bridge v1.3.0
github.com/ethereum/go-ethereum v1.10.26
github.com/gogo/protobuf v1.3.3
Expand Down Expand Up @@ -186,5 +186,5 @@ require (
replace (
github.com/cosmos/cosmos-sdk => github.com/celestiaorg/cosmos-sdk v1.6.0-sdk-v0.46.7
github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1
github.com/tendermint/tendermint => github.com/celestiaorg/celestia-core v1.13.0-tm-v0.34.23
github.com/tendermint/tendermint => github.com/rootulp/celestia-core v1.9.0-tm-v0.34.20.0.20230129211651-baf61ed501c1
)
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -186,14 +186,12 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/celestiaorg/celestia-core v1.13.0-tm-v0.34.23 h1:nMg928RIanyA2ICN3PHpIE6jgg+008BtD5XmxeObEhM=
github.com/celestiaorg/celestia-core v1.13.0-tm-v0.34.23/go.mod h1:Iu2XuSzF+QG3rzd60/sGhhk3n4wOAm8YyadU80KlcMs=
github.com/celestiaorg/cosmos-sdk v1.6.0-sdk-v0.46.7 h1:wP4RRK9zbVBlQXqRqJXji09e9UUFpNpwL2zorHwbJhQ=
github.com/celestiaorg/cosmos-sdk v1.6.0-sdk-v0.46.7/go.mod h1:MkOIxmKiICA4d8yPWo/590S4fqpaLXfocz0xn1fxBp4=
github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 h1:CJdIpo8n5MFP2MwK0gSRcOVlDlFdQJO1p+FqdxYzmvc=
github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4/go.mod h1:fzuHnhzj1pUygGz+1ZkB3uQbEUL4htqCGJ4Qs2LwMZA=
github.com/celestiaorg/nmt v0.12.0 h1:6CmaMPri9FdSiytZP7yCrEq3ewebFiIEjlJhasrS6oQ=
github.com/celestiaorg/nmt v0.12.0/go.mod h1:NN3W8EEoospv8EHCw50DDNWwPLpJkFHoEFiqCEcNCH4=
github.com/celestiaorg/nmt v0.13.0 h1:zhuwhvICMJhXy2lzaBydFBYztTuLzV5N3xAa9zTSiCw=
github.com/celestiaorg/nmt v0.13.0/go.mod h1:b+pwd9cGTSSYLZnUIQSJl07pusJdFeEvCVsVfSRH9gA=
github.com/celestiaorg/quantum-gravity-bridge v1.3.0 h1:9zPIp7w1FWfkPnn16y3S4FpFLnQtS7rm81CUVcHEts0=
github.com/celestiaorg/quantum-gravity-bridge v1.3.0/go.mod h1:6WOajINTDEUXpSj5UZzod16UZ96ZVB/rFNKyM+Mt1gI=
github.com/celestiaorg/rsmt2d v0.8.0 h1:ZUxTCELZCM9zMGKNF3cT+rUqMddXMeiuyleSJPZ3Wn4=
Expand Down Expand Up @@ -927,6 +925,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rootulp/celestia-core v1.9.0-tm-v0.34.20.0.20230129211651-baf61ed501c1 h1:lA8Ko5gkwWCokdJo6SsbGR4XZu1JplDLrfaOfU1Dymw=
github.com/rootulp/celestia-core v1.9.0-tm-v0.34.20.0.20230129211651-baf61ed501c1/go.mod h1:Ib1QbXOT+ETe83OfolwIZP1666iCpiSTIrEjXsdah04=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
Expand Down
233 changes: 43 additions & 190 deletions pkg/proof/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,99 +22,43 @@ import (
"github.com/tendermint/tendermint/types"
)

// TxInclusion uses the provided block data to progressively generate rows
// of a data square, and then using those shares to creates nmt inclusion proofs.
// It is possible that a transaction spans more than one row. In that case, we
// have to return more than one proof.
func TxInclusion(codec rsmt2d.Codec, data types.Data, txIndex uint64) (types.TxProof, error) {
// calculate the index of the shares that contain the tx
startPos, endPos, err := TxSharePosition(data.Txs, txIndex)
// NewTxInclusionProof returns a new share inclusion proof for the given
// transaction index.
func NewTxInclusionProof(codec rsmt2d.Codec, data types.Data, txIndex uint64) (types.ShareProof, error) {
rawShares, err := shares.Split(data, true)
if err != nil {
return types.TxProof{}, err
return types.ShareProof{}, err
}

// use the index of the shares and the square size to determine the row that
// contains the tx we need to prove
startRow := startPos / data.SquareSize
endRow := endPos / data.SquareSize
startLeaf := startPos % data.SquareSize
endLeaf := endPos % data.SquareSize

rowShares, err := genRowShares(codec, data, startRow, endRow)
startShare, endShare, err := TxSharePosition(data, txIndex)
if err != nil {
return types.TxProof{}, err
return types.ShareProof{}, err
}

var proofs []*tmproto.NMTProof //nolint:prealloc // rarely will this contain more than a single proof
var rawShares [][]byte //nolint:prealloc // rarely will this contain more than a single share
var rowRoots []tmbytes.HexBytes //nolint:prealloc // rarely will this contain more than a single root
for i, row := range rowShares {
// create an nmt to use to generate a proof
tree := wrapper.NewErasuredNamespacedMerkleTree(data.SquareSize, uint(i))
for _, share := range row {
tree.Push(
share,
)
}

startLeafPos := startLeaf
endLeafPos := endLeaf

// if this is not the first row, then start with the first leaf
if i > 0 {
startLeafPos = 0
}
// if this is not the last row, then select for the rest of the row
if i != (len(rowShares) - 1) {
endLeafPos = data.SquareSize - 1
}

rawShares = append(rawShares, shares.ToBytes(row[startLeafPos:endLeafPos+1])...)
proof, err := tree.Tree().ProveRange(int(startLeafPos), int(endLeafPos+1))
if err != nil {
return types.TxProof{}, err
}

proofs = append(proofs, &tmproto.NMTProof{
Start: int32(proof.Start()),
End: int32(proof.End()),
Nodes: proof.Nodes(),
LeafHash: proof.LeafHash(),
})
namespace := getTxNamespace(data.Txs[txIndex])
return NewShareInclusionProof(rawShares, data.SquareSize, namespace, startShare, endShare)
}

// we don't store the data availability header anywhere, so we
// regenerate the roots to each row
rowRoots = append(rowRoots, tree.Root())
func getTxNamespace(tx types.Tx) (ns namespace.ID) {
_, isIndexWrapper := types.UnmarshalIndexWrapper(tx)
if isIndexWrapper {
return appconsts.PayForBlobNamespaceID
}

return types.TxProof{
RowRoots: rowRoots,
Data: rawShares,
Proofs: proofs,
}, nil
return appconsts.TxNamespaceID
}

// TxSharePosition returns the start and end positions for the shares that
// include a given txIndex. Returns an error if index is greater than the length
// of txs.
func TxSharePosition(txs types.Txs, txIndex uint64) (startSharePos, endSharePos uint64, err error) {
if txIndex >= uint64(len(txs)) {
return startSharePos, endSharePos, errors.New("transaction index is greater than the number of txs")
}

prevTxTotalLen := 0
for i := uint64(0); i < txIndex; i++ {
txLen := len(txs[i])
prevTxTotalLen += (shares.DelimLen(uint64(txLen)) + txLen)
func TxSharePosition(data types.Data, txIndex uint64) (startShare uint64, endShare uint64, err error) {
if int(txIndex) >= len(data.Txs) {
return 0, 0, errors.New("transaction index is greater than the number of txs")
}

currentTxLen := len(txs[txIndex])
currentTxTotalLen := shares.DelimLen(uint64(currentTxLen)) + currentTxLen
endOfCurrentTxLen := prevTxTotalLen + currentTxTotalLen
_, _, shareRanges := shares.SplitTxs(data.Txs)
shareRange := shareRanges[data.Txs[txIndex].Key()]

startSharePos = txShareIndex(prevTxTotalLen)
endSharePos = txShareIndex(endOfCurrentTxLen)
return startSharePos, endSharePos, nil
return uint64(shareRange.Start), uint64(shareRange.End), nil
}

// BlobShareRange returns the start and end positions for the shares
Expand Down Expand Up @@ -158,126 +102,35 @@ func BlobShareRange(tx types.Tx) (beginShare uint64, endShare uint64, err error)
return beginShare, beginShare + uint64(sharesUsed) - 1, nil
}

// txShareIndex returns the index of the compact share that would contain
// transactions with totalTxLen
func txShareIndex(totalTxLen int) (index uint64) {
if totalTxLen <= appconsts.FirstCompactShareContentSize {
return 0
}

index++
totalTxLen -= appconsts.FirstCompactShareContentSize

for totalTxLen > appconsts.ContinuationCompactShareContentSize {
index++
totalTxLen -= appconsts.ContinuationCompactShareContentSize
}
return index
}

// genRowShares progessively generates data square rows from block data
func genRowShares(codec rsmt2d.Codec, data types.Data, startRow, endRow uint64) ([][]shares.Share, error) {
if endRow > data.SquareSize {
return nil, errors.New("cannot generate row shares past the original square size")
}
origRowShares := splitIntoRows(
data.SquareSize,
genOrigRowShares(data, startRow, endRow),
)

encodedRowShares := make([][]shares.Share, len(origRowShares))
for i, row := range origRowShares {
encRow, err := codec.Encode(shares.ToBytes(row))
if err != nil {
panic(err)
}
encodedRowShares[i] = append(
append(
make([]shares.Share, 0, len(row)+len(encRow)),
row...,
), shares.FromBytes(encRow)...,
)
}

return encodedRowShares, nil
}

// genOrigRowShares progressively generates data square rows for the original
// data square, meaning the rows only half the full square length, as there is
// not erasure data
func genOrigRowShares(data types.Data, startRow, endRow uint64) []shares.Share {
wantLen := (endRow + 1) * data.SquareSize
startPos := startRow * data.SquareSize

rawTxShares, pfbTxShares := shares.SplitTxs(data.Txs)
rawShares := append(rawTxShares, pfbTxShares...)
// return if we have enough shares
if uint64(len(rawShares)) >= wantLen {
return rawShares[startPos:wantLen]
}

for _, blob := range data.Blobs {
blobShares, err := shares.SplitBlobs(0, nil, []types.Blob{blob}, false)
if err != nil {
panic(err)
}

// TODO: does this need to account for padding between compact shares
// and the first blob?
// https://github.com/celestiaorg/celestia-app/issues/1226
rawShares = append(rawShares, blobShares...)

// return if we have enough shares
if uint64(len(rawShares)) >= wantLen {
return rawShares[startPos:wantLen]
}
}

tailShares := shares.TailPaddingShares(int(wantLen) - len(rawShares))
rawShares = append(rawShares, tailShares...)

return rawShares[startPos:wantLen]
}

// splitIntoRows splits shares into rows of a particular square size
func splitIntoRows(squareSize uint64, s []shares.Share) [][]shares.Share {
rowCount := uint64(len(s)) / squareSize
rows := make([][]shares.Share, rowCount)
for i := uint64(0); i < rowCount; i++ {
rows[i] = s[i*squareSize : (i+1)*squareSize]
}
return rows
}

// GenerateSharesInclusionProof generates an nmt inclusion proof for a set of shares to the data root.
// NewShareInclusionProof returns an NMT inclusion proof for a set of shares to the data root.
// Expects the share range to be pre-validated.
// Note: only supports inclusion proofs for shares belonging to the same namespace.
func GenerateSharesInclusionProof(
func NewShareInclusionProof(
allRawShares []shares.Share,
squareSize uint64,
namespaceID namespace.ID,
startShare uint64,
endShare uint64,
) (types.SharesProof, error) {
) (types.ShareProof, error) {
startRow := startShare / squareSize
endRow := endShare / squareSize
startLeaf := startShare % squareSize
endLeaf := endShare % squareSize

eds, err := da.ExtendShares(squareSize, shares.ToBytes(allRawShares))
if err != nil {
return types.SharesProof{}, err
return types.ShareProof{}, err
}

edsRowRoots := eds.RowRoots()

// create the binary merkle inclusion proof for all the square rows to the data root
_, allProofs := merkle.ProofsFromByteSlices(append(edsRowRoots, eds.ColRoots()...))
rowsProofs := make([]*merkle.Proof, endRow-startRow+1)
rowsRoots := make([]tmbytes.HexBytes, endRow-startRow+1)
rowProofs := make([]*merkle.Proof, endRow-startRow+1)
rowRoots := make([]tmbytes.HexBytes, endRow-startRow+1)
for i := startRow; i <= endRow; i++ {
rowsProofs[i-startRow] = allProofs[i]
rowsRoots[i-startRow] = edsRowRoots[i]
rowProofs[i-startRow] = allProofs[i]
rowRoots[i-startRow] = edsRowRoots[i]
}

// get the extended rows containing the shares.
Expand All @@ -286,7 +139,7 @@ func GenerateSharesInclusionProof(
rows[i-startRow] = shares.FromBytes(eds.Row(uint(i)))
}

var sharesProofs []*tmproto.NMTProof //nolint:prealloc
var shareProofs []*tmproto.NMTProof //nolint:prealloc
var rawShares [][]byte
for i, row := range rows {
// create an nmt to generate a proof.
Expand All @@ -313,31 +166,31 @@ func GenerateSharesInclusionProof(
rawShares = append(rawShares, shares.ToBytes(row[startLeafPos:endLeafPos+1])...)
proof, err := tree.Tree().ProveRange(int(startLeafPos), int(endLeafPos+1))
if err != nil {
return types.SharesProof{}, err
return types.ShareProof{}, err
}

sharesProofs = append(sharesProofs, &tmproto.NMTProof{
shareProofs = append(shareProofs, &tmproto.NMTProof{
Start: int32(proof.Start()),
End: int32(proof.End()),
Nodes: proof.Nodes(),
LeafHash: proof.LeafHash(),
})

// make sure that the generated root is the same as the eds row root.
if !bytes.Equal(rowsRoots[i].Bytes(), tree.Root()) {
return types.SharesProof{}, errors.New("eds row root is different than tree root")
if !bytes.Equal(rowRoots[i].Bytes(), tree.Root()) {
return types.ShareProof{}, errors.New("eds row root is different than tree root")
}
}

return types.SharesProof{
RowsProof: types.RowsProof{
RowsRoots: rowsRoots,
Proofs: rowsProofs,
StartRow: uint32(startRow),
EndRow: uint32(endRow),
return types.ShareProof{
RowProof: types.RowProof{
RowRoots: rowRoots,
Proofs: rowProofs,
StartRow: uint32(startRow),
EndRow: uint32(endRow),
},
Data: rawShares,
SharesProofs: sharesProofs,
NamespaceID: namespaceID,
Data: rawShares,
ShareProofs: shareProofs,
NamespaceID: namespaceID,
}, nil
}
Loading