forked from asonnino/fraudproofs-prototype
-
Notifications
You must be signed in to change notification settings - Fork 0
/
block.go
321 lines (285 loc) · 9.32 KB
/
block.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
package fraudproofs
import (
"bytes"
"encoding/binary"
"errors"
"github.com/NebulousLabs/merkletree"
//"crypto/sha256"
//"github.com/minio/sha256-simd"
"crypto/sha512"
"github.com/musalbas/smt"
)
// Step defines the interval on which to compute intermediate state roots (must be a positive integer)
const Step int = 2
// ChunksSize defines the size of each chunk
const chunksSize int = 256
// Block is a block of the blockchain
type Block struct {
// data structure
dataRoot []byte
stateRoot []byte
transactions []Transaction
// implementation specific
prev *Block // link to the previous block
dataTree *merkletree.Tree // Merkle tree storing chunks
interStateRoots [][]byte // intermediate state roots (saved every 'step' transactions)
}
// NewBlock creates a new block with the given transactions.
func NewBlock(t []Transaction, stateTree *smt.SparseMerkleTree) (*Block, error) {
for i := 0; i < len(t); i++ {
err := t[i].CheckTransaction()
if err != nil {
return nil, err
}
}
interStateRoots, stateRoot, err := fillStateTree(t, stateTree)
if err != nil {
return nil, err
}
dataTree := merkletree.New(sha512.New512_256())
dataRoot, err := fillDataTree(t, interStateRoots, dataTree)
if err != nil {
return nil, err
}
return &Block{
dataRoot,
stateRoot,
t,
nil,
dataTree,
interStateRoots}, nil
}
// fillStateTree fills the input state tree with key-values from the input transactions, and returns the state root and
// the intermediate state roots.
func fillStateTree(t []Transaction, stateTree *smt.SparseMerkleTree) ([][]byte, []byte, error){
var stateRoot []byte
var interStateRoots [][]byte
for i := 0; i < len(t); i++ {
//fmt.Println("transaction size: ", len(t[i].Serialize()))
for j := 0; j < len(t[i].writeKeys); j++ {
root, err := stateTree.Update(t[i].writeKeys[j], t[i].newData[j])
if err != nil {
return nil, nil, err
}
stateRoot = make([]byte, len(root))
copy(stateRoot, root)
}
if i != 0 && i % Step == 0 {
interStateRoots = append(interStateRoots, stateRoot)
}
}
if len(t)%Step == 0 {
interStateRoots = append(interStateRoots, stateRoot)
}
return interStateRoots, stateRoot, nil
}
// fillDataTree fills the data tree and returns its root.
func fillDataTree(t []Transaction, interStateRoots [][]byte, dataTree *merkletree.Tree) ([]byte, error) {
chunks, _, err := makeChunks(chunksSize, t, interStateRoots)
if err != nil {
return nil, err
}
for i := 0; i < len(chunks); i++ {
dataTree.Push(chunks[i])
}
return dataTree.Root(), nil
}
// makeChunks splits a set of transactions and state roots into multiple chunks.
func makeChunks(chunkSize int, t []Transaction, s [][]byte) ([][]byte, map[[256]byte]int, error) {
if len(s) != int(len(t)/Step) {
return nil, nil, errors.New("wrong number of intermediate state roots")
}
interStateRoots := make([][]byte, len(s))
copy(interStateRoots, s)
var buff []byte
buffMap := make(map[[256]byte]int)
for i := 0; i < len(t); i++ {
buffMap[t[i].HashKey()] = len(buff)
buff = append(buff, t[i].Serialize()...)
if i != 0 && i%Step == 0 {
buff = append(buff, interStateRoots[0]...)
interStateRoots = interStateRoots[1:]
}
}
if len(t)%Step == 0 {
buff = append(buff, interStateRoots[0]...)
}
var chunk []byte
size := chunkSize - 1
chunks := make([][]byte, 0, len(buff)/size+1)
for len(buff) >= size {
chunk, buff = buff[:size], buff[size:]
chunk = append([]byte{0x0}, chunk...)
chunks = append(chunks, chunk)
}
if len(buff) > 0 {
chunk = buff[:]
chunk = append([]byte{0x0}, chunk...)
chunks = append(chunks, chunk)
}
for i := len(t)-1; i >= 0; i-- {
chunkIndex := buffMap[t[i].HashKey()] / chunksSize
chunkPosition := byte(buffMap[t[i].HashKey()] % chunksSize)
chunks[chunkIndex][0] = chunkPosition
}
return chunks, buffMap, nil
}
// CheckBlock checks that the block is constructed correctly, and returns a fraud proof if it is not.
func (b *Block) CheckBlock(stateTree *smt.SparseMerkleTree) (*FraudProof, error) {
rebuiltBlock, err := NewBlock(b.transactions, stateTree)
if err != nil {
return nil, err
}
// verify that every intermediate state roots are constructed correctly
for i := 0; i < len(rebuiltBlock.interStateRoots); i++ {
if len(b.interStateRoots) <= i || !bytes.Equal(rebuiltBlock.interStateRoots[i], b.interStateRoots[i]) {
// 1. get the transactions causing the (first) invalid intermediate state
t := rebuiltBlock.transactions[i*Step:(i+1)*Step]
// 2. generate Merkle proofs of the keys-values contained in the transaction
var writeKeys, oldData, readKeys, readData [][]byte
for j := 0; j < len(t); j++ {
for k := 0; k < len(t[j].writeKeys); k++ {
writeKeys = append(writeKeys, t[j].writeKeys[k])
oldData = append(oldData, t[j].oldData[k])
}
for k := 0; k < len(t[j].readKeys); k++ {
readKeys = append(readKeys, t[j].readKeys[k])
readData = append(readData, t[j].readData[k])
}
}
proofstate := make([][][]byte, len(writeKeys))
for j := 0; j < len(writeKeys); j++ {
proof, err := stateTree.ProveCompact(writeKeys[j])
if err != nil {
return nil, err
}
proofstate[j] = proof
}
// 3. get chunks concerned by the proof
// TODO compact 'makeChunks' and 'getChunksIndexes'
chunksIndexes, _, err := b.getChunksIndexes(t)
if err != nil {
return nil, err
}
chunks, _, err := makeChunks(chunksSize, b.transactions, b.interStateRoots)
if err != nil {
return nil, err
}
var concernedChunks [][]byte
//fmt.Println(chunksIndexes)
for j := 0; j < len(chunksIndexes); j++ {
concernedChunks = append(concernedChunks, chunks[chunksIndexes[j]])
}
// 4. generate Merkle proofs of the transactions, previous state root, and next state root
proofChunks := make([][][]byte, len(chunksIndexes))
var numOfLeaves uint64
for j := 0; j < len(chunksIndexes); j++ {
// merkletree.Tree cannot call SetIndex on Tree if Tree has not been reset
// a dirty workaround is to copy the data tree
tmpDataTree := merkletree.New(sha512.New512_256())
err = tmpDataTree.SetIndex(chunksIndexes[j])
if err != nil {
return nil, err
}
_, err := fillDataTree(b.transactions, b.interStateRoots, tmpDataTree)
if err != nil {
return nil, err
}
_, proof, _, leaves := tmpDataTree.Prove()
numOfLeaves = leaves
proofChunks[j] = proof
}
return &FraudProof{
writeKeys,
oldData,
readKeys,
readData,
proofstate,
concernedChunks,
proofChunks,
chunksIndexes,
numOfLeaves}, nil
}
}
return nil, nil
}
// getChunksIndexes returns the indexes and number of chunks in which the given transactions are included
func (b *Block) getChunksIndexes(t []Transaction) ([]uint64, uint64, error) {
chunks, buffMap, err := makeChunks(chunksSize, b.transactions, b.interStateRoots)
if err != nil {
return nil, 0, err
}
//fmt.Println(buffMap)
var chunksIndexes []uint64
for i := 0; i < len(t); i++ {
index := uint64(buffMap[t[i].HashKey()]/chunksSize)
//chunksIndexes = append(chunksIndexes, index)
length := int(binary.LittleEndian.Uint16(t[i].Serialize()[:MaxSize]))
//fmt.Println(buffMap[t[i].HashKey()]/chunksSize, length)
last := length/chunksSize
for j := 0; j <= last; j++ {
chunksIndexes = append(chunksIndexes, index + uint64(j))
}
if length > (chunksSize - buffMap[t[i].HashKey()]%chunksSize) {
chunksIndexes = append(chunksIndexes, index+uint64(last)+1) // ugly fix
}
}
uniquesMap := make(map[uint64]bool)
var uniques []uint64
for _, entry := range chunksIndexes {
if _, value := uniquesMap[entry]; !value {
uniquesMap[entry] = true
uniques = append(uniques, entry)
}
}
return uniques, uint64(len(chunks)), nil
}
// VerifyFraudProof verifies whether or not a fraud proof is valid.
func (b *Block) VerifyFraudProof(fp FraudProof) bool {
// 1. check that the transactions, prevStateRoot, nextStateRoot are in the data tree
for i := 0; i < len(fp.proofChunks); i++ {
ret := merkletree.VerifyProof(sha512.New512_256(), b.dataRoot, fp.proofChunks[i], fp.chunksIndexes[i], fp.numOfLeaves)
if ret != true {
return false
}
}
// 2. extract new data from chunks
var indexes []int
var buff []byte
//fmt.Println()
//fmt.Println(fp.chunks)
for i := 0; i < len(fp.chunks); i++ {
indexes = append(indexes, int(fp.chunks[i][0]))
buff = append(buff, fp.chunks[i][1:]...)
}
//fmt.Println(buff)
var newData [][]byte
buff = buff[indexes[0]:]
for i := 0; len(buff) >= MaxSize; i++ {
length := int(binary.LittleEndian.Uint16(buff[:MaxSize]))
//fmt.Println(len(buff), length)
if len(buff) < length {
break
}
t, _ := Deserialize(buff[:length])
buff = buff[length:]
newData = append(newData, t.newData...)
}
//fmt.Println(len(newData), newData)
//fmt.Println(len(fp.writeKeys), fp.writeKeys)
// 3. check keys-values contained in the transaction are in the state tree for old data
subtree := smt.NewDeepSparseMerkleSubTree(smt.NewSimpleMap(), sha512.New512_256())
for i := 0; i < len(fp.writeKeys); i++ {
proof, err := smt.DecompactProof(fp.proofState[i], sha512.New512_256())
if err != nil {
return false
}
subtree.AddBranches(proof, fp.writeKeys[i],fp.oldData[i], true)
// 4. update keys with new data
subtree.Update(fp.writeKeys[i], newData[i])
}
if bytes.Compare(b.stateRoot, subtree.Root()) != 0 {
return false
}
return true
}