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 }