Skip to content

Commit a27fc03

Browse files
committed
use nil in InternalNode to turn it into a stateless node
1 parent 64f0e0b commit a27fc03

File tree

4 files changed

+120
-22
lines changed

4 files changed

+120
-22
lines changed

doc.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var (
3636
errNotSupportedInStateless = errors.New("not implemented in stateless")
3737
errInsertIntoOtherStem = errors.New("insert splits a stem where it should not happen")
3838
errStatelessAndStatefulMix = errors.New("a stateless node should not be found in a stateful tree")
39+
errMissingNodeInStateless = errors.New("trying to access a node that is missing from the stateless view")
3940
)
4041

4142
const (

proof_ipa.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -335,23 +335,23 @@ func TreeFromProof(proof *Proof, rootC *Point) (VerkleNode, error) {
335335
}
336336
}
337337

338-
root := NewStatelessWithCommitment(rootC)
338+
root := NewStatelessInternal().(*InternalNode)
339+
root.commitment = rootC
339340
comms := proof.Cs
340341
for _, p := range paths {
341-
comms, err = root.insertStem(p, info[string(p)], comms)
342-
if err != nil {
343-
return nil, err
344-
}
345-
}
342+
values := make([][]byte, NodeWidth)
343+
for i, k := range proof.Keys {
344+
if len(proof.Values[i]) == 0 {
345+
// Skip the nil keys, they are here to prove
346+
// an absence.
347+
continue
348+
}
346349

347-
for i, k := range proof.Keys {
348-
if len(proof.Values[i]) == 0 {
349-
// Skip the nil keys, they are here to prove
350-
// an absence.
351-
continue
350+
if bytes.Equal(k, info[string(p)].stem) {
351+
values[k[31]] = proof.Values[i]
352+
}
352353
}
353-
354-
err = root.insertValue(k, proof.Values[i])
354+
comms, err = root.CreatePath(p, info[string(p)], comms, values)
355355
if err != nil {
356356
return nil, err
357357
}

stateless_test.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ func TestStatelessToDot(t *testing.T) {
250250
}
251251
}
252252

253+
// TODO move this to proof_test
253254
func TestStatelessDeserialize(t *testing.T) {
254255
root := New()
255256
for _, k := range [][]byte{zeroKeyTest, oneKeyTest, fourtyKeyTest, ffx32KeyTest} {
@@ -278,11 +279,11 @@ func TestStatelessDeserialize(t *testing.T) {
278279
t.Fatalf("differing root commitments %x != %x", droot.Commitment().Bytes(), root.Commitment().Bytes())
279280
}
280281

281-
if !Equal(droot.(*StatelessNode).children[0].(*StatelessNode).commitment, root.(*InternalNode).children[0].Commit()) {
282+
if !Equal(droot.(*InternalNode).children[0].(*LeafNode).commitment, root.(*InternalNode).children[0].Commit()) {
282283
t.Fatal("differing commitment for child #0")
283284
}
284285

285-
if !Equal(droot.(*StatelessNode).children[64].Commit(), root.(*InternalNode).children[64].Commit()) {
286+
if !Equal(droot.(*InternalNode).children[64].Commit(), root.(*InternalNode).children[64].Commit()) {
286287
t.Fatal("differing commitment for child #64")
287288
}
288289
}
@@ -313,13 +314,14 @@ func TestStatelessDeserializeMissginChildNode(t *testing.T) {
313314
if !Equal(droot.Commit(), root.Commit()) {
314315
t.Fatal("differing root commitments")
315316
}
316-
317-
if !Equal(droot.(*StatelessNode).children[0].Commit(), root.(*InternalNode).children[0].Commit()) {
317+
t.Log(ToDot(root))
318+
t.Log(ToDot(droot))
319+
if !Equal(droot.(*InternalNode).children[0].Commit(), root.(*InternalNode).children[0].Commit()) {
318320
t.Fatal("differing commitment for child #0")
319321
}
320322

321-
if droot.(*StatelessNode).children[64] != nil {
322-
t.Fatal("non-nil child #64")
323+
if droot.(*InternalNode).children[64] != nil {
324+
t.Fatalf("non-nil child #64: %v", droot.(*InternalNode).children[64])
323325
}
324326
}
325327

@@ -351,7 +353,7 @@ func TestStatelessDeserializeDepth2(t *testing.T) {
351353
t.Fatal("differing root commitments")
352354
}
353355

354-
if !Equal(droot.(*StatelessNode).children[0].Commit(), root.(*InternalNode).children[0].Commit()) {
356+
if !Equal(droot.(*InternalNode).children[0].Commit(), root.(*InternalNode).children[0].Commit()) {
355357
t.Fatal("differing commitment for child #0")
356358
}
357359
}

tree.go

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,12 @@ func New() VerkleNode {
196196
return newInternalNode(0)
197197
}
198198

199+
func NewStatelessInternal() VerkleNode {
200+
node := new(InternalNode)
201+
node.children = make([]VerkleNode, NodeWidth)
202+
return node
203+
}
204+
199205
// New creates a new leaf node
200206
func NewLeafNode(stem []byte, values [][]byte) *LeafNode {
201207
cfg := GetConfig()
@@ -296,6 +302,11 @@ func (n *InternalNode) InsertStem(stem []byte, values [][]byte, resolver NodeRes
296302
n.cowChild(nChild)
297303

298304
switch child := n.children[nChild].(type) {
305+
case nil:
306+
// Finding nil in a tree means that this is a stateless tree, so
307+
// some information is missing. Typically, this means that the proof
308+
// is incorrect, or that there is a bug in tree reconstruction.
309+
return errMissingNodeInStateless
299310
case Empty:
300311
n.children[nChild] = NewLeafNode(stem, values)
301312
n.children[nChild].setDepth(n.depth + 1)
@@ -351,9 +362,86 @@ func (n *InternalNode) InsertStem(stem []byte, values [][]byte, resolver NodeRes
351362
return nil
352363
}
353364

365+
// CreatePath inserts a given stem in the tree, placing it as
366+
// described by stemInfo. Its third parameters is the list of
367+
// commitments that have not been assigned a node. It returns
368+
// the same list, save the commitments that were consumed
369+
// during this call.
370+
func (n *InternalNode) CreatePath(path []byte, stemInfo stemInfo, comms []*Point, values [][]byte) ([]*Point, error) {
371+
if len(path) == 0 {
372+
return comms, errors.New("invalid path")
373+
}
374+
375+
// path is 1 byte long, the leaf node must be created
376+
if len(path) == 1 {
377+
switch stemInfo.stemType & 3 {
378+
case extStatusAbsentEmpty:
379+
// nothing to do
380+
case extStatusAbsentOther:
381+
// insert poa stem
382+
case extStatusPresent:
383+
// insert stem
384+
newchild := &LeafNode{
385+
commitment: comms[0],
386+
stem: stemInfo.stem,
387+
values: values,
388+
depth: n.depth + 1,
389+
}
390+
n.children[path[0]] = newchild
391+
comms = comms[1:]
392+
if stemInfo.has_c1 {
393+
newchild.c1 = comms[0]
394+
comms = comms[1:]
395+
} else {
396+
newchild.c1 = new(Point)
397+
}
398+
if stemInfo.has_c2 {
399+
newchild.c2 = comms[0]
400+
comms = comms[1:]
401+
} else {
402+
newchild.c2 = new(Point)
403+
}
404+
for b, value := range stemInfo.values {
405+
newchild.values[b] = value
406+
}
407+
}
408+
return comms, nil
409+
}
410+
411+
switch child := n.children[path[0]].(type) {
412+
case nil:
413+
// create the child node if missing
414+
n.children[path[0]] = &InternalNode{
415+
children: make([]VerkleNode, NodeWidth),
416+
depth: n.depth + 1,
417+
commitment: comms[0],
418+
}
419+
comms = comms[1:]
420+
case *InternalNode:
421+
// nothing else to do
422+
case *LeafNode:
423+
return comms, fmt.Errorf("error rebuilding the tree from a proof: stem %x leads to an already-existing leaf node at depth %x", stemInfo.stem, n.depth)
424+
default:
425+
return comms, fmt.Errorf("error rebuilding the tree from a proof: stem %x leads to an unsupported node type %v", stemInfo.stem, child)
426+
}
427+
428+
// This should only be used in the context of
429+
// stateless nodes, so panic if another node
430+
// type is found.
431+
child := n.children[path[0]].(*InternalNode)
432+
433+
// recurse
434+
return child.CreatePath(path[1:], stemInfo, comms, values)
435+
}
436+
354437
func (n *InternalNode) GetStem(stem []byte, resolver NodeResolverFn) ([][]byte, error) {
355438
nchild := offset2key(stem, n.depth) // index of the child pointed by the next byte in the key
356439
switch child := n.children[nchild].(type) {
440+
case nil:
441+
// Finding nil in a tree means that this is a stateless tree, so
442+
// some information is missing. Typically, this means that the proof
443+
// is incorrect, or that there is a bug in tree reconstruction.
444+
return nil, errMissingNodeInStateless
357445
case Empty:
358446
return nil, nil
359447
case *HashedNode:
@@ -641,7 +729,11 @@ func (n *InternalNode) GetProofItems(keys keylist) (*ProofElements, []byte, [][]
641729
var points [NodeWidth]*Point
642730
for i, child := range n.children {
643731
fiPtrs[i] = &fi[i]
644-
points[i] = child.Commitment()
732+
if child != nil {
733+
points[i] = child.Commitment()
734+
} else {
735+
points[i] = new(Point)
736+
}
645737
}
646738
toFrMultiple(fiPtrs[:], points[:])
647739

@@ -665,7 +757,7 @@ func (n *InternalNode) GetProofItems(keys keylist) (*ProofElements, []byte, [][]
665757

666758
// Special case of a proof of absence: no children
667759
// commitment, as the value is 0.
668-
if _, ok := n.children[childIdx].(Empty); ok {
760+
if _, ok := n.children[childIdx].(Empty); ok || n.children[childIdx] == nil {
669761
// A question arises here: what if this proof of absence
670762
// corresponds to several stems? Should the ext status be
671763
// repeated as many times? It would be wasteful, so the
@@ -768,6 +860,9 @@ func (n *InternalNode) toDot(parent, path string) string {
768860
}
769861

770862
for i, child := range n.children {
863+
if child == nil {
864+
continue
865+
}
771866
ret = fmt.Sprintf("%s%s", ret, child.toDot(me, fmt.Sprintf("%s%02x", path, i)))
772867
}
773868

0 commit comments

Comments
 (0)