Skip to content

Commit f681c57

Browse files
authored
Merge pull request #2 from everFinance/merkle-add
feat(): add merkle
2 parents 9e00678 + 5f73154 commit f681c57

File tree

9 files changed

+188
-24
lines changed

9 files changed

+188
-24
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
main.go
2-
test-keyfile.json
2+
test-keyfile.json
3+
.DS_Store

Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
test:
2+
sh ./test.sh

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,11 @@ func main() {
7474
fmt.Println(id, stat, err) // {{id}}, Pending, nil
7575
}
7676
```
77+
78+
### Development
79+
80+
#### Test
81+
82+
```
83+
make test
84+
```

go.mod

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@ module github.com/everFinance/goar
33
go 1.14
44

55
require github.com/stretchr/testify v1.6.1
6-
require github.com/everFinance/gojwk v1.0.0
6+
7+
require (
8+
github.com/everFinance/gojwk v1.0.0
9+
github.com/shopspring/decimal v1.2.0
10+
)

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ github.com/everFinance/gojwk v1.0.0 h1:le/oI2NgXlrqg3MHU6ka+V30EWcD7TD6+Ilh+go79
44
github.com/everFinance/gojwk v1.0.0/go.mod h1:icXSXsIdpAczlpAtSljQlmABkMTRZENr73KHmo0GOGc=
55
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
66
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
7+
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
8+
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
79
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
810
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
911
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=

test.sh

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#! /bin/bash
2+
3+
for i in $(find . -iname "*_test.go" -exec dirname {} \; | uniq)
4+
do
5+
go test -race -cover $i;
6+
if [ $? != 0 ]
7+
then
8+
return 1
9+
fi
10+
done

utils/merkle.go

+153-22
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package utils
22

33
import (
44
"crypto/sha256"
5+
"math"
56
"math/big"
7+
8+
"github.com/shopspring/decimal"
69
)
710

811
const (
@@ -12,13 +15,15 @@ const (
1215
// number of bits in a big.Word
1316
wordBits = 32 << (uint64(^big.Word(0)) >> 63)
1417
// number of bytes in a big.Word
15-
wordBytes = wordBits / 8
18+
wordBytes = wordBits / 8
19+
BranchNodeType = "branch"
20+
LeafNodeType = "leaf"
1621
)
1722

1823
type Chunks struct {
1924
DataRoot []byte
2025
Chunks []Chunk
21-
Proofs []Proof
26+
Proofs []*Proof
2227
}
2328

2429
type Chunk struct {
@@ -27,68 +32,179 @@ type Chunk struct {
2732
MaxByteRange int
2833
}
2934

30-
type LeafNode struct {
35+
// Node include leaf node and branch node
36+
type Node struct {
3137
ID []byte
32-
DataHash []byte
33-
Type string
34-
MinByteRange int
38+
Type string // "branch" or "leaf"
39+
DataHash []byte // only leaf node
40+
MinByteRange int // only leaf node
3541
MaxByteRange int
42+
ByteRange int // only branch node
43+
LeftChild *Node // only branch node
44+
RightChild *Node // only branch node
3645
}
3746

3847
type Proof struct {
3948
Offest int
4049
Proof []byte
4150
}
4251

43-
func GenerateChunks(data []byte) (c Chunks) {
52+
func GenerateChunks(data []byte) Chunks {
4453
chunks := chunkData(data)
4554
leaves := generateLeaves(chunks)
46-
// TODO, calculate root & proofs
55+
root := buildLayer(leaves, 0) // leaf node level == 0
56+
proofs := generateProofs(root)
57+
58+
// Discard the last chunk & proof if it's zero length.
59+
lastChunk := chunks[len(chunks)-1]
60+
if lastChunk.MaxByteRange-lastChunk.MinByteRange == 0 {
61+
chunks = chunks[:len(chunks)-1]
62+
proofs = proofs[:len(proofs)-1]
63+
}
4764

48-
c.DataRoot = leaves[0].ID
49-
return
65+
return Chunks{
66+
DataRoot: root.ID,
67+
Chunks: chunks,
68+
Proofs: proofs,
69+
}
5070
}
5171

5272
func chunkData(data []byte) (chunks []Chunk) {
5373
cursor := 0
54-
// TODO
55-
// for len(data) >= MAX_CHUNK_SIZE {
56-
// }
74+
var rest = data
75+
// if data length > max size
76+
for len(rest) >= MAX_CHUNK_SIZE {
77+
chunkSize := MAX_CHUNK_SIZE
78+
79+
// 查看下一轮的chunkSize 是否小于最小的size,如果是则在这轮中调整chunk size 的大小
80+
nextChunkSize := len(rest) - MAX_CHUNK_SIZE
81+
if nextChunkSize > 0 && nextChunkSize < MIN_CHUNK_SIZE {
82+
dec := decimal.NewFromFloat(math.Ceil(float64(len(rest) / 2)))
83+
chunkSize = int(dec.IntPart())
84+
}
85+
86+
chunk := rest[:chunkSize]
87+
dataHash := sha256.Sum256(chunk)
88+
cursor += len(chunk)
89+
chunks = append(chunks, Chunk{
90+
DataHash: dataHash[:],
91+
MinByteRange: cursor - len(chunk),
92+
MaxByteRange: cursor,
93+
})
5794

58-
hash := sha256.Sum256(data)
95+
rest = rest[chunkSize:]
96+
}
97+
98+
hash := sha256.Sum256(rest)
5999
chunks = append(chunks, Chunk{
60100
DataHash: hash[:],
61101
MinByteRange: cursor,
62-
MaxByteRange: cursor + len(data),
102+
MaxByteRange: cursor + len(rest),
63103
})
64104
return
65105
}
66106

67-
func generateLeaves(chunks []Chunk) (leafs []LeafNode) {
107+
func generateLeaves(chunks []Chunk) (leafs []*Node) {
68108
for _, chunk := range chunks {
69109
hDataHash := sha256.Sum256(chunk.DataHash)
70110
hMaxByteRange := sha256.Sum256(PaddedBigBytes(big.NewInt(int64(chunk.MaxByteRange)), 32))
71111

72-
leafs = append(leafs, LeafNode{
112+
leafs = append(leafs, &Node{
73113
ID: hashArray(
74114
[][]byte{hDataHash[:], hMaxByteRange[:]},
75115
),
116+
Type: LeafNodeType,
76117
DataHash: chunk.DataHash,
77-
Type: "leaf",
78118
MinByteRange: chunk.MinByteRange,
79119
MaxByteRange: chunk.MaxByteRange,
120+
ByteRange: 0,
121+
LeftChild: nil,
122+
RightChild: nil,
80123
})
81124
}
125+
return
126+
}
127+
128+
// buildLayer
129+
func buildLayer(nodes []*Node, level int) (root *Node) {
130+
if len(nodes) == 1 {
131+
root = nodes[0]
132+
return
133+
}
134+
135+
nextLayer := make([]*Node, 0, len(nodes)/2)
136+
for i := 0; i < len(nodes); i += 2 {
137+
leftNode := nodes[i]
138+
var rightNode *Node
139+
if i+1 < len(nodes) {
140+
rightNode = nodes[i+1]
141+
}
142+
nextLayer = append(nextLayer, hashBranch(leftNode, rightNode))
143+
}
144+
145+
return buildLayer(nextLayer, level+1)
146+
}
147+
148+
// hashBranch get branch node by child node
149+
func hashBranch(leftNode, rightNode *Node) (branchNode *Node) {
150+
// 如果只有一个node,则该node 为branch node
151+
if rightNode == nil {
152+
return leftNode
153+
}
154+
hLeafNodeId := sha256.Sum256(leftNode.ID)
155+
hRightNodeId := sha256.Sum256(rightNode.ID)
156+
hLeafNodeMaxRange := sha256.Sum256(PaddedBigBytes(big.NewInt(int64(leftNode.MaxByteRange)), 32))
157+
id := hashArray([][]byte{hLeafNodeId[:], hRightNodeId[:], hLeafNodeMaxRange[:]})
158+
159+
branchNode = &Node{
160+
Type: BranchNodeType,
161+
ID: id,
162+
DataHash: nil,
163+
MinByteRange: 0,
164+
MaxByteRange: rightNode.MaxByteRange,
165+
ByteRange: leftNode.MaxByteRange,
166+
LeftChild: leftNode,
167+
RightChild: rightNode,
168+
}
82169

83170
return
84171
}
85172

86-
func buildLayer() {
87-
//TODO
173+
func generateProofs(rootNode *Node) []*Proof {
174+
return resolveBranchProofs(rootNode, []byte{}, 0)
88175
}
89176

90-
func generateProofs() {
91-
// TODO
177+
// resolveBranchProofs 从root node 递归搜索叶子节点并为其生成证明
178+
func resolveBranchProofs(node *Node, proof []byte, depth int) (proofs []*Proof) {
179+
180+
if node.Type == LeafNodeType {
181+
p := &Proof{
182+
Offest: node.MaxByteRange - 1,
183+
Proof: ConcatBuffer(
184+
proof,
185+
node.DataHash,
186+
PaddedBigBytes(big.NewInt(int64(node.MaxByteRange)), 32),
187+
),
188+
}
189+
proofs = append(proofs, p)
190+
return
191+
}
192+
193+
if node.Type == BranchNodeType {
194+
partialProof := ConcatBuffer(
195+
proof,
196+
node.LeftChild.ID,
197+
node.RightChild.ID,
198+
PaddedBigBytes(big.NewInt(int64(node.ByteRange)), 32),
199+
)
200+
leftProofs := resolveBranchProofs(node.LeftChild, partialProof, depth+1)
201+
rightProofs := resolveBranchProofs(node.RightChild, partialProof, depth+1)
202+
proofs = append(append(proofs, leftProofs...), rightProofs...)
203+
return
204+
}
205+
206+
// node type error then return nil
207+
return nil
92208
}
93209

94210
func hashArray(data [][]byte) []byte {
@@ -120,3 +236,18 @@ func ReadBits(bigint *big.Int, buf []byte) {
120236
}
121237
}
122238
}
239+
240+
// ConcatBuffer
241+
func ConcatBuffer(buffers ...[]byte) []byte {
242+
totalLength := 0
243+
for i := 0; i < len(buffers); i++ {
244+
totalLength += len(buffers[i])
245+
}
246+
247+
temp := make([]byte, 0, totalLength)
248+
249+
for _, val := range buffers {
250+
temp = append(temp, val...)
251+
}
252+
return temp
253+
}

utils/merkle_test.go

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package utils
22

33
import (
4+
"io/ioutil"
45
"testing"
56

67
"github.com/stretchr/testify/assert"
@@ -9,6 +10,11 @@ import (
910
func TestGenerateChunks(t *testing.T) {
1011
data, _ := Base64Decode("NzcyNg")
1112
assert.Equal(t, "z3rQGxyiqdQuOh2dxDst176oOKmW3S9MwQNTEh4DK1U", Base64Encode(GenerateChunks(data).DataRoot))
13+
14+
data, err := ioutil.ReadFile("./testfile/1mb.bin")
15+
assert.NoError(t, err)
16+
chunks := GenerateChunks(data)
17+
assert.Equal(t, "o1tTTjbC7hIZN6KbUUYjlkQoDl2k8VXNuBDcGIs52Hc", Base64Encode(chunks.DataRoot))
1218
}
1319

1420
func TestChunkData(t *testing.T) {

utils/testfile/1mb.bin

1 MB
Binary file not shown.

0 commit comments

Comments
 (0)