Skip to content
This repository has been archived by the owner on Apr 2, 2024. It is now read-only.

Commit

Permalink
feat: encoding of nLeaves and offset to VarInt in CMP
Browse files Browse the repository at this point in the history
  • Loading branch information
vitalibalashka authored and witalij-4chain committed Sep 20, 2023
1 parent 56df9d9 commit 2382df7
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 28 deletions.
22 changes: 13 additions & 9 deletions model_compound_merkle_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,26 @@ package bux
import (
"bytes"
"database/sql/driver"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"reflect"
"sort"

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

// CompoundMerklePath represents Compound Merkle Path type
type CompoundMerklePath []map[string]uint64
type CompoundMerklePath []map[string]bt.VarInt

// CMPSlice represents slice of Compound Merkle Pathes
// There must be several CMPs in case if utxos from different blocks is used in tx
type CMPSlice []CompoundMerklePath

type nodeOffset struct {
node string
offset uint64
offset bt.VarInt
}

// Hex returns CMP in hex format
Expand All @@ -44,21 +47,22 @@ func (cmps *CMPSlice) Bytes() []byte {
}

func (cmp *CompoundMerklePath) bytesBuffer() *bytes.Buffer {
var hex bytes.Buffer
hex.WriteString(leadingZeroInt(len(*cmp)))
var buff bytes.Buffer
buff.WriteString(leadingZeroInt(len(*cmp) - 1))

for _, m := range *cmp {
hex.WriteString(leadingZeroInt(len(m)))
leafs := len(m)
buff.WriteString(hex.EncodeToString(bt.VarInt(leafs).Bytes()))
sortedNodes := sortByOffset(m)
for _, n := range sortedNodes {
hex.WriteString(leadingZeroInt(int(n.offset)))
hex.WriteString(n.node)
buff.WriteString(hex.EncodeToString(n.offset.Bytes()))
buff.WriteString(n.node)
}
}
return &hex
return &buff
}

func sortByOffset(m map[string]uint64) []nodeOffset {
func sortByOffset(m map[string]bt.VarInt) []nodeOffset {
n := make([]nodeOffset, 0)
for node, offset := range m {
n = append(n, nodeOffset{node, offset})
Expand Down
157 changes: 145 additions & 12 deletions model_compound_merkle_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bux
import (
"testing"

"github.com/libsv/go-bt/v2"
"github.com/stretchr/testify/assert"
)

Expand All @@ -19,7 +20,7 @@ func TestCompoundMerklePathModel_CalculateCompoundMerklePath(t *testing.T) {
},
}
expectedCMP := CompoundMerklePath(
[]map[string]uint64{
[]map[string]bt.VarInt{
{
"node0": 0,
"txId": 1,
Expand Down Expand Up @@ -59,7 +60,7 @@ func TestCompoundMerklePathModel_CalculateCompoundMerklePath(t *testing.T) {
},
}
expectedCMP := CompoundMerklePath(
[]map[string]uint64{
[]map[string]bt.VarInt{
{
"txId1": 2,
"D": 3,
Expand Down Expand Up @@ -103,7 +104,7 @@ func TestCompoundMerklePathModel_CalculateCompoundMerklePath(t *testing.T) {
},
}
expectedCMP := CompoundMerklePath(
[]map[string]uint64{
[]map[string]bt.VarInt{
{
"I": 8,
"J": 9,
Expand Down Expand Up @@ -155,15 +156,15 @@ func TestCompoundMerklePathModel_CalculateCompoundMerklePath(t *testing.T) {
}
cmp, err := CalculateCompoundMerklePath(signleMerkleProof)
assert.NoError(t, err)
assert.Equal(t, cmp, CompoundMerklePath{})
assert.Equal(t, CompoundMerklePath{}, cmp)
})
}

// TestCompoundMerklePathModel_Hex will test the method Hex()
func TestCompoundMerklePathModel_Hex(t *testing.T) {
t.Run("Sorted Compound Merkle Path", func(t *testing.T) {
cmp := CompoundMerklePath(
[]map[string]uint64{
[]map[string]bt.VarInt{
{
"txId1": 2,
"D": 3,
Expand All @@ -188,14 +189,14 @@ func TestCompoundMerklePathModel_Hex(t *testing.T) {
},
},
)
expectedHex := "040602txId103D06G07txId212M13txId30300AB02EF07OP0300ABCD01EFGH02IJKL0200ABCDEFGH01IJKLMNOP"
hex := cmp.Hex()
assert.Equal(t, hex, expectedHex)
expectedHex := "030602txId103D06G07txId20cM0dtxId30300AB02EF07OP0300ABCD01EFGH02IJKL0200ABCDEFGH01IJKLMNOP"
actualHex := cmp.Hex()
assert.Equal(t, expectedHex, actualHex)
})

t.Run("Unsorted Compound Merkle Path", func(t *testing.T) {
cmp := CompoundMerklePath(
[]map[string]uint64{
[]map[string]bt.VarInt{
{
"F": 5,
"E": 4,
Expand All @@ -218,8 +219,140 @@ func TestCompoundMerklePathModel_Hex(t *testing.T) {
},
},
)
expectedHex := "030800A01B02C03D04E05F06G07H0400AB01CD02EF03GH0200ABCD01EFGH"
hex := cmp.Hex()
assert.Equal(t, hex, expectedHex)
expectedHex := "020800A01B02C03D04E05F06G07H0400AB01CD02EF03GH0200ABCD01EFGH"
actualHex := cmp.Hex()
assert.Equal(t, expectedHex, actualHex)
})
}

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

t.Run("Real Merkle Proof", func(t *testing.T) {
signleMerkleProof := []MerkleProof{
{
Index: 1153,
TxOrID: "2130b63dcbfe1356a30137fe9578691f59c6cf42d5e8928a800619de7f8e14da",
Nodes: []string{
"4d4bde1dc35c87bba992944ec0379e0bb009916108113dc3de1c4aecda6457a3",
"168595f83accfcec66d0e0df06df89e6a9a2eaa3aa69427fb86cb54d8ea5b1e9",
"c2edd41b237844a45a0e6248a9e7c520af303a5c91cc8a443ad0075d6a3dec79",
"bdd0fddf45fee49324e55dfc6fdb9044c86dc5be3dbf941a80b395838495ac09",
"3e5ec052b86621b5691d15ad54fab2551c27a36d9ab84f428a304b607aa33d33",
"9feb9b1aaa2cd8486edcacb60b9d477a89aec5867d292608c3c59a18324d608a",
"22e1db219f8d874315845b7cee84832dc0865b5f9e18221a011043a4d6704e7d",
"7f118890abd8df3f8a51c344da0f9235609f5fd380e38cfe519e81262aedb2a7",
"20dcf60bbcecd2f587e8d3344fb68c71f2f2f7a6cc85589b9031c2312a433fe6",
"0be65c1f3b53b937608f8426e43cb41c1db31227d0d9933e8b0ce3b8cc30d67f",
"a8036cf77d8de296f60607862b228174733a30486a37962a56465f5e8c214d87",
"b8e4d7975537bb775e320f01f874c06cf38dd2ce7bb836a1afe0337aeb9fb06f",
"88e6b0bd93e02b057ea43a80a5bb8cf9673f143340af3f569fe0c55c085e5efb",
"15f731176e17f4402802d5be3893419e690225e732d69dfd27f6e614f188233d",
},
},
}
expectedCMP := CompoundMerklePath(
[]map[string]bt.VarInt{
{
"4d4bde1dc35c87bba992944ec0379e0bb009916108113dc3de1c4aecda6457a3": 1152,
"2130b63dcbfe1356a30137fe9578691f59c6cf42d5e8928a800619de7f8e14da": 1153,
},
{
"168595f83accfcec66d0e0df06df89e6a9a2eaa3aa69427fb86cb54d8ea5b1e9": 577,
},
{
"c2edd41b237844a45a0e6248a9e7c520af303a5c91cc8a443ad0075d6a3dec79": 289,
},
{
"bdd0fddf45fee49324e55dfc6fdb9044c86dc5be3dbf941a80b395838495ac09": 145,
},
{
"3e5ec052b86621b5691d15ad54fab2551c27a36d9ab84f428a304b607aa33d33": 73,
},
{
"9feb9b1aaa2cd8486edcacb60b9d477a89aec5867d292608c3c59a18324d608a": 37,
},
{
"22e1db219f8d874315845b7cee84832dc0865b5f9e18221a011043a4d6704e7d": 19,
},
{
"7f118890abd8df3f8a51c344da0f9235609f5fd380e38cfe519e81262aedb2a7": 8,
},
{
"20dcf60bbcecd2f587e8d3344fb68c71f2f2f7a6cc85589b9031c2312a433fe6": 5,
},
{
"0be65c1f3b53b937608f8426e43cb41c1db31227d0d9933e8b0ce3b8cc30d67f": 3,
},
{
"a8036cf77d8de296f60607862b228174733a30486a37962a56465f5e8c214d87": 0,
},
{
"b8e4d7975537bb775e320f01f874c06cf38dd2ce7bb836a1afe0337aeb9fb06f": 1,
},
{
"88e6b0bd93e02b057ea43a80a5bb8cf9673f143340af3f569fe0c55c085e5efb": 1,
},
{
"15f731176e17f4402802d5be3893419e690225e732d69dfd27f6e614f188233d": 1,
},
},
)
cmp, err := CalculateCompoundMerklePath(signleMerkleProof)
assert.NoError(t, err)
assert.Equal(t, expectedCMP, cmp)
expectedHex := "13" + // height
"02" + // nLeafs at this height VarInt
"fd8004" + // offset VarInt
"4d4bde1dc35c87bba992944ec0379e0bb009916108113dc3de1c4aecda6457a3" + // 32 byte hash
"fd8104" + // offset VarInt
"2130b63dcbfe1356a30137fe9578691f59c6cf42d5e8928a800619de7f8e14da" + // 32 byte hash
// ----------------------
// implied end of leaves at this height
// height of next leaves is therefore 12
"01" + // nLeafs at this height VarInt
"fd4102" + // offset VarInt
"168595f83accfcec66d0e0df06df89e6a9a2eaa3aa69427fb86cb54d8ea5b1e9" + // 32 byte hash
// ----------------------
// implied end of leaves at this height
// height of next leaves is therefore 11 and so on...
"01" +
"fd2101" +
"c2edd41b237844a45a0e6248a9e7c520af303a5c91cc8a443ad0075d6a3dec79" +
"01" +
"91" +
"bdd0fddf45fee49324e55dfc6fdb9044c86dc5be3dbf941a80b395838495ac09" +
"01" +
"49" +
"3e5ec052b86621b5691d15ad54fab2551c27a36d9ab84f428a304b607aa33d33" +
"01" +
"25" +
"9feb9b1aaa2cd8486edcacb60b9d477a89aec5867d292608c3c59a18324d608a" +
"01" +
"13" +
"22e1db219f8d874315845b7cee84832dc0865b5f9e18221a011043a4d6704e7d" +
"01" +
"08" +
"7f118890abd8df3f8a51c344da0f9235609f5fd380e38cfe519e81262aedb2a7" +
"01" +
"05" +
"20dcf60bbcecd2f587e8d3344fb68c71f2f2f7a6cc85589b9031c2312a433fe6" +
"01" +
"03" +
"0be65c1f3b53b937608f8426e43cb41c1db31227d0d9933e8b0ce3b8cc30d67f" +
"01" +
"00" +
"a8036cf77d8de296f60607862b228174733a30486a37962a56465f5e8c214d87" +
"01" +
"01" +
"b8e4d7975537bb775e320f01f874c06cf38dd2ce7bb836a1afe0337aeb9fb06f" +
"01" +
"01" +
"88e6b0bd93e02b057ea43a80a5bb8cf9673f143340af3f569fe0c55c085e5efb" +
"01" +
"01" +
"15f731176e17f4402802d5be3893419e690225e732d69dfd27f6e614f188233d"
actualHex := cmp.Hex()
assert.Equal(t, expectedHex, actualHex)
})
}
12 changes: 7 additions & 5 deletions model_merkle_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"reflect"

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

// MerkleProof represents Merkle Proof type
Expand All @@ -20,15 +21,16 @@ func (m MerkleProof) ToCompoundMerklePath() CompoundMerklePath {
return nil
}
cmp := make(CompoundMerklePath, height)
pathMap := make(map[string]uint64, 2)
pathMap := make(map[string]bt.VarInt, 2)
offset := m.Index
pathMap[m.TxOrID] = offset
pathMap[m.Nodes[0]] = offsetPair(offset)
op := offsetPair(offset)
pathMap[m.TxOrID] = bt.VarInt(offset)
pathMap[m.Nodes[0]] = bt.VarInt(op)
cmp[0] = pathMap
for i := 1; i < height; i++ {
path := make(map[string]uint64, 1)
path := make(map[string]bt.VarInt, 1)
offset = parrentOffset(offset)
path[m.Nodes[i]] = offset
path[m.Nodes[i]] = bt.VarInt(offset)
cmp[i] = path
}
return cmp
Expand Down
5 changes: 3 additions & 2 deletions model_merkle_proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package bux
import (
"testing"

"github.com/libsv/go-bt/v2"
"github.com/stretchr/testify/assert"
)

Expand All @@ -17,7 +18,7 @@ func TestMerkleProofModel_ToCompoundMerklePath(t *testing.T) {
Nodes: []string{"node0", "node1", "node2", "node3"},
}
expectedCMP := CompoundMerklePath(
[]map[string]uint64{
[]map[string]bt.VarInt{
{
"node0": 0,
"txId": 1,
Expand All @@ -44,7 +45,7 @@ func TestMerkleProofModel_ToCompoundMerklePath(t *testing.T) {
Nodes: []string{"node0", "node1", "node2", "node3", "node4"},
}
expectedCMP := CompoundMerklePath(
[]map[string]uint64{
[]map[string]bt.VarInt{
{
"txId": 14,
"node0": 15,
Expand Down

0 comments on commit 2382df7

Please sign in to comment.