github.com/celestiaorg/celestia-node@v0.15.0-beta.1/nodebuilder/da/service.go (about) 1 package da 2 3 import ( 4 "context" 5 "encoding/binary" 6 "fmt" 7 "strings" 8 9 logging "github.com/ipfs/go-log/v2" 10 "github.com/rollkit/go-da" 11 12 "github.com/celestiaorg/celestia-app/pkg/appconsts" 13 14 "github.com/celestiaorg/celestia-node/blob" 15 nodeblob "github.com/celestiaorg/celestia-node/nodebuilder/blob" 16 "github.com/celestiaorg/celestia-node/share" 17 ) 18 19 var _ da.DA = (*Service)(nil) 20 21 var log = logging.Logger("go-da") 22 23 // heightLen is a length (in bytes) of serialized height. 24 // 25 // This is 8 as uint64 consist of 8 bytes. 26 const heightLen = 8 27 28 type Service struct { 29 blobServ nodeblob.Module 30 } 31 32 func NewService(blobMod nodeblob.Module) *Service { 33 return &Service{ 34 blobServ: blobMod, 35 } 36 } 37 38 // MaxBlobSize returns the max blob size 39 func (s *Service) MaxBlobSize(context.Context) (uint64, error) { 40 return appconsts.DefaultMaxBytes, nil 41 } 42 43 // Get returns Blob for each given ID, or an error. 44 func (s *Service) Get(ctx context.Context, ids []da.ID, ns da.Namespace) ([]da.Blob, error) { 45 blobs := make([]da.Blob, 0, len(ids)) 46 for _, id := range ids { 47 height, commitment := SplitID(id) 48 log.Debugw("getting blob", "height", height, "commitment", commitment, "namespace", share.Namespace(ns)) 49 currentBlob, err := s.blobServ.Get(ctx, height, ns, commitment) 50 log.Debugw("got blob", "height", height, "commitment", commitment, "namespace", share.Namespace(ns)) 51 if err != nil { 52 return nil, err 53 } 54 blobs = append(blobs, currentBlob.Data) 55 } 56 return blobs, nil 57 } 58 59 // GetIDs returns IDs of all Blobs located in DA at given height. 60 func (s *Service) GetIDs(ctx context.Context, height uint64, namespace da.Namespace) ([]da.ID, error) { 61 var ids []da.ID //nolint:prealloc 62 log.Debugw("getting ids", "height", height, "namespace", share.Namespace(namespace)) 63 blobs, err := s.blobServ.GetAll(ctx, height, []share.Namespace{namespace}) 64 log.Debugw("got ids", "height", height, "namespace", share.Namespace(namespace)) 65 if err != nil { 66 if strings.Contains(err.Error(), blob.ErrBlobNotFound.Error()) { 67 return nil, nil 68 } 69 return nil, err 70 } 71 for _, b := range blobs { 72 ids = append(ids, MakeID(height, b.Commitment)) 73 } 74 return ids, nil 75 } 76 77 // GetProofs returns inclusion Proofs for all Blobs located in DA at given height. 78 func (s *Service) GetProofs(ctx context.Context, ids []da.ID, namespace da.Namespace) ([]da.Proof, error) { 79 proofs := make([]da.Proof, len(ids)) 80 for i, id := range ids { 81 height, commitment := SplitID(id) 82 proof, err := s.blobServ.GetProof(ctx, height, namespace, commitment) 83 if err != nil { 84 return nil, err 85 } 86 proofs[i], err = proof.MarshalJSON() 87 if err != nil { 88 return nil, err 89 } 90 } 91 return proofs, nil 92 } 93 94 // Commit creates a Commitment for each given Blob. 95 func (s *Service) Commit(_ context.Context, daBlobs []da.Blob, namespace da.Namespace) ([]da.Commitment, error) { 96 _, commitments, err := s.blobsAndCommitments(daBlobs, namespace) 97 return commitments, err 98 } 99 100 // Submit submits the Blobs to Data Availability layer. 101 func (s *Service) Submit( 102 ctx context.Context, 103 daBlobs []da.Blob, 104 gasPrice float64, 105 namespace da.Namespace, 106 ) ([]da.ID, error) { 107 blobs, _, err := s.blobsAndCommitments(daBlobs, namespace) 108 if err != nil { 109 return nil, err 110 } 111 112 height, err := s.blobServ.Submit(ctx, blobs, blob.GasPrice(gasPrice)) 113 if err != nil { 114 log.Error("failed to submit blobs", "height", height, "gas price", gasPrice) 115 return nil, err 116 } 117 log.Info("successfully submitted blobs", "height", height, "gas price", gasPrice) 118 ids := make([]da.ID, len(blobs)) 119 for i, blob := range blobs { 120 ids[i] = MakeID(height, blob.Commitment) 121 } 122 return ids, nil 123 } 124 125 // blobsAndCommitments converts []da.Blob to []*blob.Blob and generates corresponding 126 // []da.Commitment 127 func (s *Service) blobsAndCommitments( 128 daBlobs []da.Blob, namespace da.Namespace, 129 ) ([]*blob.Blob, []da.Commitment, error) { 130 blobs := make([]*blob.Blob, 0, len(daBlobs)) 131 commitments := make([]da.Commitment, 0, len(daBlobs)) 132 for _, daBlob := range daBlobs { 133 b, err := blob.NewBlobV0(namespace, daBlob) 134 if err != nil { 135 return nil, nil, err 136 } 137 blobs = append(blobs, b) 138 139 commitments = append(commitments, b.Commitment) 140 } 141 return blobs, commitments, nil 142 } 143 144 // Validate validates Commitments against the corresponding Proofs. This should be possible without 145 // retrieving the Blobs. 146 func (s *Service) Validate( 147 ctx context.Context, 148 ids []da.ID, 149 daProofs []da.Proof, 150 namespace da.Namespace, 151 ) ([]bool, error) { 152 included := make([]bool, len(ids)) 153 proofs := make([]*blob.Proof, len(ids)) 154 for i, daProof := range daProofs { 155 blobProof := &blob.Proof{} 156 err := blobProof.UnmarshalJSON(daProof) 157 if err != nil { 158 return nil, err 159 } 160 proofs[i] = blobProof 161 } 162 for i, id := range ids { 163 height, commitment := SplitID(id) 164 // TODO(tzdybal): for some reason, if proof doesn't match commitment, API returns (false, "blob: 165 // invalid proof") but analysis of the code in celestia-node implies this should never happen - 166 // maybe it's caused by openrpc? there is no way of gently handling errors here, but returned 167 // value is fine for us 168 fmt.Println("proof", proofs[i] == nil, "commitment", commitment == nil) 169 isIncluded, _ := s.blobServ.Included(ctx, height, namespace, proofs[i], commitment) 170 included = append(included, isIncluded) 171 } 172 return included, nil 173 } 174 175 func MakeID(height uint64, commitment da.Commitment) da.ID { 176 id := make([]byte, heightLen+len(commitment)) 177 binary.LittleEndian.PutUint64(id, height) 178 copy(id[heightLen:], commitment) 179 return id 180 } 181 182 func SplitID(id da.ID) (uint64, da.Commitment) { 183 if len(id) <= heightLen { 184 return 0, nil 185 } 186 commitment := id[heightLen:] 187 return binary.LittleEndian.Uint64(id[:heightLen]), commitment 188 }