-
Notifications
You must be signed in to change notification settings - Fork 276
/
blob_tx.go
113 lines (98 loc) · 2.7 KB
/
blob_tx.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
package types
import (
"bytes"
"github.com/celestiaorg/celestia-app/pkg/appconsts"
shares "github.com/celestiaorg/celestia-app/pkg/shares"
"github.com/celestiaorg/nmt/namespace"
"github.com/cosmos/cosmos-sdk/client"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
)
// Blob wraps the tendermint type so that users can simply import this one.
type Blob = tmproto.Blob
// NewBlob creates a new coretypes.Blob from the provided data after performing
// basic stateless checks over it.
func NewBlob(ns namespace.ID, blob []byte) (*Blob, error) {
err := ValidateBlobNamespaceID(ns)
if err != nil {
return nil, err
}
if len(blob) == 0 {
return nil, ErrZeroBlobSize
}
return &tmproto.Blob{
NamespaceId: ns,
Data: blob,
ShareVersion: uint32(appconsts.DefaultShareVersion),
}, nil
}
// ValidateBlobTx performs stateless checks on the BlobTx to ensure that the
// blobs attached to the transaction are valid.
func ValidateBlobTx(txcfg client.TxEncodingConfig, bTx tmproto.BlobTx) error {
sdkTx, err := txcfg.TxDecoder()(bTx.Tx)
if err != nil {
return err
}
// TODO: remove this check once support for multiple sdk.Msgs in a BlobTx is
// supported.
msgs := sdkTx.GetMsgs()
if len(msgs) != 1 {
return ErrMultipleMsgsInBlobTx
}
msg := msgs[0]
pfb, ok := msg.(*MsgPayForBlobs)
if !ok {
return ErrNoPFB
}
err = pfb.ValidateBasic()
if err != nil {
return err
}
// perform basic checks on the blobs
sizes := make([]uint32, len(bTx.Blobs))
for i, pblob := range bTx.Blobs {
sizes[i] = uint32(len(pblob.Data))
}
err = ValidateBlobs(bTx.Blobs...)
if err != nil {
return err
}
// check that the info in the pfb matches that in the blobs
if !equalSlices(sizes, pfb.BlobSizes) {
return ErrBlobSizeMismatch.Wrapf("actual %v declared %v", sizes, pfb.BlobSizes)
}
for i := range pfb.NamespaceIds {
// check that the metadata matches
if !bytes.Equal(bTx.Blobs[i].NamespaceId, pfb.NamespaceIds[i]) {
return ErrNamespaceMismatch.Wrapf("%v %v", bTx.Blobs[i].NamespaceId, pfb.NamespaceIds[i])
}
}
// verify that the commitment of the blob matches that of the PFB
for i, commitment := range pfb.ShareCommitments {
calculatedCommit, err := CreateCommitment(bTx.Blobs[i])
if err != nil {
return ErrCalculateCommit
}
if !bytes.Equal(calculatedCommit, commitment) {
return ErrInvalidShareCommit
}
}
return nil
}
func BlobTxSharesUsed(btx tmproto.BlobTx) int {
sharesUsed := 0
for _, blob := range btx.Blobs {
sharesUsed += shares.SparseSharesNeeded(uint32(len(blob.Data)))
}
return sharesUsed
}
func equalSlices[T comparable](a, b []T) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}