github.com/celestiaorg/celestia-node@v0.15.0-beta.1/blob/blob.go (about)

     1  package blob
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  
     9  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    10  
    11  	"github.com/celestiaorg/celestia-app/pkg/appconsts"
    12  	"github.com/celestiaorg/celestia-app/pkg/shares"
    13  	"github.com/celestiaorg/celestia-app/x/blob/types"
    14  	"github.com/celestiaorg/nmt"
    15  
    16  	"github.com/celestiaorg/celestia-node/share"
    17  )
    18  
    19  // Commitment is a Merkle Root of the subtree built from shares of the Blob.
    20  // It is computed by splitting the blob into shares and building the Merkle subtree to be included
    21  // after Submit.
    22  type Commitment []byte
    23  
    24  func (com Commitment) String() string {
    25  	return string(com)
    26  }
    27  
    28  // Equal ensures that commitments are the same
    29  func (com Commitment) Equal(c Commitment) bool {
    30  	return bytes.Equal(com, c)
    31  }
    32  
    33  // Proof is a collection of nmt.Proofs that verifies the inclusion of the data.
    34  type Proof []*nmt.Proof
    35  
    36  func (p Proof) Len() int { return len(p) }
    37  
    38  func (p Proof) MarshalJSON() ([]byte, error) {
    39  	proofs := make([]string, 0, len(p))
    40  	for _, proof := range p {
    41  		proofBytes, err := proof.MarshalJSON()
    42  		if err != nil {
    43  			return nil, err
    44  		}
    45  		proofs = append(proofs, string(proofBytes))
    46  	}
    47  	return json.Marshal(proofs)
    48  }
    49  
    50  func (p *Proof) UnmarshalJSON(b []byte) error {
    51  	var proofs []string
    52  	if err := json.Unmarshal(b, &proofs); err != nil {
    53  		return err
    54  	}
    55  	for _, proof := range proofs {
    56  		var nmtProof nmt.Proof
    57  		if err := nmtProof.UnmarshalJSON([]byte(proof)); err != nil {
    58  			return err
    59  		}
    60  		*p = append(*p, &nmtProof)
    61  	}
    62  	return nil
    63  }
    64  
    65  // equal is a temporary method that compares two proofs.
    66  // should be removed in BlobService V1.
    67  func (p Proof) equal(input Proof) error {
    68  	if p.Len() != input.Len() {
    69  		return ErrInvalidProof
    70  	}
    71  
    72  	for i, proof := range p {
    73  		pNodes := proof.Nodes()
    74  		inputNodes := input[i].Nodes()
    75  		for i, node := range pNodes {
    76  			if !bytes.Equal(node, inputNodes[i]) {
    77  				return ErrInvalidProof
    78  			}
    79  		}
    80  
    81  		if proof.Start() != input[i].Start() || proof.End() != input[i].End() {
    82  			return ErrInvalidProof
    83  		}
    84  
    85  		if !bytes.Equal(proof.LeafHash(), input[i].LeafHash()) {
    86  			return ErrInvalidProof
    87  		}
    88  
    89  	}
    90  	return nil
    91  }
    92  
    93  // Blob represents any application-specific binary data that anyone can submit to Celestia.
    94  type Blob struct {
    95  	types.Blob `json:"blob"`
    96  
    97  	Commitment Commitment `json:"commitment"`
    98  
    99  	// the celestia-node's namespace type
   100  	// this is to avoid converting to and from app's type
   101  	namespace share.Namespace
   102  }
   103  
   104  // NewBlobV0 constructs a new blob from the provided Namespace and data.
   105  // The blob will be formatted as v0 shares.
   106  func NewBlobV0(namespace share.Namespace, data []byte) (*Blob, error) {
   107  	return NewBlob(appconsts.ShareVersionZero, namespace, data)
   108  }
   109  
   110  // NewBlob constructs a new blob from the provided Namespace, data and share version.
   111  func NewBlob(shareVersion uint8, namespace share.Namespace, data []byte) (*Blob, error) {
   112  	if len(data) == 0 || len(data) > appconsts.DefaultMaxBytes {
   113  		return nil, fmt.Errorf("blob data must be > 0 && <= %d, but it was %d bytes", appconsts.DefaultMaxBytes, len(data))
   114  	}
   115  	if err := namespace.ValidateForBlob(); err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	blob := tmproto.Blob{
   120  		NamespaceId:      namespace.ID(),
   121  		Data:             data,
   122  		ShareVersion:     uint32(shareVersion),
   123  		NamespaceVersion: uint32(namespace.Version()),
   124  	}
   125  
   126  	com, err := types.CreateCommitment(&blob)
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	return &Blob{Blob: blob, Commitment: com, namespace: namespace}, nil
   131  }
   132  
   133  // Namespace returns blob's namespace.
   134  func (b *Blob) Namespace() share.Namespace {
   135  	return b.namespace
   136  }
   137  
   138  type jsonBlob struct {
   139  	Namespace    share.Namespace `json:"namespace"`
   140  	Data         []byte          `json:"data"`
   141  	ShareVersion uint32          `json:"share_version"`
   142  	Commitment   Commitment      `json:"commitment"`
   143  }
   144  
   145  func (b *Blob) MarshalJSON() ([]byte, error) {
   146  	blob := &jsonBlob{
   147  		Namespace:    b.Namespace(),
   148  		Data:         b.Data,
   149  		ShareVersion: b.ShareVersion,
   150  		Commitment:   b.Commitment,
   151  	}
   152  	return json.Marshal(blob)
   153  }
   154  
   155  func (b *Blob) UnmarshalJSON(data []byte) error {
   156  	var blob jsonBlob
   157  	err := json.Unmarshal(data, &blob)
   158  	if err != nil {
   159  		return err
   160  	}
   161  
   162  	b.Blob.NamespaceVersion = uint32(blob.Namespace.Version())
   163  	b.Blob.NamespaceId = blob.Namespace.ID()
   164  	b.Blob.Data = blob.Data
   165  	b.Blob.ShareVersion = blob.ShareVersion
   166  	b.Commitment = blob.Commitment
   167  	b.namespace = blob.Namespace
   168  	return nil
   169  }
   170  
   171  // buildBlobsIfExist takes shares and tries building the Blobs from them.
   172  // It will build blobs either until appShares will be empty or the first incomplete blob will
   173  // appear, so in this specific case it will return all built blobs + remaining shares.
   174  func buildBlobsIfExist(appShares []shares.Share) ([]*Blob, []shares.Share, error) {
   175  	if len(appShares) == 0 {
   176  		return nil, nil, errors.New("empty shares received")
   177  	}
   178  	blobs := make([]*Blob, 0, len(appShares))
   179  	for {
   180  		length, err := appShares[0].SequenceLen()
   181  		if err != nil {
   182  			return nil, nil, err
   183  		}
   184  
   185  		amount := shares.SparseSharesNeeded(length)
   186  		if amount > len(appShares) {
   187  			return blobs, appShares, nil
   188  		}
   189  
   190  		b, err := parseShares(appShares[:amount])
   191  		if err != nil {
   192  			return nil, nil, err
   193  		}
   194  
   195  		// only 1 blob will be created bc we passed the exact amount of shares
   196  		blobs = append(blobs, b[0])
   197  
   198  		if amount == len(appShares) {
   199  			return blobs, nil, nil
   200  		}
   201  		appShares = appShares[amount:]
   202  	}
   203  }