Skip to content

Commit caa81a5

Browse files
committed
fix global mutation issue
1 parent 4bc28ef commit caa81a5

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

encoding.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ func parseEoAccountNode(serialized []byte, depth byte) (VerkleNode, error) {
145145
if err := ln.c1.SetBytesUncompressed(serialized[leafStemOffset+StemSize:leafStemOffset+StemSize+banderwagon.UncompressedSize], true); err != nil {
146146
return nil, fmt.Errorf("error setting leaf C1 commitment: %w", err)
147147
}
148-
ln.c2 = &banderwagon.Identity
148+
ln.c2 = new(Point)
149+
*ln.c2 = banderwagon.Identity
149150
ln.commitment = new(Point)
150151
if err := ln.commitment.SetBytesUncompressed(serialized[leafStemOffset+StemSize+banderwagon.UncompressedSize:leafStemOffset+StemSize+banderwagon.UncompressedSize*2], true); err != nil {
151152
return nil, fmt.Errorf("error setting leaf root commitment: %w", err)
@@ -171,13 +172,15 @@ func parseSingleSlotNode(serialized []byte, depth byte) (VerkleNode, error) {
171172
if err := ln.c1.SetBytesUncompressed(cnCommBytes, true); err != nil {
172173
return nil, fmt.Errorf("error setting leaf C1 commitment: %w", err)
173174
}
174-
ln.c2 = &banderwagon.Identity
175+
ln.c2 = new(Point)
176+
*ln.c2 = banderwagon.Identity
175177
} else {
176178
ln.c2 = new(Point)
177179
if err := ln.c2.SetBytesUncompressed(cnCommBytes, true); err != nil {
178180
return nil, fmt.Errorf("error setting leaf C2 commitment: %w", err)
179181
}
180-
ln.c1 = &banderwagon.Identity
182+
ln.c1 = new(Point)
183+
*ln.c1 = banderwagon.Identity
181184
}
182185
ln.commitment = new(Point)
183186
if err := ln.commitment.SetBytesUncompressed(rootCommBytes, true); err != nil {

encoding_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,43 @@ func TestParseNodeSingleSlot(t *testing.T) {
191191
t.Fatalf("invalid commitment, got %x, expected %x", lnd.commitment, ln.commitment)
192192
}
193193
}
194+
195+
func TestLeaf_FirstC2Write_DoesNotMutate_GlobalIdentity(t *testing.T) {
196+
// Build a leaf with exactly one value in c1 (suffix 7), so Serialize() picks single-slot encoding.
197+
values := make([][]byte, NodeWidth)
198+
values[7] = word32(0x11)
199+
200+
ln, err := NewLeafNode(ffx32KeyTest[:StemSize], values)
201+
if err != nil {
202+
t.Fatalf("NewLeafNode: %v", err)
203+
}
204+
ser, err := ln.Serialize()
205+
if err != nil {
206+
t.Fatalf("Serialize: %v", err)
207+
}
208+
if got := ser[0]; got != singleSlotType {
209+
t.Fatalf("expected single-slot encoding, got type=%d", got)
210+
}
211+
212+
node, err := ParseNode(ser, 0)
213+
if err != nil {
214+
t.Fatalf("ParseNode: %v", err)
215+
}
216+
leaf, ok := node.(*LeafNode)
217+
if !ok {
218+
t.Fatalf("expected *LeafNode, got %T", node)
219+
}
220+
221+
// Snapshot global identity (value copy) to compare later.
222+
idBefore := banderwagon.Identity
223+
224+
// First write into c2 (suffix 128). This must NOT mutate banderwagon.Identity.
225+
val := word32(0x03)
226+
if err := leaf.updateCn(128, val, leaf.c2); err != nil {
227+
t.Fatalf("updateCn(c2): %v", err)
228+
}
229+
230+
if idBefore != banderwagon.Identity {
231+
t.Fatalf("BUG: first c2 write mutated global banderwagon.Identity")
232+
}
233+
}

0 commit comments

Comments
 (0)