Skip to content

Commit

Permalink
add building/decoding merkle path binary from/to merkle path json data
Browse files Browse the repository at this point in the history
  • Loading branch information
ssilagadze committed Sep 7, 2023
1 parent eafc069 commit 7c6761d
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
72 changes: 72 additions & 0 deletions merklepathbinary.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package bc

import (
"encoding/hex"

"github.com/libsv/go-bt/v2"
)

// Merkle path data model json format according to BRC-58

Check failure on line 9 in merklepathbinary.go

View workflow job for this annotation

GitHub Actions / lint (1.17.x, ubuntu-latest)

Comment should end in a period (godot)
type MerklePathData struct {
Index uint64 `json:"index"`
Path []string `json:"path"`
}

// Merkle Path Binary Format according to BRC-71 [index, nLeaves, [leaf0, leaf1, leaf2, ... leafnLeaves-1]]

Check failure on line 15 in merklepathbinary.go

View workflow job for this annotation

GitHub Actions / lint (1.17.x, ubuntu-latest)

Comment should end in a period (godot)
type MerklePath string

// Based on merkle path data model builds merkle path binary format

Check failure on line 18 in merklepathbinary.go

View workflow job for this annotation

GitHub Actions / lint (1.17.x, ubuntu-latest)

Comment should end in a period (godot)
func BuildMerklePathBinary(merklePath *MerklePathData) (MerklePath, error) {
index := bt.VarInt(merklePath.Index)
nLeaves := bt.VarInt(len(merklePath.Path))

// first two arguments in merkle path bynary format are index of the transaction and number of leaves
bytes := []byte{}
bytes = append(bytes, index.Bytes()...)
bytes = append(bytes, nLeaves.Bytes()...)

// now add each leave into the binary path
for _, leave := range merklePath.Path {
// decode hex leave into bytes
leaveBytes, err := hex.DecodeString(leave)
if err != nil {
return "", err
}

// append leave bytes into binary path
bytes = append(bytes, leaveBytes...)
}

return MerklePath(hex.EncodeToString(bytes)), nil
}

// from merkle path binary decodes MerklePathData

Check failure on line 43 in merklepathbinary.go

View workflow job for this annotation

GitHub Actions / lint (1.17.x, ubuntu-latest)

Comment should end in a period (godot)
func DecodeMerklePathBinary(merklePath MerklePath) (*MerklePathData, error) {
// convert hex to byte array
merklePathBinary, err := hex.DecodeString(string(merklePath))
if err != nil {
return nil, err
}

merklePathData := &MerklePathData{}
merklePathData.Path = make([]string, 0)

// start paring transaction index
var offset int
index, size := bt.NewVarIntFromBytes(merklePathBinary[offset:])
merklePathData.Index = uint64(index)
offset += size

// next value in the byte array is nLeaves (number of leaves in merkle path)
nLeaves, size := bt.NewVarIntFromBytes(merklePathBinary[offset:])
offset += size

// parse each leaf from the binary path
for k := 0; k < int(nLeaves); k++ {
leaf := merklePathBinary[offset : offset+32]
merklePathData.Path = append(merklePathData.Path, hex.EncodeToString(leaf))
offset += 32
}

return merklePathData, nil
}
59 changes: 59 additions & 0 deletions merklepathbinary_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package bc_test

import (
"testing"

"github.com/libsv/go-bc"

"github.com/stretchr/testify/assert"
)

func TestBuildingMerklePathBinary(t *testing.T) {
t.Parallel()

// build example merkle path data
merklePathData := bc.MerklePathData{
Index: 136,
Path: []string{"0c82025f47b31054e9ad52109ef25b00fd9aaae7153564619bab031d4112f56c",
"3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cd",
"efac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b4",
"1e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034"},
}

// build binary path from it
merklePathBinary, err := bc.BuildMerklePathBinary(&merklePathData)
if err != nil {
t.Error(err)
return
}

// assert binary path is expected
assert.Equal(t, bc.MerklePath("88040c82025f47b31054e9ad52109ef25b00fd9aaae7153564619bab031d4112f56c3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cdefac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b41e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034"), merklePathBinary)
}

func TestDecodingMerklePathBinary(t *testing.T) {
t.Parallel()

merklePath := bc.MerklePath("88040c82025f47b31054e9ad52109ef25b00fd9aaae7153564619bab031d4112f56c3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cdefac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b41e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034")
merklePathData, err := bc.DecodeMerklePathBinary(merklePath)
if err != nil {
t.Error(err)
return
}

// merklePathData := bc.MerklePathData{
// Index: 136,
// Path: []string{"0c82025f47b31054e9ad52109ef25b00fd9aaae7153564619bab031d4112f56c",
// "3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cd",
// "efac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b4",
// "1e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034"},
// }

// assert binary path is expected
assert.Equal(t, uint64(136), merklePathData.Index)
assert.Equal(t, 4, len(merklePathData.Path))
assert.Equal(t, "0c82025f47b31054e9ad52109ef25b00fd9aaae7153564619bab031d4112f56c", merklePathData.Path[0])
assert.Equal(t, "3b6ea708d7b84a078179b53cf2cb2f0636162ffd60a96f81815564bbc6c073cd", merklePathData.Path[1])
assert.Equal(t, "efac0f077fca2a10730400da62ebaebaba852bd5fc3fb7770e090a1919d9c8b4", merklePathData.Path[2])
assert.Equal(t, "1e81e396da7f63e3989a8bc9bdbefddf95c75da1eb3936944b6a55cf82d87034", merklePathData.Path[3])
}

0 comments on commit 7c6761d

Please sign in to comment.