Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5a2e0b2

Browse files
committedMar 31, 2021
start spec compliant share merging
1 parent 529b265 commit 5a2e0b2

File tree

1 file changed

+334
-8
lines changed

1 file changed

+334
-8
lines changed
 

‎types/shares.go

+334-8
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import (
44
"bytes"
55
"encoding/binary"
66

7+
"github.com/gogo/protobuf/proto"
8+
tmbytes "github.com/lazyledger/lazyledger-core/libs/bytes"
9+
tmproto "github.com/lazyledger/lazyledger-core/proto/tendermint/types"
710
"github.com/lazyledger/nmt/namespace"
11+
"github.com/lazyledger/rsmt2d"
812
)
913

1014
// Share contains the raw share data without the corresponding namespace.
@@ -42,8 +46,9 @@ func (tx Tx) MarshalDelimited() ([]byte, error) {
4246
lenBuf := make([]byte, binary.MaxVarintLen64)
4347
length := uint64(len(tx))
4448
n := binary.PutUvarint(lenBuf, length)
49+
out := append(lenBuf[:n], tx...)
4550

46-
return append(lenBuf[:n], tx...), nil
51+
return out, nil
4752
}
4853

4954
// MarshalDelimited marshals the raw data (excluding the namespace) of this
@@ -83,12 +88,10 @@ func splitContiguous(nid namespace.ID, rawDatas [][]byte) []NamespacedShare {
8388
innerIndex := 0
8489
for outerIndex < len(rawDatas) {
8590
var rawData []byte
86-
startIndex := 0
87-
rawData, outerIndex, innerIndex, startIndex = getNextChunk(rawDatas, outerIndex, innerIndex, TxShareSize)
88-
rawShare := append(append(append(
89-
make([]byte, 0, len(nid)+1+len(rawData)),
91+
rawData, outerIndex, innerIndex, _ = getNextChunk(rawDatas, outerIndex, innerIndex, MsgShareSize)
92+
rawShare := append(append(
93+
make([]byte, 0, MsgShareSize),
9094
nid...),
91-
byte(startIndex)),
9295
rawData...)
9396
paddedShare := zeroPadIfNecessary(rawShare, ShareSize)
9497
share := NamespacedShare{paddedShare, nid}
@@ -97,8 +100,6 @@ func splitContiguous(nid namespace.ID, rawDatas [][]byte) []NamespacedShare {
97100
return shares
98101
}
99102

100-
// TODO(ismail): implement corresponding merge method for clients requesting
101-
// shares for a particular namespace
102103
func split(rawData []byte, nid namespace.ID) []NamespacedShare {
103104
shares := make([]NamespacedShare, 0)
104105
firstRawShare := append(append(
@@ -184,3 +185,328 @@ func zeroPadIfNecessary(share []byte, width int) []byte {
184185
}
185186
return share
186187
}
188+
189+
// DataFromSquare extracts block data from an extended data square.
190+
func DataFromSquare(eds *rsmt2d.ExtendedDataSquare) (Data, error) {
191+
originalWidth := eds.Width() / 2
192+
193+
// sort block data by namespace
194+
// define a slice for the raw share data of each type
195+
var (
196+
// transactions
197+
txsShares [][]byte
198+
// intermediate state roots
199+
isrShares [][]byte
200+
// evidence
201+
evdShares [][]byte
202+
// messages
203+
msgShares [][]byte
204+
)
205+
206+
// iterate over each row index
207+
for x := uint(0); x < originalWidth; x++ {
208+
// iterate over each col index
209+
for y := uint(0); y < originalWidth; y++ {
210+
// sort the data of that share types via namespace
211+
share := eds.Cell(x, y)
212+
nid := share[:NamespaceSize]
213+
switch {
214+
case bytes.Compare(TxNamespaceID, nid) == 0:
215+
txsShares = append(txsShares, share)
216+
217+
case bytes.Compare(IntermediateStateRootsNamespaceID, nid) == 0:
218+
isrShares = append(isrShares, share)
219+
220+
case bytes.Compare(EvidenceNamespaceID, nid) == 0:
221+
evdShares = append(evdShares, share)
222+
223+
case bytes.Compare(TailPaddingNamespaceID, nid) == 0:
224+
continue
225+
226+
// every other namespaceID should be a message
227+
default:
228+
msgShares = append(msgShares, share)
229+
}
230+
}
231+
}
232+
233+
// pass the raw share data to their respective parsers
234+
txs := parseTxs(txsShares)
235+
236+
isrs := parseIsrs(isrShares)
237+
238+
evd, err := parseEvd(evdShares)
239+
if err != nil {
240+
return Data{}, err
241+
}
242+
243+
msgs, err := parseMsgs(msgShares)
244+
if err != nil {
245+
return Data{}, err
246+
}
247+
248+
return Data{
249+
Txs: txs,
250+
IntermediateStateRoots: isrs,
251+
Evidence: evd,
252+
Messages: msgs,
253+
}, nil
254+
}
255+
256+
func parseTxs(shares [][]byte) Txs {
257+
// parse the sharse
258+
rawTxs := parseShares(shares)
259+
260+
// convert to the Tx type
261+
txs := make(Txs, len(rawTxs))
262+
for i := 0; i < len(txs); i++ {
263+
txs[i] = Tx(rawTxs[i])
264+
}
265+
266+
return txs
267+
}
268+
269+
func parseIsrs(shares [][]byte) IntermediateStateRoots {
270+
rawISRs := parseShares(shares)
271+
272+
ISRs := make([]tmbytes.HexBytes, len(rawISRs))
273+
for i := 0; i < len(ISRs); i++ {
274+
ISRs[i] = rawISRs[i]
275+
}
276+
277+
return IntermediateStateRoots{RawRootsList: ISRs}
278+
}
279+
280+
// parseMsgs collects all messages from the shares provided
281+
func parseEvd(shares [][]byte) (EvidenceData, error) {
282+
rawEvd := parseShares(shares)
283+
284+
evdList := make(EvidenceList, len(rawEvd))
285+
286+
// parse into protobuf bytes
287+
for i := 0; i < len(rawEvd); i++ {
288+
// unmarshal the evidence
289+
var protoEvd *tmproto.Evidence
290+
err := proto.Unmarshal(rawEvd[i], protoEvd)
291+
if err != nil {
292+
return EvidenceData{}, err
293+
}
294+
295+
evd, err := EvidenceFromProto(protoEvd)
296+
if err != nil {
297+
return EvidenceData{}, err
298+
}
299+
300+
evdList[i] = evd
301+
}
302+
303+
return EvidenceData{Evidence: evdList}, nil
304+
}
305+
306+
// parseMsgs collects all messages from the shares provided
307+
func parseMsgs(shares [][]byte) (Messages, error) {
308+
msgList, err := parseMsgShares(shares)
309+
if err != nil {
310+
return MessagesEmpty, err
311+
}
312+
313+
return Messages{
314+
MessagesList: msgList,
315+
}, nil
316+
}
317+
318+
// parseShares iterates through raw shares and separates the contiguous chunks
319+
// of data. we use this for transactions, evidence, and intermediate state roots
320+
func parseShares(shares [][]byte) [][]byte {
321+
currentShare := shares[0][NamespaceSize+ShareReservedBytes:]
322+
txLen := uint8(shares[0][NamespaceSize])
323+
var parsed [][]byte
324+
for cursor := 0; cursor < len(shares); {
325+
var p []byte
326+
327+
currentShare, cursor, txLen, p = next(shares, currentShare, cursor, txLen)
328+
if p != nil {
329+
parsed = append(parsed, p)
330+
}
331+
}
332+
333+
return parsed
334+
}
335+
336+
// next returns the next chunk of a contiguous share. Used for parsing
337+
// transaction, evidence, and intermediate state root block data.
338+
func next(shares [][]byte, current []byte, cursor int, l uint8) ([]byte, int, uint8, []byte) {
339+
switch {
340+
// the rest of the shares should be tail padding
341+
case l == 0:
342+
343+
cursor++
344+
if len(shares) != cursor {
345+
panic("contiguous share of length zero")
346+
}
347+
return nil, cursor, 0, nil
348+
349+
// the tx is contained in the current share
350+
case int(l) < len(current):
351+
352+
tx := append(make([]byte, 0, l), current[:l]...)
353+
354+
// set the next txLen and update the next share
355+
txLen := current[l]
356+
357+
// make sure that nothing panics if txLen is at the end of the share
358+
if len(current) < int(l)+ShareReservedBytes+1 {
359+
cursor++
360+
return shares[cursor][NamespaceSize:], cursor, txLen, tx
361+
}
362+
363+
current := current[l+ShareReservedBytes:]
364+
// try printing current everytime instead
365+
366+
return current, cursor, txLen, tx
367+
368+
// the tx requires some portion of the following share
369+
case int(l) > len(current):
370+
371+
cursor++
372+
373+
// merge the current and the next share
374+
375+
current := append(current, shares[cursor][NamespaceSize:]...)
376+
377+
// try again using the next share
378+
return next(shares, current, cursor, l)
379+
380+
// the tx is exactly the same size of the current share
381+
case int(l) == len(current):
382+
383+
tx := make([]byte, l)
384+
copy(tx, current)
385+
386+
cursor++
387+
388+
// if this is the end of shares only return the tx
389+
if cursor == len(shares) {
390+
return []byte{}, cursor, 0, tx
391+
}
392+
393+
// set the next txLen and next share
394+
next := shares[cursor][NamespaceSize+ShareReservedBytes:]
395+
nextTxLen := shares[cursor][NamespaceSize]
396+
397+
return next, cursor, nextTxLen, tx
398+
}
399+
400+
// this code is unreachable but the compiler doesn't know that
401+
return nil, 0, 0, nil
402+
}
403+
404+
// parseMessages iterates through raw shares and separates the contiguous chunks
405+
// of data. we use this for transactions, evidence, and intermediate state roots
406+
func parseMsgShares(shares [][]byte) ([]Message, error) {
407+
// set the first nid and current share
408+
nid := shares[0][:NamespaceSize]
409+
currentShare := shares[0][NamespaceSize:]
410+
411+
// find and remove the msg len delimiter
412+
currentShare, msgLen, err := tailorMsg(currentShare)
413+
if err != nil {
414+
return nil, err
415+
}
416+
417+
var msgs []Message
418+
for cursor := 0; cursor < len(shares); {
419+
var msg Message
420+
currentShare, cursor, msgLen, msg, err = nextMsg(
421+
shares,
422+
currentShare,
423+
nid,
424+
cursor,
425+
msgLen,
426+
)
427+
if err != nil {
428+
return nil, err
429+
}
430+
if msg.Data != nil {
431+
msgs = append(msgs, msg)
432+
}
433+
}
434+
435+
return msgs, nil
436+
}
437+
438+
// next returns the next chunk of a contiguous share. Used for parsing
439+
// transaction, evidence, and intermediate state root block data.
440+
func nextMsg(shares [][]byte, current, nid []byte, cursor int, l uint64) ([]byte, int, uint64, Message, error) {
441+
switch {
442+
// the rest of the share should be tail padding
443+
case l == 0:
444+
cursor++
445+
if len(shares) != cursor {
446+
panic("message of length zero")
447+
}
448+
return nil, cursor, 0, MessageEmpty, nil
449+
450+
// the msg is contained in the current share
451+
case int(l) < len(current):
452+
msg := Message{NamespaceID: nid, Data: current[:l]}
453+
454+
// set the next msgLen and update the next share
455+
next, msgLen, err := tailorMsg(current[l:])
456+
if err != nil {
457+
return nil, 0, 0, MessageEmpty, err
458+
}
459+
460+
return next, cursor, msgLen, msg, nil
461+
462+
// the msg requires some portion of the following share
463+
case int(l) > len(current):
464+
cursor++
465+
466+
// merge the current and the next share
467+
current := append(current, shares[cursor][NamespaceSize:]...)
468+
469+
// try again using the next share
470+
return nextMsg(shares, current, nid, cursor, l)
471+
472+
// the msg is exactly the same size of the current share
473+
case l == uint64(len(current)):
474+
msg := Message{NamespaceID: nid, Data: current}
475+
476+
cursor++
477+
478+
// if this is the end of shares only return the msg
479+
if cursor == len(shares) {
480+
return []byte{}, cursor, 0, msg, nil
481+
}
482+
483+
// set the next msgLen and next share
484+
next, nextMsgLen, err := tailorMsg(shares[cursor][NamespaceSize:])
485+
if err != nil {
486+
return nil, 0, 0, MessageEmpty, err
487+
}
488+
489+
return next, cursor, nextMsgLen, msg, nil
490+
}
491+
492+
// this code is unreachable but the compiler doesn't know that
493+
return nil, 0, 0, MessageEmpty, nil
494+
}
495+
496+
// tailorMsg finds and returns the length delimiter of the message provided
497+
// while also removing the delimiter bytes from the input
498+
func tailorMsg(input []byte) ([]byte, uint64, error) {
499+
// read the length of the message
500+
r := bytes.NewBuffer(input[:binary.MaxVarintLen64])
501+
msgLen, err := binary.ReadUvarint(r)
502+
if err != nil {
503+
return nil, 0, err
504+
}
505+
506+
// calculate the number of bytes used by the delimiter
507+
lenBuf := make([]byte, binary.MaxVarintLen64)
508+
n := binary.PutUvarint(lenBuf, msgLen)
509+
510+
// return the input without the length delimiter
511+
return input[n+1:], msgLen, nil
512+
}

0 commit comments

Comments
 (0)
Please sign in to comment.