github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/p2p/ipld/wrapper/nmt_wrapper.go (about)

     1  package wrapper
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/lazyledger/nmt"
     8  	"github.com/lazyledger/nmt/namespace"
     9  	"github.com/lazyledger/rsmt2d"
    10  
    11  	"github.com/lazyledger/lazyledger-core/types/consts"
    12  )
    13  
    14  // emptyNamepsaceID occurs when a share is empty and indicates that
    15  var emptyNamespaceID = namespace.ID{0, 0, 0, 0, 0, 0, 0, 0}
    16  
    17  // Fulfills the rsmt2d.Tree interface and rsmt2d.TreeConstructorFn function
    18  var _ rsmt2d.TreeConstructorFn = ErasuredNamespacedMerkleTree{}.Constructor
    19  var _ rsmt2d.Tree = &ErasuredNamespacedMerkleTree{}
    20  
    21  // ErasuredNamespacedMerkleTree wraps NamespaceMerkleTree to conform to the
    22  // rsmt2d.Tree interface while also providing the correct namespaces to the
    23  // underlying NamespaceMerkleTree. It does this by adding the already included
    24  // namespace to the first half of the tree, and then uses the parity namespace
    25  // ID for each share pushed to the second half of the tree. This allows for the
    26  // namespaces to be included in the erasure data, while also keeping the nmt
    27  // library sufficiently general
    28  type ErasuredNamespacedMerkleTree struct {
    29  	squareSize uint64 // note: this refers to the width of the original square before erasure-coded
    30  	options    []nmt.Option
    31  	tree       *nmt.NamespacedMerkleTree
    32  }
    33  
    34  // NewErasuredNamespacedMerkleTree issues a new ErasuredNamespacedMerkleTree. squareSize must be greater than zero
    35  func NewErasuredNamespacedMerkleTree(squareSize uint64, setters ...nmt.Option) ErasuredNamespacedMerkleTree {
    36  	if squareSize == 0 {
    37  		panic("cannot create a ErasuredNamespacedMerkleTree of squareSize == 0")
    38  	}
    39  	tree := nmt.New(consts.NewBaseHashFunc, setters...)
    40  	return ErasuredNamespacedMerkleTree{squareSize: squareSize, options: setters, tree: tree}
    41  }
    42  
    43  // Constructor acts as the rsmt2d.TreeConstructorFn for
    44  // ErasuredNamespacedMerkleTree
    45  func (w ErasuredNamespacedMerkleTree) Constructor() rsmt2d.Tree {
    46  	newTree := NewErasuredNamespacedMerkleTree(w.squareSize, w.options...)
    47  	return &newTree
    48  }
    49  
    50  // Push adds the provided data to the underlying NamespaceMerkleTree, and
    51  // automatically uses the first DefaultNamespaceIDLen number of bytes as the
    52  // namespace unless the data pushed to the second half of the tree. Fulfills the
    53  // rsmt.Tree interface. NOTE: panics if an error is encountered while pushing or
    54  // if the tree size is exceeded.
    55  func (w *ErasuredNamespacedMerkleTree) Push(data []byte, idx rsmt2d.SquareIndex) {
    56  	// determine the namespace based on where in the tree we're pushing
    57  	nsID := make(namespace.ID, consts.NamespaceSize)
    58  
    59  	if idx.Axis+1 > 2*uint(w.squareSize) || idx.Cell+1 > 2*uint(w.squareSize) {
    60  		panic(fmt.Sprintf("pushed past predetermined square size: boundary at %d index at %+v", 2*w.squareSize, idx))
    61  	}
    62  
    63  	// use the parity namespace if the cell is not in Q0 of the extended
    64  	// datasquare if the cell is empty it means we got an empty block so we need
    65  	// to use TailPaddingNamespaceID
    66  	if idx.Axis+1 > uint(w.squareSize) || idx.Cell+1 > uint(w.squareSize) {
    67  		copy(nsID, consts.ParitySharesNamespaceID)
    68  	} else {
    69  		// empty shares use the TailPaddingNamespaceID if the data is empty, so
    70  		// here we check if the share is empty (namepsace == [0,0,0,0,0,0,0,0])
    71  		if bytes.Equal(data[:consts.NamespaceSize], emptyNamespaceID) {
    72  			copy(nsID, consts.TailPaddingNamespaceID)
    73  		} else {
    74  			copy(nsID, data[:consts.NamespaceSize])
    75  		}
    76  	}
    77  	nidAndData := append(append(make([]byte, 0, consts.NamespaceSize+len(data)), nsID...), data...)
    78  	// push to the underlying tree
    79  	err := w.tree.Push(nidAndData)
    80  	// panic on error
    81  	if err != nil {
    82  		panic(err)
    83  	}
    84  }
    85  
    86  // Prove fulfills the rsmt.Tree interface by generating and returning a single
    87  // leaf proof using the underlying NamespacedMerkleTree. NOTE: panics if the
    88  // underlying NamespaceMerkleTree errors.
    89  func (w *ErasuredNamespacedMerkleTree) Prove(
    90  	idx int,
    91  ) (merkleRoot []byte, proofSet [][]byte, proofIndex uint64, numLeaves uint64) {
    92  	proof, err := w.tree.Prove(idx)
    93  	if err != nil {
    94  		panic(err)
    95  	}
    96  	nodes := proof.Nodes()
    97  	return w.Root(), nodes, uint64(proof.Start()), uint64(len(nodes))
    98  }
    99  
   100  // Root fulfills the rsmt.Tree interface by generating and returning the
   101  // underlying NamespaceMerkleTree Root.
   102  func (w *ErasuredNamespacedMerkleTree) Root() []byte {
   103  	return w.tree.Root().Bytes()
   104  }