@@ -49,7 +49,7 @@ func Inclusion(index, size uint64) (Nodes, error) {
4949 if index >= size {
5050 return Nodes {}, fmt .Errorf ("index %d out of bounds for tree size %d" , index , size )
5151 }
52- return nodes (index , 0 , size ), nil
52+ return nodes (index , 0 , size ). skipFirst () , nil
5353}
5454
5555// Consistency returns the information on how to fetch and construct a
@@ -72,57 +72,59 @@ func Consistency(size1, size2 uint64) (Nodes, error) {
7272 // into this node in the tree of size2.
7373 p := nodes (index , level , size2 )
7474
75- // Handle the case when size1 is not a power of 2.
76- if index != 0 {
77- // Prepend the earlier computed node to the proof.
78- // TODO(pavelkalinnikov): This code path is invoked almost always. Avoid
79- // the extra allocation that append does.
80- p .IDs = append (p .IDs , compact.NodeID {})
81- copy (p .IDs [1 :], p .IDs )
82- p .IDs [0 ] = compact .NewNodeID (level , index )
83-
84- // Fixup the indices into the IDs slice.
85- if p .begin < p .end {
86- p .begin ++
87- p .end ++
88- }
75+ // Handle the case when size1 is a power of 2.
76+ if index == 0 {
77+ return p .skipFirst (), nil
8978 }
90-
9179 return p , nil
9280}
9381
9482// nodes returns the node IDs necessary to prove that the (level, index) node
9583// is included in the Merkle tree of the given size.
9684func nodes (index uint64 , level uint , size uint64 ) Nodes {
97- node := compact .NewNodeID (level , index )
98- begin , _ := node .Coverage ()
85+ // Compute the `fork` node, where the path from root to (level, index) node
86+ // diverges from the path to (0, size).
87+ //
88+ // The sibling of this node is the ephemeral node which represents a subtree
89+ // that is not complete in the tree of the given size. To compute the hash
90+ // of the ephemeral node, we need all the non-ephemeral nodes that cover the
91+ // same range of leaves.
92+ //
93+ // The `inner` variable is how many layers up from (level, index) the `fork`
94+ // and the ephemeral nodes are.
95+ inner := bits .Len64 (index ^ (size >> level )) - 1
96+ fork := compact .NewNodeID (level + uint (inner ), index >> inner )
9997
100- // Compute the level at which the path to leaf `begin` diverges from the path
101- // to `size`. This is where the ephemeral node is located. The ephemeral node
102- // represents a subtree that is not complete in the tree of the given size,
103- // so we instead provide the minimal list of non-ephemeral nodes which cover
104- // the same range of leaves.
105- ephemLevel := uint (bits .Len64 (begin ^ size ) - 1 )
98+ begin , end := fork .Coverage ()
99+ left := compact .RangeSize (0 , begin )
100+ right := compact .RangeSize (end , size )
101+
102+ node := compact .NewNodeID (level , index )
103+ // Pre-allocate the exact number of nodes for the proof, in order:
104+ // - The seed node for which we are building the proof.
105+ // - The `inner` nodes at each level up to the fork node.
106+ // - The `right` nodes, comprising the ephemeral node.
107+ // - The `left` nodes, completing the coverage of the whole [0, size) range.
108+ nodes := append (make ([]compact.NodeID , 0 , 1 + inner + right + left ), node )
106109
107110 // The first portion of the proof consists of the siblings for nodes of the
108111 // path going up to the level at which the ephemeral node appears.
109- // TODO(pavelkalinnikov): Pre-allocate the full capacity.
110- nodes := make ([]compact.NodeID , 0 , ephemLevel - level )
111- for ; node .Level < ephemLevel ; node = node .Parent () {
112+ for ; node .Level < fork .Level ; node = node .Parent () {
112113 nodes = append (nodes , node .Sibling ())
113114 }
114- // This portion of the proof covers the range under the reached node . The
115+ // This portion of the proof covers the range [begin, end) under it . The
115116 // ranges to the left and to the right from it remain to be covered.
116- begin , end := node .Coverage ()
117117
118118 // Add all the nodes (potentially none) that cover the right range, and
119119 // represent the ephemeral node. Reverse them so that the Rehash method can
120120 // process hashes in the convenient order, from lower to upper levels.
121121 len1 := len (nodes )
122- nodes = append (nodes , reverse (compact .RangeNodes (end , size ))... )
122+ nodes = compact .RangeNodes (end , size , nodes )
123+ reverse (nodes [len (nodes )- right :])
123124 len2 := len (nodes )
124125 // Add the nodes that cover the left range, ordered increasingly by level.
125- nodes = append (nodes , reverse (compact .RangeNodes (0 , begin ))... )
126+ nodes = compact .RangeNodes (0 , begin , nodes )
127+ reverse (nodes [len (nodes )- left :])
126128
127129 // nodes[len1:len2] contains the nodes representing the ephemeral node. If
128130 // it's empty or only has one node, make it zero.
@@ -133,7 +135,7 @@ func nodes(index uint64, level uint, size uint64) Nodes {
133135 len1 , len2 = 0 , 0
134136 }
135137
136- return Nodes {IDs : nodes , begin : len1 , end : len2 , ephem : node .Sibling ()}
138+ return Nodes {IDs : nodes , begin : len1 , end : len2 , ephem : fork .Sibling ()}
137139}
138140
139141// Ephem returns the ephemeral node, and indices begin and end, such that
@@ -173,9 +175,18 @@ func (n Nodes) Rehash(h [][]byte, hc func(left, right []byte) []byte) ([][]byte,
173175 return h [:cursor ], nil
174176}
175177
176- func reverse (ids []compact.NodeID ) []compact.NodeID {
178+ func (n Nodes ) skipFirst () Nodes {
179+ n .IDs = n .IDs [1 :]
180+ // Fixup the indices into the IDs slice.
181+ if n .begin < n .end {
182+ n .begin --
183+ n .end --
184+ }
185+ return n
186+ }
187+
188+ func reverse (ids []compact.NodeID ) {
177189 for i , j := 0 , len (ids )- 1 ; i < j ; i , j = i + 1 , j - 1 {
178190 ids [i ], ids [j ] = ids [j ], ids [i ]
179191 }
180- return ids
181192}
0 commit comments