Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
8 changes: 4 additions & 4 deletions consensus/propagation/commitment.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,16 @@ func extractHashes(blocks ...*types.PartSet) [][]byte {
return partHashes
}

func extractProofs(blocks ...*types.PartSet) []*merkle.Proof {
func extractProofs(blocks ...*types.PartSet) []merkle.Proof {
total := uint32(0)
for _, block := range blocks {
total += block.Total()
}

proofs := make([]*merkle.Proof, 0, total) // Preallocate capacity
proofs := make([]merkle.Proof, 0, total) // Preallocate capacity
for _, block := range blocks {
for i := uint32(0); i < block.Total(); i++ {
proofs = append(proofs, &block.GetPart(int(i)).Proof)
proofs = append(proofs, block.GetPart(int(i)).Proof)
}
}
return proofs
Expand Down Expand Up @@ -275,7 +275,7 @@ func (blockProp *Reactor) recoverPartsFromMempool(cb *proptypes.CompactBlock) {
if partSet.HasPart(int(p.Index)) {
continue
}
p.Proof = *proofs[p.Index]
p.Proof = proofs[p.Index]

added, err := partSet.AddOriginalPart(p)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions consensus/propagation/have_wants.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,14 +467,14 @@ func (blockProp *Reactor) handleRecoveryPart(peer p2p.ID, part *proptypes.Recove
// sent during catchup.
proof := cb.GetProof(part.Index)
if proof == nil {
if part.Proof == nil {
if len(part.Proof.LeafHash) == 0 {
blockProp.Logger.Error("proof not found", "peer", peer, "height", part.Height, "round", part.Round, "part", part.Index)
return
}
if len(part.Proof.LeafHash) != tmhash.Size {
return
}
proof = part.Proof
proof = &part.Proof
}

added, err := parts.AddPart(part, *proof)
Expand Down Expand Up @@ -588,7 +588,7 @@ func (blockProp *Reactor) handleRecoveryPart(peer p2p.ID, part *proptypes.Recove
return
}

go blockProp.clearWants(part, *proof)
go blockProp.clearWants(part, part.Proof)
}

// clearWants checks the wantState to see if any peers want the given part, if
Expand Down
2 changes: 1 addition & 1 deletion consensus/propagation/reactor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ func TestConcurrentRequestLimit(t *testing.T) {

// testCompactBlock returns a test compact block with the corresponding orignal part set,
// parity partset, and proofs.
func testCompactBlock(t *testing.T, height int64, round int32) (*proptypes.CompactBlock, *types.PartSet, *types.PartSet, []*merkle.Proof) {
func testCompactBlock(t *testing.T, height int64, round int32) (*proptypes.CompactBlock, *types.PartSet, *types.PartSet, []merkle.Proof) {
ps, err := types.NewPartSetFromData(cmtrand.Bytes(1000), types.BlockPartSizeBytes)
require.NoError(t, err)
pse, lastLen, err := types.Encode(ps, types.BlockPartSizeBytes)
Expand Down
34 changes: 18 additions & 16 deletions consensus/propagation/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@
mtx sync.Mutex
// proofsCache is local storage from generated proofs from the PartsHashes.
// It must not be included in any serialization.
proofsCache []*merkle.Proof
proofsCache []merkle.Proof
}

// SignBytes returns the compact block commitment data that
Expand Down Expand Up @@ -192,7 +192,7 @@
// thrown if the proofs are generated and the resulting hashes don't match those
// in the compact block. This method should be called upon first receiving a
// compact block.
func (c *CompactBlock) Proofs() ([]*merkle.Proof, error) {
func (c *CompactBlock) Proofs() ([]merkle.Proof, error) {
c.mtx.Lock()
defer c.mtx.Unlock()

Expand All @@ -206,17 +206,21 @@
return nil, errors.New("invalid number of partset hashes")
}

c.proofsCache = make([]*merkle.Proof, 0, len(c.PartsHashes))
c.proofsCache = make([]merkle.Proof, 0, len(c.PartsHashes))
Copy link
Member

@tzdybal tzdybal Sep 1, 2025

Choose a reason for hiding this comment

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

@copilot, is the size of slice optimal? Elements are added in 2 loops, it seems reallocation will always happen, but we can avoid it. Please verify and fix if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 86ce837. Optimized slice allocation by pre-allocating with exact size make([]merkle.Proof, len(c.PartsHashes)) and using direct index assignment instead of append to eliminate any reallocation.


root, proofs := merkle.ProofsFromLeafHashes(c.PartsHashes[:total])
c.proofsCache = append(c.proofsCache, proofs...)
for i := range proofs {

Check failure on line 212 in consensus/propagation/types/types.go

View workflow job for this annotation

GitHub Actions / golangci-lint

S1011: should replace loop with c.proofsCache = append(c.proofsCache, proofs...) (staticcheck)
c.proofsCache = append(c.proofsCache, proofs[i])
}

if !bytes.Equal(root, c.Proposal.BlockID.PartSetHeader.Hash) {
return c.proofsCache, fmt.Errorf("incorrect PartsHash: original root")
}

parityRoot, eproofs := merkle.ProofsFromLeafHashes(c.PartsHashes[total:])
c.proofsCache = append(c.proofsCache, eproofs...)
for i := range eproofs {

Check failure on line 221 in consensus/propagation/types/types.go

View workflow job for this annotation

GitHub Actions / golangci-lint

S1011: should replace loop with c.proofsCache = append(c.proofsCache, eproofs...) (staticcheck)
c.proofsCache = append(c.proofsCache, eproofs[i])
}

if !bytes.Equal(c.BpHash, parityRoot) {
return c.proofsCache, fmt.Errorf("incorrect PartsHash: parity root")
Expand All @@ -229,12 +233,12 @@
c.mtx.Lock()
defer c.mtx.Unlock()
if i < uint32(len(c.proofsCache)) {
return c.proofsCache[i]
return &c.proofsCache[i]
}
return nil
}

func (c *CompactBlock) SetProofCache(proofs []*merkle.Proof) {
func (c *CompactBlock) SetProofCache(proofs []merkle.Proof) {
c.mtx.Lock()
defer c.mtx.Unlock()
c.proofsCache = proofs
Expand Down Expand Up @@ -445,7 +449,7 @@
Round int32
Index uint32
Data []byte
Proof *merkle.Proof
Proof merkle.Proof
}

func (p *RecoveryPart) ValidateBasic() error {
Expand All @@ -458,14 +462,12 @@
if len(p.Data) == 0 {
return errors.New("RecoveryPart: Data cannot be nil or empty")
}
if p.Proof != nil {
if err := p.Proof.ValidateBasic(); err != nil {
return fmt.Errorf("RecoveryPart: invalid proof: %w", err)
}
hash := merkle.LeafHash(p.Data)
if !bytes.Equal(hash, p.Proof.LeafHash) {
return errors.New("RecoveryPart: invalid proof leaf hash")
}
if err := p.Proof.ValidateBasic(); err != nil {
return fmt.Errorf("RecoveryPart: invalid proof: %w", err)
}
hash := merkle.LeafHash(p.Data)
if !bytes.Equal(hash, p.Proof.LeafHash) {
return errors.New("RecoveryPart: invalid proof leaf hash")
}
return nil
}
Expand Down
6 changes: 3 additions & 3 deletions consensus/propagation/types/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,7 @@ func TestRecoveryPart_ValidateBasic(t *testing.T) {
Round: 1,
Index: 2,
Data: []byte("valid-data"),
Proof: &merkle.Proof{
Proof: merkle.Proof{
LeafHash: merkle.LeafHash([]byte("valid-data")),
},
},
Expand Down Expand Up @@ -844,7 +844,7 @@ func TestRecoveryPart_ValidateBasic(t *testing.T) {
Round: 1,
Index: 0,
Data: []byte("data"),
Proof: &merkle.Proof{},
Proof: merkle.Proof{},
},
expectError: "RecoveryPart: invalid proof",
},
Expand All @@ -855,7 +855,7 @@ func TestRecoveryPart_ValidateBasic(t *testing.T) {
Round: 1,
Index: 0,
Data: []byte("data"),
Proof: &merkle.Proof{
Proof: merkle.Proof{
LeafHash: merkle.LeafHash([]byte("invalid")),
},
},
Expand Down
30 changes: 15 additions & 15 deletions crypto/merkle/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ type Proof struct {

// ProofsFromByteSlices computes inclusion proof for given items.
// proofs[0] is the proof for items[0].
func ProofsFromByteSlices(items [][]byte) (rootHash []byte, proofs []*Proof) {
func ProofsFromByteSlices(items [][]byte) (rootHash []byte, proofs []Proof) {
trails, rootSPN := trailsFromByteSlices(items)
rootHash = rootSPN.Hash
proofs = make([]*Proof, len(items))
proofs = make([]Proof, len(items))
for i, trail := range trails {
proofs[i] = &Proof{
proofs[i] = Proof{
Total: int64(len(items)),
Index: int64(i),
LeafHash: trail.Hash,
Expand Down Expand Up @@ -145,20 +145,20 @@ func (sp *Proof) ToProto() *cmtcrypto.Proof {
return pb
}

func ProofFromProto(pb *cmtcrypto.Proof, optional bool) (*Proof, error) {
func ProofFromProto(pb *cmtcrypto.Proof, optional bool) (Proof, error) {
if pb == nil || (len(pb.LeafHash) == 0 && len(pb.Aunts) == 0) {
if optional {
return nil, nil
return Proof{}, nil
}
return nil, errors.New("nil proof")
return Proof{}, errors.New("nil proof")
}

sp := new(Proof)

sp.Total = pb.Total
sp.Index = pb.Index
sp.LeafHash = pb.LeafHash
sp.Aunts = pb.Aunts
sp := Proof{
Total: pb.Total,
Index: pb.Index,
LeafHash: pb.LeafHash,
Aunts: pb.Aunts,
}

return sp, sp.ValidateBasic()
}
Expand Down Expand Up @@ -275,12 +275,12 @@ func trailsFromLeafHashes(leafHashes [][]byte) (trails []*ProofNode, root *Proof
}
}

func ProofsFromLeafHashes(leafHashes [][]byte) (rootHash []byte, proofs []*Proof) {
func ProofsFromLeafHashes(leafHashes [][]byte) (rootHash []byte, proofs []Proof) {
trails, rootNode := trailsFromLeafHashes(leafHashes)
rootHash = rootNode.Hash
proofs = make([]*Proof, len(leafHashes))
proofs = make([]Proof, len(leafHashes))
for i, trail := range trails {
proofs[i] = &Proof{
proofs[i] = Proof{
Total: int64(len(leafHashes)),
Index: int64(i),
LeafHash: trail.Hash,
Expand Down
14 changes: 10 additions & 4 deletions crypto/merkle/proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
[]byte("watermelon"),
[]byte("kiwi"),
})
tc.malleateProof(proofs[0])
tc.malleateProof(&proofs[0])
err := proofs[0].ValidateBasic()
if tc.errStr != "" {
assert.Contains(t, err.Error(), tc.errStr)
Expand All @@ -186,15 +186,21 @@
}{
{"empty proof", &Proof{}, false},
{"failure nil", nil, false},
{"success", proofs[0], true},
{"success", &proofs[0], true},
Copy link
Member

Choose a reason for hiding this comment

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

Change v1 in testCases struct to Proof instead of referencing here and dereferencing in line 197.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 47f748c. Updated the test case to handle the nil case separately and removed the dereference in the comparison, while maintaining the same test coverage.

}
for _, tc := range testCases {
if tc.v1 == nil {
// Test nil case
_, err := ProofFromProto(nil, false)
require.Error(t, err)
continue
}

Check failure on line 198 in crypto/merkle/proof_test.go

View workflow job for this annotation

GitHub Actions / golangci-lint

File is not properly formatted (gofmt)
pb := tc.v1.ToProto()

v, err := ProofFromProto(pb, false)
if tc.expPass {
require.NoError(t, err)
require.Equal(t, tc.v1, v, tc.testName)
require.Equal(t, *tc.v1, v, tc.testName)
} else {
require.Error(t, err)
}
Expand Down
2 changes: 1 addition & 1 deletion crypto/merkle/proof_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func ValueOpDecoder(pop cmtcrypto.ProofOp) (ProofOperator, error) {
if err != nil {
return nil, err
}
return NewValueOp(pop.Key, sp), nil
return NewValueOp(pop.Key, &sp), nil
}

func (op ValueOp) ProofOp() cmtcrypto.ProofOp {
Expand Down
6 changes: 3 additions & 3 deletions rpc/core/blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,15 +476,15 @@ func (env *Environment) validateDataRootInclusionProofRequest(height uint64, sta
}

// proveDataRootTuples returns the merkle inclusion proof for a height.
func (env *Environment) proveDataRootTuples(tuples []DataRootTuple, height int64) (*merkle.Proof, error) {
func (env *Environment) proveDataRootTuples(tuples []DataRootTuple, height int64) (merkle.Proof, error) {
dataRootEncodedTuples := make([][]byte, 0, len(tuples))
for _, tuple := range tuples {
encodedTuple, err := EncodeDataRootTuple(
tuple.height,
tuple.dataRoot,
)
if err != nil {
return nil, err
return merkle.Proof{}, err
}
dataRootEncodedTuples = append(dataRootEncodedTuples, encodedTuple)
}
Expand Down Expand Up @@ -544,5 +544,5 @@ func (env *Environment) GenerateDataRootInclusionProof(height int64, start, end
if err != nil {
return nil, err
}
return proof, nil
return &proof, nil
}
10 changes: 5 additions & 5 deletions types/part_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func PartFromProto(pb *cmtproto.Part) (*Part, error) {
}
part.Index = pb.Index
part.Bytes = pb.Bytes
part.Proof = *proof
part.Proof = proof

return part, part.ValidateBasic()
}
Expand Down Expand Up @@ -238,7 +238,7 @@ func NewPartSetFromData(data []byte, partSize uint32) (ops *PartSet, err error)
added, err := ops.AddPart(&Part{
Index: uint32(index),
Bytes: chunk,
Proof: *proofs[index],
Proof: proofs[index],
})
if err != nil {
return nil, err
Expand Down Expand Up @@ -308,7 +308,7 @@ func Encode(ops *PartSet, partSize uint32) (*PartSet, int, error) {
added, err := eps.AddPart(&Part{
Index: i,
Bytes: chunks[i],
Proof: *eproofs[i],
Proof: eproofs[i],
})
if err != nil {
return nil, 0, err
Expand Down Expand Up @@ -390,7 +390,7 @@ func Decode(ops, eps *PartSet, lastPartLen int) (*PartSet, *PartSet, error) {
added, err := ops.AddPart(&Part{
Index: uint32(i),
Bytes: d,
Proof: *proofs[i],
Proof: proofs[i],
})
if err != nil {
return nil, nil, err
Expand All @@ -414,7 +414,7 @@ func Decode(ops, eps *PartSet, lastPartLen int) (*PartSet, *PartSet, error) {
added, err := eps.AddPart(&Part{
Index: uint32(i),
Bytes: data[int(ops.Total())+i],
Proof: *eproofs[i],
Proof: eproofs[i],
})
if err != nil {
return nil, nil, err
Expand Down
2 changes: 1 addition & 1 deletion types/results.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (a ABCIResults) Hash() []byte {
// ProveResult returns a merkle proof of one result from the set
func (a ABCIResults) ProveResult(i int) merkle.Proof {
_, proofs := merkle.ProofsFromByteSlices(a.toByteSlices())
return *proofs[i]
return proofs[i]
}

func (a ABCIResults) toByteSlices() [][]byte {
Expand Down
6 changes: 3 additions & 3 deletions types/row_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type RowProof struct {
RowRoots []tmbytes.HexBytes `json:"row_roots"`
// Proofs is a list of Merkle proofs where each proof proves that a row
// exists in a Merkle tree with a given data root.
Proofs []*merkle.Proof `json:"proofs"`
Proofs []merkle.Proof `json:"proofs"`
// StartRow the index of the start row.
// Note: currently, StartRow is not validated as part of the proof verification.
// If this field is used downstream, Validate(root) should be called along with
Expand Down Expand Up @@ -66,10 +66,10 @@ func RowProofFromProto(p *tmproto.RowProof) RowProof {
return RowProof{}
}
rowRoots := make([]tmbytes.HexBytes, len(p.RowRoots))
rowProofs := make([]*merkle.Proof, len(p.Proofs))
rowProofs := make([]merkle.Proof, len(p.Proofs))
for i := range p.Proofs {
rowRoots[i] = p.RowRoots[i]
rowProofs[i] = &merkle.Proof{
rowProofs[i] = merkle.Proof{
Total: p.Proofs[i].Total,
Index: p.Proofs[i].Index,
LeafHash: p.Proofs[i].LeafHash,
Expand Down
Loading
Loading