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

Create nmt inclusion proofs for transactions in a given block #615

Merged
merged 29 commits into from
Feb 2, 2022

Conversation

evan-forbes
Copy link
Member

@evan-forbes evan-forbes commented Jan 24, 2022

Proving transactions are included in a block using nmt proofs

How this is currently done

if prove {
block := env.BlockStore.LoadBlock(height)
proof = block.Data.Txs.Proof(int(index)) // XXX: overflow on 32-bit machines
}

What we changed

var txProof types.TxProof
if proveTx {
block := env.BlockStore.LoadBlock(height)
txProof, err = prove.ProveTxInclusion(consts.DefaultCodec(), block.Data, int(block.Data.OriginalSquareSize), int(r.Index))
if err != nil {
return nil, err
}
}

celestia-core doesn't store block data in the format that we commit to, so we have to regenerate that data if we want to use it to generate a proof. Since the transactions are always in the beginning of the square, we don't have to regenerate the entire extended data square, we only have to regenerate the row that the transaction is in. We do this by calculating the position of the transaction in the square, and then progressively filling in the rest of the row by using the other block data.

// 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, originalSquareSize, startRow, endRow int) [][]byte {
wantLen := (endRow + 1) * originalSquareSize
startPos := startRow * originalSquareSize
shares := data.Txs.SplitIntoShares()
// return if we have enough shares
if len(shares) >= wantLen {
return shares[startPos:wantLen].RawShares()
}
shares = append(shares, data.IntermediateStateRoots.SplitIntoShares()...)
if len(shares) >= wantLen {
return shares[startPos:wantLen].RawShares()
}
shares = append(shares, data.Evidence.SplitIntoShares()...)
if len(shares) >= wantLen {
return shares[startPos:wantLen].RawShares()
}
for _, m := range data.Messages.MessagesList {
rawData, err := m.MarshalDelimited()
if err != nil {
panic(fmt.Sprintf("app accepted a Message that can not be encoded %#v", m))
}
shares = types.AppendToShares(shares, m.NamespaceID, rawData)
// return if we have enough shares
if len(shares) >= wantLen {
return shares[startPos:wantLen].RawShares()
}
}
tailShares := types.TailPaddingShares(wantLen - len(shares))
shares = append(shares, tailShares...)
return shares[startPos:wantLen].RawShares()
}

we also switched to using nmt proofs

// Proof represents proof of a namespace.ID in an NMT.
// In case this proof proves the absence of a namespace.ID
// in a tree it also contains the leaf hashes of the range
// where that namespace would be.
type NMTProof struct {
// start index of this proof.
Start int32 `protobuf:"varint,1,opt,name=start,proto3" json:"start,omitempty"`
// end index of this proof.
End int32 `protobuf:"varint,2,opt,name=end,proto3" json:"end,omitempty"`
// Nodes that together with the corresponding leaf values
// can be used to recompute the root and verify this proof.
Nodes [][]byte `protobuf:"bytes,3,rep,name=nodes,proto3" json:"nodes,omitempty"`
// leafHash are nil if the namespace is present in the NMT.
// In case the namespace to be proved is in the min/max range of
// the tree but absent, this will contain the leaf hash
// necessary to verify the proof of absence.
LeafHash []byte `protobuf:"bytes,4,opt,name=leaf_hash,json=leafHash,proto3" json:"leaf_hash,omitempty"`
}

celestia-core/types/tx.go

Lines 111 to 130 in 62e497a

func (tp *TxProof) VerifyProof() bool {
for i, proof := range tp.Proofs {
nmtProof := nmt.NewInclusionProof(
int(proof.Start),
int(proof.End),
proof.Nodes,
true,
)
valid := nmtProof.VerifyInclusion(
consts.NewBaseHashFunc(),
consts.TxNamespaceID,
tp.Data[i],
tp.RowRoots[i],
)
if !valid {
return false
}
}
return true
}

Closes #416

@evan-forbes evan-forbes self-assigned this Jan 24, 2022
@evan-forbes evan-forbes changed the title add ability to create nmt proofs by progressively generating the square Create nmt inclusion proofs for transactions in a given block Jan 24, 2022
@lgtm-com
Copy link

lgtm-com bot commented Jan 24, 2022

This pull request introduces 1 alert when merging 72273fd into 863a00a - view on LGTM.com

new alerts:

  • 1 for Off-by-one comparison against length

@Wondertan
Copy link
Member

Typo in file name. Currently ‘postion.go’, but I guess should be ‘position.go’

Comment on lines -30 to +31
func NewErasuredNamespacedMerkleTree(squareSize uint64, setters ...nmt.Option) ErasuredNamespacedMerkleTree {
if squareSize == 0 {
func NewErasuredNamespacedMerkleTree(origSquareSize uint64, setters ...nmt.Option) ErasuredNamespacedMerkleTree {
if origSquareSize == 0 {
Copy link
Member Author

Choose a reason for hiding this comment

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

renamed for clarity

@@ -91,6 +91,7 @@ message Data {
IntermediateStateRoots intermediate_state_roots = 2 [(gogoproto.nullable) = false];
EvidenceList evidence = 3 [(gogoproto.nullable) = false];
Messages messages = 4 [(gogoproto.nullable) = false];
uint64 original_square_size = 5;
Copy link
Member Author

Choose a reason for hiding this comment

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

Since we don't store the data in the format that we commit to or the square size for each block, we cash the original square size in the data after we generate it. This is then saved and loaded when loading the block to serve a TxProof request.

We can change this in the future by saving adding things to BlockMeta or something similar.

Comment on lines 218 to 222
message TxProof {
bytes root_hash = 1;
bytes data = 2;
tendermint.crypto.Proof proof = 3;
repeated bytes row_roots = 1;
repeated bytes data = 2;
repeated NMTProof proofs = 3;
}
Copy link
Member Author

@evan-forbes evan-forbes Jan 26, 2022

Choose a reason for hiding this comment

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

It's possible for a transaction to span multiple rows, and therefore we now need to potentially prove two or more proofs

Comment on lines +1183 to +1188
func (msgs *Messages) sortMessages() {
sort.Slice(msgs.MessagesList, func(i, j int) bool {
return bytes.Compare(msgs.MessagesList[i].NamespaceID, msgs.MessagesList[j].NamespaceID) < 0
})
}

Copy link
Member Author

@evan-forbes evan-forbes Jan 26, 2022

Choose a reason for hiding this comment

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

must have forgot to pull this from #546, which is very important to stop the node from panicking when attempting to generate the data availability header. Otherwise, it's possible to to order message so that their namespaces are not in lexicographical order. tbh, idek how devnet is still alive

@@ -52,105 +49,6 @@ func TestTxIndexByHash(t *testing.T) {
}
}

func TestValidTxProof(t *testing.T) {
Copy link
Member Author

Choose a reason for hiding this comment

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

deleted this because I also deleted the old way to create proofs for txs. We can keep both though if we want.

@evan-forbes evan-forbes marked this pull request as ready for review January 27, 2022 00:06
@evan-forbes evan-forbes requested review from liamsi, Wondertan, adlerjohn and rach-id and removed request for tac0turtle January 27, 2022 00:06
Comment on lines +1 to +2
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: tendermint/abci/types.proto
Copy link
Member Author

Choose a reason for hiding this comment

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

idk how this was 15,098 loc. looking into this

return nil
}

func (tp TxProof) ToProto() tmproto.TxProof {
func (tp *TxProof) VerifyProof() bool {
Copy link
Member Author

Choose a reason for hiding this comment

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

will change this to take a slice of row roots as args, and not use the roots already stored in the TxProof

@evan-forbes evan-forbes merged commit f613b1a into v0.34.x-celestia Feb 2, 2022
@evan-forbes evan-forbes deleted the evan/prove-tx-inclusion branch February 2, 2022 17:25
evan-forbes added a commit that referenced this pull request Apr 29, 2022
* add ability to create nmt proofs by progressively generating the square

* rename var to be more specific

* fix file name change

* core logic and testing

* export needed function

* export needed function

* generate nmtProof proto files

* update remaining tests

* update proto files

* update test to use new format for verifying proofs

* make tx proofs more ergonomic and handle multiple proofs

* integrate into rpc core

* cache original square size

* move proving logic to pkg

* add original square size

* change name of function to TxInclusion

* typo

* linter

* better docs

Co-authored-by: John Adler <[email protected]>

* use passed codec arg instead of using the default

Co-authored-by: John Adler <[email protected]>

* use already existing constant

* return error for txSharePosition

* bubble error instead of panicking

* use uint cause it's the right thing to do even tho go fights you

* include the lengths of bytes in the nmt proofs

* use passed codec
Co-authored-by: John Adler <[email protected]>

* add comment to better explain division

* linter

Co-authored-by: John Adler <[email protected]>
evan-forbes added a commit that referenced this pull request May 3, 2022
* add ability to create nmt proofs by progressively generating the square

* rename var to be more specific

* fix file name change

* core logic and testing

* export needed function

* export needed function

* generate nmtProof proto files

* update remaining tests

* update proto files

* update test to use new format for verifying proofs

* make tx proofs more ergonomic and handle multiple proofs

* integrate into rpc core

* cache original square size

* move proving logic to pkg

* add original square size

* change name of function to TxInclusion

* typo

* linter

* better docs

Co-authored-by: John Adler <[email protected]>

* use passed codec arg instead of using the default

Co-authored-by: John Adler <[email protected]>

* use already existing constant

* return error for txSharePosition

* bubble error instead of panicking

* use uint cause it's the right thing to do even tho go fights you

* include the lengths of bytes in the nmt proofs

* use passed codec
Co-authored-by: John Adler <[email protected]>

* add comment to better explain division

* linter

Co-authored-by: John Adler <[email protected]>
evan-forbes added a commit that referenced this pull request May 3, 2022
* add ability to create nmt proofs by progressively generating the square

* rename var to be more specific

* fix file name change

* core logic and testing

* export needed function

* export needed function

* generate nmtProof proto files

* update remaining tests

* update proto files

* update test to use new format for verifying proofs

* make tx proofs more ergonomic and handle multiple proofs

* integrate into rpc core

* cache original square size

* move proving logic to pkg

* add original square size

* change name of function to TxInclusion

* typo

* linter

* better docs

Co-authored-by: John Adler <[email protected]>

* use passed codec arg instead of using the default

Co-authored-by: John Adler <[email protected]>

* use already existing constant

* return error for txSharePosition

* bubble error instead of panicking

* use uint cause it's the right thing to do even tho go fights you

* include the lengths of bytes in the nmt proofs

* use passed codec
Co-authored-by: John Adler <[email protected]>

* add comment to better explain division

* linter

Co-authored-by: John Adler <[email protected]>
evan-forbes added a commit that referenced this pull request May 3, 2022
* add ability to create nmt proofs by progressively generating the square

* rename var to be more specific

* fix file name change

* core logic and testing

* export needed function

* export needed function

* generate nmtProof proto files

* update remaining tests

* update proto files

* update test to use new format for verifying proofs

* make tx proofs more ergonomic and handle multiple proofs

* integrate into rpc core

* cache original square size

* move proving logic to pkg

* add original square size

* change name of function to TxInclusion

* typo

* linter

* better docs

Co-authored-by: John Adler <[email protected]>

* use passed codec arg instead of using the default

Co-authored-by: John Adler <[email protected]>

* use already existing constant

* return error for txSharePosition

* bubble error instead of panicking

* use uint cause it's the right thing to do even tho go fights you

* include the lengths of bytes in the nmt proofs

* use passed codec
Co-authored-by: John Adler <[email protected]>

* add comment to better explain division

* linter

Co-authored-by: John Adler <[email protected]>
williambanfield pushed a commit to interchainio/celestia-core that referenced this pull request Jul 14, 2022
…iaorg#615)

* add ability to create nmt proofs by progressively generating the square

* rename var to be more specific

* fix file name change

* core logic and testing

* export needed function

* export needed function

* generate nmtProof proto files

* update remaining tests

* update proto files

* update test to use new format for verifying proofs

* make tx proofs more ergonomic and handle multiple proofs

* integrate into rpc core

* cache original square size

* move proving logic to pkg

* add original square size

* change name of function to TxInclusion

* typo

* linter

* better docs

Co-authored-by: John Adler <[email protected]>

* use passed codec arg instead of using the default

Co-authored-by: John Adler <[email protected]>

* use already existing constant

* return error for txSharePosition

* bubble error instead of panicking

* use uint cause it's the right thing to do even tho go fights you

* include the lengths of bytes in the nmt proofs

* use passed codec
Co-authored-by: John Adler <[email protected]>

* add comment to better explain division

* linter

Co-authored-by: John Adler <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Archived in project
Development

Successfully merging this pull request may close these issues.

3 participants