Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add building/decoding merkle path binary from/to merkle path json data #66

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
)

// MerklePathData model json format according to BRC-58.
type MerklePathData struct {
Index uint64 `json:"index"`
Path []string `json:"path"`
}

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

// BuildMerklePathBinary on merkle path data model builds merkle path binary format.
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 leaf into the binary path
for _, leaf := range merklePath.Path {
// decode hex leaf into bytes
leafBytes, err := hex.DecodeString(leaf)
if err != nil {
return "", err
}

// append leaf bytes into binary path
bytes = append(bytes, leafBytes...)
}

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

// DecodeMerklePathBinary from merkle path binary decodes MerklePathData.
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])
}
Loading