-
Notifications
You must be signed in to change notification settings - Fork 893
/
blob.go
162 lines (133 loc) · 4.42 KB
/
blob.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
package blob
import (
"bytes"
"encoding/json"
"errors"
"fmt"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
"github.com/celestiaorg/celestia-app/pkg/appconsts"
"github.com/celestiaorg/celestia-app/x/blob/types"
"github.com/celestiaorg/nmt"
"github.com/celestiaorg/celestia-node/share"
)
var errEmptyShares = errors.New("empty shares")
// Commitment is a Merkle Root of the subtree built from shares of the Blob.
// It is computed by splitting the blob into shares and building the Merkle subtree to be included
// after Submit.
type Commitment []byte
func (com Commitment) String() string {
return string(com)
}
// Equal ensures that commitments are the same
func (com Commitment) Equal(c Commitment) bool {
return bytes.Equal(com, c)
}
// Proof is a collection of nmt.Proofs that verifies the inclusion of the data.
// Proof proves the WHOLE namespaced data for the particular row.
// TODO (@vgonkivs): rework `Proof` in order to prove a particular blob.
// https://github.com/celestiaorg/celestia-node/issues/2303
type Proof []*nmt.Proof
func (p Proof) Len() int { return len(p) }
// equal is a temporary method that compares two proofs.
// should be removed in BlobService V1.
func (p Proof) equal(input Proof) error {
if p.Len() != input.Len() {
return ErrInvalidProof
}
for i, proof := range p {
pNodes := proof.Nodes()
inputNodes := input[i].Nodes()
for i, node := range pNodes {
if !bytes.Equal(node, inputNodes[i]) {
return ErrInvalidProof
}
}
if proof.Start() != input[i].Start() || proof.End() != input[i].End() {
return ErrInvalidProof
}
if !bytes.Equal(proof.LeafHash(), input[i].LeafHash()) {
return ErrInvalidProof
}
}
return nil
}
// Blob represents any application-specific binary data that anyone can submit to Celestia.
type Blob struct {
types.Blob `json:"blob"`
Commitment Commitment `json:"commitment"`
// the celestia-node's namespace type
// this is to avoid converting to and from app's type
namespace share.Namespace
// index represents the index of the blob's first share in the EDS.
// Only retrieved, on-chain blobs will have the index set. Default is -1.
index int
}
// NewBlobV0 constructs a new blob from the provided Namespace and data.
// The blob will be formatted as v0 shares.
func NewBlobV0(namespace share.Namespace, data []byte) (*Blob, error) {
return NewBlob(appconsts.ShareVersionZero, namespace, data)
}
// NewBlob constructs a new blob from the provided Namespace, data and share version.
func NewBlob(shareVersion uint8, namespace share.Namespace, data []byte) (*Blob, error) {
if len(data) == 0 || len(data) > appconsts.DefaultMaxBytes {
return nil, fmt.Errorf("blob data must be > 0 && <= %d, but it was %d bytes", appconsts.DefaultMaxBytes, len(data))
}
if err := namespace.ValidateForBlob(); err != nil {
return nil, err
}
blob := tmproto.Blob{
NamespaceId: namespace.ID(),
Data: data,
ShareVersion: uint32(shareVersion),
NamespaceVersion: uint32(namespace.Version()),
}
com, err := types.CreateCommitment(&blob)
if err != nil {
return nil, err
}
return &Blob{Blob: blob, Commitment: com, namespace: namespace, index: -1}, nil
}
// Namespace returns blob's namespace.
func (b *Blob) Namespace() share.Namespace {
return b.namespace
}
// Index returns the blob's first share index in the EDS.
// Only retrieved, on-chain blobs will have the index set. Default is -1.
func (b *Blob) Index() int {
return b.index
}
func (b *Blob) compareCommitments(com Commitment) bool {
return bytes.Equal(b.Commitment, com)
}
type jsonBlob struct {
Namespace share.Namespace `json:"namespace"`
Data []byte `json:"data"`
ShareVersion uint32 `json:"share_version"`
Commitment Commitment `json:"commitment"`
Index int `json:"index"`
}
func (b *Blob) MarshalJSON() ([]byte, error) {
blob := &jsonBlob{
Namespace: b.Namespace(),
Data: b.Data,
ShareVersion: b.ShareVersion,
Commitment: b.Commitment,
Index: b.index,
}
return json.Marshal(blob)
}
func (b *Blob) UnmarshalJSON(data []byte) error {
var blob jsonBlob
err := json.Unmarshal(data, &blob)
if err != nil {
return err
}
b.Blob.NamespaceVersion = uint32(blob.Namespace.Version())
b.Blob.NamespaceId = blob.Namespace.ID()
b.Blob.Data = blob.Data
b.Blob.ShareVersion = blob.ShareVersion
b.Commitment = blob.Commitment
b.namespace = blob.Namespace
b.index = blob.Index
return nil
}