Skip to content

Commit 38e5579

Browse files
authored
feat: DA client, remove interface part 1: copy subset of types needed for the client using blob rpc. (#2905)
Parent Epic: #2796 <!-- Please read and fill out this form before submitting your PR. Please make sure you have reviewed our contributors guide before submitting your first PR. NOTE: PR titles should follow semantic commits: https://www.conventionalcommits.org/en/v1.0.0/ --> ## Overview <!-- Please provide an explanation of the PR, including the appropriate context, background, goal, and rationale. If there is an issue with this information, please provide a tl;dr and link the issue. Ex: Closes #<issue number> -->
1 parent 5ee785f commit 38e5579

File tree

6 files changed

+252
-0
lines changed

6 files changed

+252
-0
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ require (
88
connectrpc.com/connect v1.19.1
99
connectrpc.com/grpcreflect v1.3.0
1010
github.com/celestiaorg/go-header v0.7.4
11+
github.com/celestiaorg/go-square/merkle v0.0.0-20240627094109-7d01436067a3
1112
github.com/celestiaorg/go-square/v3 v3.0.2
13+
github.com/celestiaorg/nmt v0.24.2
1214
github.com/celestiaorg/utils v0.1.0
1315
github.com/evstack/ev-node/core v1.0.0-beta.5
1416
github.com/go-kit/kit v0.13.0

go.sum

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ github.com/celestiaorg/go-header v0.7.4 h1:kQx3bVvKV+H2etxRi4IUuby5VQydBONx3giHF
2626
github.com/celestiaorg/go-header v0.7.4/go.mod h1:eX9iTSPthVEAlEDLux40ZT/olXPGhpxHd+mEzJeDhd0=
2727
github.com/celestiaorg/go-libp2p-messenger v0.2.2 h1:osoUfqjss7vWTIZrrDSy953RjQz+ps/vBFE7bychLEc=
2828
github.com/celestiaorg/go-libp2p-messenger v0.2.2/go.mod h1:oTCRV5TfdO7V/k6nkx7QjQzGrWuJbupv+0o1cgnY2i4=
29+
github.com/celestiaorg/go-square/merkle v0.0.0-20240627094109-7d01436067a3 h1:wP84mtwOCVNOTfS3zErICjxKLnh74Z1uf+tdrlSFjVM=
30+
github.com/celestiaorg/go-square/merkle v0.0.0-20240627094109-7d01436067a3/go.mod h1:86qIYnEhmn/hfW+xvw98NOI3zGaDEB3x8JGjYo2FqLs=
2931
github.com/celestiaorg/go-square/v3 v3.0.2 h1:eSQOgNII8inK9IhiBZ+6GADQeWbRq4HYY72BOgcduA4=
3032
github.com/celestiaorg/go-square/v3 v3.0.2/go.mod h1:oFReMLsSDMRs82ICFEeFQFCqNvwdsbIM1BzCcb0f7dM=
33+
github.com/celestiaorg/nmt v0.24.2 h1:LlpJSPOd6/Lw1Ig6HUhZuqiINHLka/ZSRTBzlNJpchg=
34+
github.com/celestiaorg/nmt v0.24.2/go.mod h1:vgLBpWBi8F5KLxTdXSwb7AU4NhiIQ1AQRGa+PzdcLEA=
3135
github.com/celestiaorg/utils v0.1.0 h1:WsP3O8jF7jKRgLNFmlDCwdThwOFMFxg0MnqhkLFVxPo=
3236
github.com/celestiaorg/utils v0.1.0/go.mod h1:vQTh7MHnvpIeCQZ2/Ph+w7K1R2UerDheZbgJEJD2hSU=
3337
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -123,6 +127,8 @@ github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
123127
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
124128
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
125129
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
130+
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
131+
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
126132
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
127133
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
128134
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -429,6 +435,12 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD
429435
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
430436
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
431437
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
438+
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
439+
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
440+
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
441+
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
442+
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
443+
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
432444
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
433445
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
434446
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=

pkg/blob/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# pkg/blob
2+
3+
This package is a **trimmed copy** of code from `celestia-node` to stay JSON-compatible with the blob RPC without importing the full Cosmos/Celestia dependency set.
4+
5+
## Upstream source
6+
- `blob.go` comes from `celestia-node/blob/blob.go` @ tag `v0.28.4` (release v0.28.4), with unused pieces removed (blob v1, proof helpers, share length calc, appconsts dependency, etc.).
7+
- `submit_options.go` mirrors the exported JSON fields of `celestia-node/state/tx_config.go` @ the same tag, leaving out functional options, defaults, and Cosmos keyring helpers.
8+
9+
## Why copy instead of import?
10+
- Avoids pulling Cosmos SDK / celestia-app dependencies into ev-node for the small surface we need (blob JSON and commitment for v0).
11+
- Keeps binary size and module graph smaller while remaining wire-compatible with celestia-node's blob service.
12+
13+
## Keeping it in sync
14+
- When celestia-node changes blob JSON or tx config fields, update this package manually:
15+
1. `diff -u pkg/blob/blob.go ../Celestia/celestia-node/blob/blob.go`
16+
2. `diff -u pkg/blob/submit_options.go ../Celestia/celestia-node/state/tx_config.go`
17+
3. Port only the fields/logic required for our RPC surface.
18+
- Consider adding a CI check that diffs these files against upstream to detect drift.

pkg/blob/blob.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package blob
2+
3+
// NOTE: This file is a trimmed copy of celestia-node's blob/blob.go
4+
// at release v0.28.4 (commit tag v0.28.4). We keep only the JSON-
5+
// compatible surface used by ev-node to avoid pulling celestia-app /
6+
// Cosmos-SDK dependencies. See pkg/blob/README.md for update guidance.
7+
8+
import (
9+
"bytes"
10+
"encoding/binary"
11+
"encoding/json"
12+
"fmt"
13+
14+
"github.com/celestiaorg/go-square/merkle"
15+
"github.com/celestiaorg/go-square/v3/inclusion"
16+
libshare "github.com/celestiaorg/go-square/v3/share"
17+
"github.com/celestiaorg/nmt"
18+
)
19+
20+
// Commitment is the Merkle subtree commitment for a blob.
21+
type Commitment []byte
22+
23+
// Proof is a set of NMT proofs used to verify a blob inclusion.
24+
// This mirrors celestia-node's blob.Proof shape.
25+
type Proof []*nmt.Proof
26+
27+
// DefaultMaxBlobSize is the default maximum blob size used by celestia-app (32 MiB).
28+
const DefaultMaxBlobSize = 32 * 1_048_576 // bytes
29+
30+
// subtreeRootThreshold is copied from celestia-app/v6 appconsts.SubtreeRootThreshold.
31+
// It controls the branching factor when generating commitments.
32+
const subtreeRootThreshold = 64
33+
34+
// Blob represents application-specific binary data that can be submitted to Celestia.
35+
// It is intentionally compatible with celestia-node's blob.Blob JSON shape.
36+
type Blob struct {
37+
*libshare.Blob `json:"blob"`
38+
39+
Commitment Commitment `json:"commitment"`
40+
41+
// index is the index of the blob's first share in the EDS.
42+
// Only blobs retrieved from the chain will have this set; default is -1.
43+
index int
44+
}
45+
46+
// NewBlobV0 builds a version 0 blob (the only version we currently need).
47+
func NewBlobV0(namespace libshare.Namespace, data []byte) (*Blob, error) {
48+
return NewBlob(libshare.ShareVersionZero, namespace, data, nil)
49+
}
50+
51+
// NewBlob constructs a new blob from the provided namespace, data, signer, and share version.
52+
// This is a lightly adapted copy of celestia-node/blob.NewBlob.
53+
func NewBlob(shareVersion uint8, namespace libshare.Namespace, data, signer []byte) (*Blob, error) {
54+
if err := namespace.ValidateForBlob(); err != nil {
55+
return nil, fmt.Errorf("invalid namespace: %w", err)
56+
}
57+
58+
libBlob, err := libshare.NewBlob(namespace, data, shareVersion, signer)
59+
if err != nil {
60+
return nil, fmt.Errorf("build blob: %w", err)
61+
}
62+
63+
com, err := inclusion.CreateCommitment(libBlob, merkle.HashFromByteSlices, subtreeRootThreshold)
64+
if err != nil {
65+
return nil, fmt.Errorf("create commitment: %w", err)
66+
}
67+
68+
return &Blob{
69+
Blob: libBlob,
70+
Commitment: com,
71+
index: -1,
72+
}, nil
73+
}
74+
75+
// Namespace returns the blob namespace.
76+
func (b *Blob) Namespace() libshare.Namespace {
77+
return b.Blob.Namespace()
78+
}
79+
80+
// Index returns the blob's first share index in the EDS (or -1 if unknown).
81+
func (b *Blob) Index() int {
82+
return b.index
83+
}
84+
85+
// MarshalJSON matches celestia-node's blob JSON encoding.
86+
func (b *Blob) MarshalJSON() ([]byte, error) {
87+
type jsonBlob struct {
88+
Namespace []byte `json:"namespace"`
89+
Data []byte `json:"data"`
90+
ShareVersion uint8 `json:"share_version"`
91+
Commitment Commitment `json:"commitment"`
92+
Signer []byte `json:"signer,omitempty"`
93+
Index int `json:"index"`
94+
}
95+
96+
jb := &jsonBlob{
97+
Namespace: b.Namespace().Bytes(),
98+
Data: b.Data(),
99+
ShareVersion: b.ShareVersion(),
100+
Commitment: b.Commitment,
101+
Signer: b.Signer(),
102+
Index: b.index,
103+
}
104+
return json.Marshal(jb)
105+
}
106+
107+
// UnmarshalJSON matches celestia-node's blob JSON decoding.
108+
func (b *Blob) UnmarshalJSON(data []byte) error {
109+
type jsonBlob struct {
110+
Namespace []byte `json:"namespace"`
111+
Data []byte `json:"data"`
112+
ShareVersion uint8 `json:"share_version"`
113+
Commitment Commitment `json:"commitment"`
114+
Signer []byte `json:"signer,omitempty"`
115+
Index int `json:"index"`
116+
}
117+
118+
var jb jsonBlob
119+
if err := json.Unmarshal(data, &jb); err != nil {
120+
return err
121+
}
122+
123+
ns, err := libshare.NewNamespaceFromBytes(jb.Namespace)
124+
if err != nil {
125+
return err
126+
}
127+
128+
blob, err := NewBlob(jb.ShareVersion, ns, jb.Data, jb.Signer)
129+
if err != nil {
130+
return err
131+
}
132+
133+
blob.Commitment = jb.Commitment
134+
blob.index = jb.Index
135+
*b = *blob
136+
return nil
137+
}
138+
139+
// MakeID constructs a blob ID by prefixing the commitment with the height (little endian).
140+
func MakeID(height uint64, commitment Commitment) []byte {
141+
id := make([]byte, 8+len(commitment))
142+
binary.LittleEndian.PutUint64(id, height)
143+
copy(id[8:], commitment)
144+
return id
145+
}
146+
147+
// SplitID splits a blob ID into height and commitment.
148+
// If the ID is malformed, it returns height 0 and nil commitment.
149+
func SplitID(id []byte) (uint64, Commitment) {
150+
if len(id) <= 8 {
151+
return 0, nil
152+
}
153+
return binary.LittleEndian.Uint64(id[:8]), id[8:]
154+
}
155+
156+
// EqualCommitment compares the blob's commitment with the provided one.
157+
func (b *Blob) EqualCommitment(com Commitment) bool {
158+
return bytes.Equal(b.Commitment, com)
159+
}

pkg/blob/blob_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package blob
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
libshare "github.com/celestiaorg/go-square/v3/share"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestMakeAndSplitID(t *testing.T) {
12+
id := MakeID(42, []byte{0x01, 0x02, 0x03})
13+
height, com := SplitID(id)
14+
require.Equal(t, uint64(42), height)
15+
require.Equal(t, []byte{0x01, 0x02, 0x03}, []byte(com))
16+
}
17+
18+
func TestBlobJSONRoundTrip(t *testing.T) {
19+
ns := libshare.MustNewV0Namespace([]byte("test-ids"))
20+
21+
blob, err := NewBlobV0(ns, []byte("hello"))
22+
require.NoError(t, err)
23+
require.NotEmpty(t, blob.Commitment)
24+
25+
encoded, err := json.Marshal(blob)
26+
require.NoError(t, err)
27+
28+
var decoded Blob
29+
require.NoError(t, json.Unmarshal(encoded, &decoded))
30+
31+
require.Equal(t, blob.Namespace().Bytes(), decoded.Namespace().Bytes())
32+
require.Equal(t, blob.Data(), decoded.Data())
33+
require.Equal(t, blob.Commitment, decoded.Commitment)
34+
}

pkg/blob/submit_options.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package blob
2+
3+
// NOTE: This mirrors the exported JSON shape of celestia-node/state/tx_config.go
4+
// at release v0.28.4, pared down to avoid importing Cosmos-SDK and
5+
// celestia-app packages. See pkg/blob/README.md for rationale and sync tips.
6+
7+
// TxPriority mirrors celestia-node/state.TxPriority to preserve JSON compatibility.
8+
type TxPriority int
9+
10+
const (
11+
TxPriorityLow TxPriority = iota + 1
12+
TxPriorityMedium
13+
TxPriorityHigh
14+
)
15+
16+
// SubmitOptions is a pared-down copy of celestia-node/state.TxConfig JSON shape.
17+
// Only exported fields are marshalled to match the RPC expectation of the blob service.
18+
type SubmitOptions struct {
19+
GasPrice float64 `json:"gas_price,omitempty"`
20+
IsGasPriceSet bool `json:"is_gas_price_set,omitempty"`
21+
MaxGasPrice float64 `json:"max_gas_price,omitempty"`
22+
Gas uint64 `json:"gas,omitempty"`
23+
TxPriority TxPriority `json:"tx_priority,omitempty"`
24+
KeyName string `json:"key_name,omitempty"`
25+
SignerAddress string `json:"signer_address,omitempty"`
26+
FeeGranterAddress string `json:"fee_granter_address,omitempty"`
27+
}

0 commit comments

Comments
 (0)