forked from BuxOrg/bux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodel_compound_merkle_path.go
157 lines (134 loc) · 3.48 KB
/
model_compound_merkle_path.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package bux
import (
"bytes"
"database/sql/driver"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"reflect"
"sort"
"github.com/libsv/go-bt/v2"
)
const maxCmpHeight = 64
// CompoundMerklePath represents Compound Merkle Path type
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 bt.VarInt
}
// Hex returns CMP in hex format
func (cmp *CompoundMerklePath) Hex() string {
return cmp.bytesBuffer().String()
}
// Bytes returns CMPSlice bytes
func (cmps *CMPSlice) Bytes() []byte {
var buff bytes.Buffer
for _, cmp := range *cmps {
bytes, _ := hex.DecodeString(cmp.Hex())
buff.Write(bytes)
}
return buff.Bytes()
}
func (cmp *CompoundMerklePath) bytesBuffer() *bytes.Buffer {
height := len(*cmp) - 1
var buff bytes.Buffer
buff.WriteString(leadingZeroInt(height))
for i := height; i >= 0; i-- {
m := (*cmp)[i]
leafs := len(m)
buff.WriteString(hex.EncodeToString(bt.VarInt(leafs).Bytes()))
sortedNodes := sortByOffset(m)
for _, n := range sortedNodes {
buff.WriteString(hex.EncodeToString(n.offset.Bytes()))
buff.WriteString(n.node)
}
}
return &buff
}
func sortByOffset(m map[string]bt.VarInt) []nodeOffset {
n := make([]nodeOffset, 0)
for node, offset := range m {
n = append(n, nodeOffset{node, offset})
}
sort.Slice(n, func(i, j int) bool {
return n[i].offset < n[j].offset
})
return n
}
// CalculateCompoundMerklePath calculates CMP from a slice of Merkle Proofs
func CalculateCompoundMerklePath(mp []MerkleProof) (CompoundMerklePath, error) {
if len(mp) == 0 || mp == nil {
return CompoundMerklePath{}, nil
}
height := len(mp[0].Nodes)
if height > maxCmpHeight {
return nil,
fmt.Errorf("Compound Merkle Path cannot be higher than %d", maxCmpHeight)
}
for _, m := range mp {
if height != len(m.Nodes) {
return nil,
errors.New("Compound Merkle Path cannot be obtained from Merkle Proofs of different heights")
}
}
cmp := make(CompoundMerklePath, height)
for _, m := range mp {
cmpToAdd := m.ToCompoundMerklePath()
err := cmp.add(cmpToAdd)
if err != nil {
return CompoundMerklePath{}, err
}
}
return cmp, nil
}
// In case the offset or height is less than 10, they must be written with a leading zero
func leadingZeroInt(i int) string {
return fmt.Sprintf("%02x", i)
}
func (cmp *CompoundMerklePath) add(c CompoundMerklePath) error {
if len(*cmp) != len(c) {
return errors.New("Compound Merkle Path with different height cannot be added")
}
for i := range c {
for k, v := range c[i] {
if (*cmp)[i] == nil {
(*cmp)[i] = c[i]
break
}
(*cmp)[i][k] = v
}
}
return nil
}
// Scan scan value into Json, implements sql.Scanner interface
func (cmps *CMPSlice) Scan(value interface{}) error {
if value == nil {
return nil
}
xType := fmt.Sprintf("%T", value)
var byteValue []byte
if xType == ValueTypeString {
byteValue = []byte(value.(string))
} else {
byteValue = value.([]byte)
}
if bytes.Equal(byteValue, []byte("")) || bytes.Equal(byteValue, []byte("\"\"")) {
return nil
}
return json.Unmarshal(byteValue, &cmps)
}
// Value return json value, implement driver.Valuer interface
func (cmps CMPSlice) Value() (driver.Value, error) {
if reflect.DeepEqual(cmps, CMPSlice{}) {
return nil, nil
}
marshal, err := json.Marshal(cmps)
if err != nil {
return nil, err
}
return string(marshal), nil
}