Skip to content

Commit e0c3e01

Browse files
Implement proving that key is in hashmap (merkle proof)
1 parent d1a4f3e commit e0c3e01

File tree

4 files changed

+179
-1
lines changed

4 files changed

+179
-1
lines changed

boc/cell.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,17 @@ func (c *Cell) bocReprWithoutRefs(mask levelMask) []byte {
424424
func (c *Cell) Level() int {
425425
return c.mask.Level()
426426
}
427+
428+
func (c *Cell) GetMerkleRoot() ([32]byte, error) {
429+
if c.CellType() != MerkleProofCell {
430+
return [32]byte{}, errors.New("not merkle proof cell")
431+
}
432+
bytes, err := c.ReadBytes(33)
433+
if err != nil {
434+
return [32]byte{}, err
435+
}
436+
var hash [32]byte
437+
copy(hash[:], bytes[1:])
438+
return hash, nil
439+
440+
}

boc/immutable_cell.go

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package boc
33
import (
44
"crypto/sha256"
55
"encoding/binary"
6+
"fmt"
67
)
78

89
// immutableCell provides a convenient way to calculate a cell's hash and depth.
@@ -13,9 +14,12 @@ type immutableCell struct {
1314
hashes [][]byte
1415
depths []int
1516

16-
// bitsBuf points to a content of this cell.
17+
// bitsBuf points to the content of this cell.
1718
// Used if this cell is a pruned branch cell to extract hashes and depth of an original cell.
1819
bitsBuf []byte
20+
// bitsLen is a length of the content of this cell in bits.
21+
// it's not always equal to len(bitsBuf) because bitsBuf can contain more bytes than the actual content.
22+
bitsLen int
1923
}
2024

2125
// newImmutableCell returns a new instance of immutable cell.
@@ -31,6 +35,7 @@ func newImmutableCell(c *Cell, cache map[*Cell]*immutableCell) (*immutableCell,
3135
hashes: make([][]byte, 0, c.mask.HashesCount()),
3236
depths: make([]int, 0, c.mask.HashesCount()),
3337
bitsBuf: c.bits.buf,
38+
bitsLen: c.bits.len,
3439
}
3540
for _, ref := range c.refs {
3641
if ref == nil {
@@ -154,3 +159,59 @@ func (ic *immutableCell) Depth(level int) int {
154159
}
155160
return ic.depths[index]
156161
}
162+
163+
// pruneCells return the current subtree (which this cell represents) with pruned cells.
164+
// if this cell is pruned, "pruneCells" returns a new pruned branch cell instead of this cell.
165+
// As of now, this function doesn't work with MerkleProofCell and MerkleUpdateCell.
166+
func (ic *immutableCell) pruneCells(pruned map[*immutableCell]struct{}) (*Cell, error) {
167+
if ic.cellType == MerkleProofCell || ic.cellType == MerkleUpdateCell {
168+
return nil, fmt.Errorf("unsupported cell type: %v", ic.cellType)
169+
}
170+
if _, ok := pruned[ic]; ok {
171+
// we are pruned
172+
// let's replace this cell with a pruned branch cell
173+
prunedCell := NewCell()
174+
prunedCell.mask = 1
175+
prunedCell.cellType = PrunedBranchCell
176+
if err := prunedCell.WriteUint(1, 8); err != nil {
177+
return nil, err
178+
}
179+
if err := prunedCell.WriteUint(1, 8); err != nil {
180+
return nil, err
181+
}
182+
if err := prunedCell.WriteBytes(ic.Hash(0)); err != nil {
183+
return nil, err
184+
}
185+
if err := prunedCell.WriteUint(uint64(ic.Depth(0)), 16); err != nil {
186+
return nil, err
187+
}
188+
prunedCell.ResetCounters()
189+
return prunedCell, nil
190+
}
191+
// all good,
192+
// going down the tree
193+
bits := BitString{
194+
buf: ic.bitsBuf,
195+
cap: ic.bitsLen,
196+
len: ic.bitsLen,
197+
}
198+
res := Cell{
199+
bits: bits,
200+
refs: [4]*Cell{},
201+
cellType: ic.cellType,
202+
mask: ic.mask,
203+
}
204+
mask := ic.mask
205+
for i, ref := range ic.refs {
206+
cell, err := ref.pruneCells(pruned)
207+
if err != nil {
208+
return nil, err
209+
}
210+
if cell.mask > 0 {
211+
mask |= cell.mask
212+
}
213+
res.refs[i] = cell
214+
}
215+
res.mask = mask
216+
return &res, nil
217+
}

boc/merkle_proof.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package boc
2+
3+
type MerkleProver struct {
4+
root *immutableCell
5+
6+
pruned map[*immutableCell]struct{}
7+
}
8+
9+
func NewMerkleProver(root *Cell) (*MerkleProver, error) {
10+
immRoot, err := newImmutableCell(root, make(map[*Cell]*immutableCell))
11+
if err != nil {
12+
return nil, err
13+
}
14+
return &MerkleProver{root: immRoot, pruned: make(map[*immutableCell]struct{})}, nil
15+
}
16+
17+
type Cursor struct {
18+
cell *immutableCell
19+
prover *MerkleProver
20+
}
21+
22+
func (p *MerkleProver) Cursor() *Cursor {
23+
return &Cursor{cell: p.root, prover: p}
24+
}
25+
26+
func (p *MerkleProver) CreateProof() ([]byte, error) {
27+
immRoot, err := p.root.pruneCells(p.pruned)
28+
if err != nil {
29+
return nil, err
30+
}
31+
mp := NewCell()
32+
mp.cellType = MerkleProofCell
33+
if err := mp.WriteUint(3, 8); err != nil {
34+
return nil, err
35+
}
36+
if err := mp.WriteBytes(p.root.Hash(0)); err != nil {
37+
return nil, err
38+
}
39+
if err := mp.WriteUint(uint64(p.root.Depth(0)), 16); err != nil {
40+
return nil, err
41+
}
42+
if err := mp.AddRef(immRoot); err != nil {
43+
return nil, err
44+
}
45+
mp.ResetCounters()
46+
return SerializeBoc(mp, false, false, false, 0)
47+
}
48+
49+
func (c *Cursor) Prune() {
50+
c.prover.pruned[c.cell] = struct{}{}
51+
}
52+
53+
func (c *Cursor) Ref(ref int) *Cursor {
54+
return &Cursor{cell: c.cell.refs[ref], prover: c.prover}
55+
}

tlb/hashmap.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,54 @@ func countLeafs(keySize, leftKeySize int, c *boc.Cell) (int, error) {
171171
return 1, nil
172172
}
173173

174+
func ProveKeyInHashmap(cell *boc.Cell, key boc.BitString) ([]byte, error) {
175+
keySize := key.BitsAvailableForRead()
176+
prover, err := boc.NewMerkleProver(cell)
177+
if err != nil {
178+
return nil, err
179+
}
180+
cursor := prover.Cursor()
181+
bitString := boc.NewBitString(keySize)
182+
prefix := &bitString
183+
for {
184+
var err error
185+
var size int
186+
size, prefix, err = loadLabel(keySize, cell, prefix)
187+
if err != nil {
188+
return nil, err
189+
}
190+
if keySize <= size {
191+
break
192+
}
193+
if _, err = key.ReadBits(size); err != nil {
194+
return nil, err
195+
}
196+
197+
isRight, err := key.ReadBit()
198+
if err != nil {
199+
return nil, err
200+
}
201+
keySize = keySize - size - 1
202+
next, err := cell.NextRef()
203+
if err != nil {
204+
return nil, err
205+
}
206+
if isRight {
207+
cursor.Ref(0).Prune()
208+
next, err = cell.NextRef()
209+
if err != nil {
210+
return nil, err
211+
}
212+
cursor = cursor.Ref(1)
213+
} else {
214+
cursor.Ref(1).Prune()
215+
cursor = cursor.Ref(0)
216+
}
217+
cell = next
218+
}
219+
return prover.CreateProof()
220+
}
221+
174222
func (h *Hashmap[keyT, T]) mapInner(keySize, leftKeySize int, c *boc.Cell, keyPrefix *boc.BitString, decoder *Decoder) error {
175223
var err error
176224
var size int

0 commit comments

Comments
 (0)