diff --git a/da/celestia/celestia.go b/da/celestia/celestia.go index 3501b77254c..210a5af67de 100644 --- a/da/celestia/celestia.go +++ b/da/celestia/celestia.go @@ -2,18 +2,21 @@ package celestia import ( "context" + "encoding/hex" "encoding/json" "fmt" "strings" "time" + "cosmossdk.io/math" "github.com/gogo/protobuf/proto" ds "github.com/ipfs/go-datastore" - "github.com/celestiaorg/go-cnc" - openrpc "github.com/rollkit/celestia-openrpc" + "github.com/rollkit/celestia-openrpc/types/blob" + "github.com/rollkit/celestia-openrpc/types/share" + openrpcns "github.com/rollkit/celestia-openrpc/types/namespace" "github.com/rollkit/rollkit/da" "github.com/rollkit/rollkit/log" "github.com/rollkit/rollkit/types" @@ -22,12 +25,11 @@ import ( // DataAvailabilityLayerClient use celestia-node public API. type DataAvailabilityLayerClient struct { - _ *openrpc.Client - client *cnc.Client + rpc *openrpc.Client - namespaceID types.NamespaceID - config Config - logger log.Logger + namespace openrpcns.Namespace + config Config + logger log.Logger } var _ da.DataAvailabilityLayerClient = &DataAvailabilityLayerClient{} @@ -35,17 +37,20 @@ var _ da.BlockRetriever = &DataAvailabilityLayerClient{} // Config stores Celestia DALC configuration parameters. type Config struct { - BaseURL string `json:"base_url"` - Timeout time.Duration `json:"timeout"` - Fee int64 `json:"fee"` - GasLimit uint64 `json:"gas_limit"` + AuthToken string `json:"auth_token"` + BaseURL string `json:"base_url"` + Timeout time.Duration `json:"timeout"` + Fee int64 `json:"fee"` + GasLimit uint64 `json:"gas_limit"` } // Init initializes DataAvailabilityLayerClient instance. -func (c *DataAvailabilityLayerClient) Init( - namespaceID types.NamespaceID, config []byte, kvStore ds.Datastore, logger log.Logger, -) error { - c.namespaceID = namespaceID +func (c *DataAvailabilityLayerClient) Init(namespaceID types.NamespaceID, config []byte, kvStore ds.Datastore, logger log.Logger) error { + namespace, err := share.NewBlobNamespaceV0(namespaceID[:]) + if err != nil { + return err + } + c.namespace = namespace.ToAppNamespace() c.logger = logger if len(config) > 0 { @@ -59,7 +64,7 @@ func (c *DataAvailabilityLayerClient) Init( func (c *DataAvailabilityLayerClient) Start() error { c.logger.Info("starting Celestia Data Availability Layer Client", "baseURL", c.config.BaseURL) var err error - c.client, err = cnc.NewClient(c.config.BaseURL, cnc.WithTimeout(c.config.Timeout)) + c.rpc, err = openrpc.NewClient(context.Background(), c.config.BaseURL, c.config.AuthToken) return err } @@ -71,7 +76,17 @@ func (c *DataAvailabilityLayerClient) Stop() error { // SubmitBlock submits a block to DA layer. func (c *DataAvailabilityLayerClient) SubmitBlock(ctx context.Context, block *types.Block) da.ResultSubmitBlock { - blob, err := block.MarshalBinary() + data, err := block.MarshalBinary() + if err != nil { + return da.ResultSubmitBlock{ + BaseResult: da.BaseResult{ + Code: da.StatusError, + Message: err.Error(), + }, + } + } + + blockBlob, err := blob.NewBlobV0(c.namespace.Bytes(), data) if err != nil { return da.ResultSubmitBlock{ BaseResult: da.BaseResult{ @@ -81,8 +96,9 @@ func (c *DataAvailabilityLayerClient) SubmitBlock(ctx context.Context, block *ty } } - txResponse, err := c.client.SubmitPFB(ctx, c.namespaceID, blob, c.config.Fee, c.config.GasLimit) + blobs := []*blob.Blob{blockBlob} + txResponse, err := c.rpc.State.SubmitPayForBlob(ctx, math.NewInt(c.config.Fee), c.config.GasLimit, blobs) if err != nil { return da.ResultSubmitBlock{ BaseResult: da.BaseResult{ @@ -92,6 +108,10 @@ func (c *DataAvailabilityLayerClient) SubmitBlock(ctx context.Context, block *ty } } + c.logger.Debug("successfully submitted PayForBlob transaction", + "fee", c.config.Fee, "gasLimit", c.config.GasLimit, + "daHeight", txResponse.Height, "daTxHash", txResponse.TxHash) + if txResponse.Code != 0 { return da.ResultSubmitBlock{ BaseResult: da.BaseResult{ @@ -111,12 +131,36 @@ func (c *DataAvailabilityLayerClient) SubmitBlock(ctx context.Context, block *ty } // CheckBlockAvailability queries DA layer to check data availability of block at given height. -func (c *DataAvailabilityLayerClient) CheckBlockAvailability( - ctx context.Context, dataLayerHeight uint64, -) da.ResultCheckBlock { - shares, err := c.client.NamespacedShares(ctx, c.namespaceID, dataLayerHeight) - code := dataRequestErrorToStatus(err) - if code != da.StatusSuccess { +func (c *DataAvailabilityLayerClient) CheckBlockAvailability(ctx context.Context, dataLayerHeight uint64) da.ResultCheckBlock { + header, err := c.rpc.Header.GetByHeight(ctx, dataLayerHeight) + if err != nil { + return da.ResultCheckBlock{ + BaseResult: da.BaseResult{ + Code: da.StatusError, + Message: err.Error(), + }, + } + } + if header.DAH == nil { + return da.ResultCheckBlock{ + BaseResult: da.BaseResult{ + Code: da.StatusSuccess, + DAHeight: dataLayerHeight, + }, + DataAvailable: false, + } + } + err = c.rpc.Share.SharesAvailable(ctx, header.DAH) + if err != nil { + if strings.Contains(err.Error(), share.ErrNotAvailable.Error()) { + return da.ResultCheckBlock{ + BaseResult: da.BaseResult{ + Code: da.StatusSuccess, + DAHeight: dataLayerHeight, + }, + DataAvailable: false, + } + } return da.ResultCheckBlock{ BaseResult: da.BaseResult{ Code: da.StatusError, @@ -124,21 +168,19 @@ func (c *DataAvailabilityLayerClient) CheckBlockAvailability( }, } } - return da.ResultCheckBlock{ BaseResult: da.BaseResult{ Code: da.StatusSuccess, DAHeight: dataLayerHeight, }, - DataAvailable: len(shares) > 0, + DataAvailable: true, } } // RetrieveBlocks gets a batch of blocks from DA layer. -func (c *DataAvailabilityLayerClient) RetrieveBlocks( - ctx context.Context, dataLayerHeight uint64, -) da.ResultRetrieveBlocks { - data, err := c.client.NamespacedData(ctx, c.namespaceID, dataLayerHeight) +func (c *DataAvailabilityLayerClient) RetrieveBlocks(ctx context.Context, dataLayerHeight uint64) da.ResultRetrieveBlocks { + c.logger.Debug("trying to retrieve blob using Blob.GetAll", "daHeight", dataLayerHeight, "namespace", hex.EncodeToString(c.namespace.Bytes())) + blobs, err := c.rpc.Blob.GetAll(ctx, dataLayerHeight, []share.Namespace{c.namespace.Bytes()}) status := dataRequestErrorToStatus(err) if status != da.StatusSuccess { return da.ResultRetrieveBlocks{ @@ -149,10 +191,10 @@ func (c *DataAvailabilityLayerClient) RetrieveBlocks( } } - blocks := make([]*types.Block, len(data)) - for i, msg := range data { + blocks := make([]*types.Block, len(blobs)) + for i, blob := range blobs { var block pb.Block - err = proto.Unmarshal(msg, &block) + err = proto.Unmarshal(blob.Data, &block) if err != nil { c.logger.Error("failed to unmarshal block", "daHeight", dataLayerHeight, "position", i, "error", err) continue @@ -187,7 +229,8 @@ func dataRequestErrorToStatus(err error) da.StatusCode { strings.Contains(err.Error(), da.ErrNamespaceNotFound.Error()): return da.StatusSuccess case strings.Contains(err.Error(), da.ErrDataNotFound.Error()), - strings.Contains(err.Error(), da.ErrEDSNotFound.Error()): + strings.Contains(err.Error(), da.ErrEDSNotFound.Error()), + strings.Contains(err.Error(), da.ErrBlobNotFound.Error()): return da.StatusNotFound default: return da.StatusError diff --git a/da/celestia/mock/messages.go b/da/celestia/mock/messages.go index cf97dd2c5ab..961476e8ff5 100644 --- a/da/celestia/mock/messages.go +++ b/da/celestia/mock/messages.go @@ -1,45 +1,8 @@ package mock -import ( - "bytes" - "encoding/binary" -) - // This code is extracted from celestia-app. It's here to build shares from messages (serialized blocks). // TODO(tzdybal): if we stop using `/namespaced_shares` we can get rid of this file. -const ( - shareSize = 256 - namespaceSize = 8 - msgShareSize = shareSize - namespaceSize -) - -// splitMessage breaks the data in a message into the minimum number of -// namespaced shares -func splitMessage(rawData []byte, nid []byte) []NamespacedShare { - shares := make([]NamespacedShare, 0) - firstRawShare := append(append( - make([]byte, 0, shareSize), - nid...), - rawData[:msgShareSize]..., - ) - shares = append(shares, NamespacedShare{firstRawShare, nid}) - rawData = rawData[msgShareSize:] - for len(rawData) > 0 { - shareSizeOrLen := min(msgShareSize, len(rawData)) - rawShare := append(append( - make([]byte, 0, shareSize), - nid...), - rawData[:shareSizeOrLen]..., - ) - paddedShare := zeroPadIfNecessary(rawShare, shareSize) - share := NamespacedShare{paddedShare, nid} - shares = append(shares, share) - rawData = rawData[shareSizeOrLen:] - } - return shares -} - // Share contains the raw share data without the corresponding namespace. type Share []byte @@ -48,59 +11,3 @@ type NamespacedShare struct { Share ID []byte } - -func min(a, b int) int { - if a <= b { - return a - } - return b -} - -func zeroPadIfNecessary(share []byte, width int) []byte { - oldLen := len(share) - if oldLen < width { - missingBytes := width - oldLen - padByte := []byte{0} - padding := bytes.Repeat(padByte, missingBytes) - share = append(share, padding...) - return share - } - return share -} - -// marshalDelimited marshals the raw data (excluding the namespace) of this -// message and prefixes it with the length of that encoding. -func marshalDelimited(data []byte) ([]byte, error) { - lenBuf := make([]byte, binary.MaxVarintLen64) - length := uint64(len(data)) - n := binary.PutUvarint(lenBuf, length) - return append(lenBuf[:n], data...), nil -} - -// appendToShares appends raw data as shares. -// Used to build shares from blocks/messages. -func appendToShares(shares []NamespacedShare, nid []byte, rawData []byte) []NamespacedShare { - if len(rawData) <= msgShareSize { - rawShare := append(append( - make([]byte, 0, len(nid)+len(rawData)), - nid...), - rawData..., - ) - paddedShare := zeroPadIfNecessary(rawShare, shareSize) - share := NamespacedShare{paddedShare, nid} - shares = append(shares, share) - } else { // len(rawData) > msgShareSize - shares = append(shares, splitMessage(rawData, nid)...) - } - return shares -} - -type namespacedSharesResponse struct { - Shares []Share `json:"shares"` - Height uint64 `json:"height"` -} - -type namespacedDataResponse struct { - Data [][]byte `json:"data"` - Height uint64 `json:"height"` -} diff --git a/da/celestia/mock/server.go b/da/celestia/mock/server.go index 4a3e68ecd48..aee0a19551a 100644 --- a/da/celestia/mock/server.go +++ b/da/celestia/mock/server.go @@ -2,25 +2,55 @@ package mock import ( "context" - "encoding/hex" + "encoding/base64" "encoding/json" "errors" + "fmt" "net" "net/http" - "strconv" "time" mux2 "github.com/gorilla/mux" - "github.com/celestiaorg/go-cnc" - - "github.com/rollkit/rollkit/da" + "github.com/rollkit/celestia-openrpc/types/blob" + "github.com/rollkit/celestia-openrpc/types/header" + "github.com/rollkit/celestia-openrpc/types/sdk" mockda "github.com/rollkit/rollkit/da/mock" "github.com/rollkit/rollkit/log" "github.com/rollkit/rollkit/store" "github.com/rollkit/rollkit/types" ) +type ErrorCode int + +type respError struct { + Code ErrorCode `json:"code"` + Message string `json:"message"` + Meta json.RawMessage `json:"meta,omitempty"` +} + +func (e *respError) Error() string { + if e.Code >= -32768 && e.Code <= -32000 { + return fmt.Sprintf("RPC error (%d): %s", e.Code, e.Message) + } + return e.Message +} + +type request struct { + Jsonrpc string `json:"jsonrpc"` + ID interface{} `json:"id,omitempty"` + Method string `json:"method"` + Params json.RawMessage `json:"params"` + Meta map[string]string `json:"meta,omitempty"` +} + +type response struct { + Jsonrpc string `json:"jsonrpc"` + Result interface{} `json:"result,omitempty"` + ID interface{} `json:"id"` + Error *respError `json:"error,omitempty"` +} + // Server mocks celestia-node HTTP API. type Server struct { mock *mockda.DataAvailabilityLayerClient @@ -70,137 +100,143 @@ func (s *Server) Stop() { func (s *Server) getHandler() http.Handler { mux := mux2.NewRouter() - mux.HandleFunc("/submit_pfb", s.submit).Methods(http.MethodPost) - mux.HandleFunc("/namespaced_shares/{namespace}/height/{height}", s.shares).Methods(http.MethodGet) - mux.HandleFunc("/namespaced_data/{namespace}/height/{height}", s.data).Methods(http.MethodGet) + mux.HandleFunc("/", s.rpc).Methods(http.MethodPost) return mux } -func (s *Server) submit(w http.ResponseWriter, r *http.Request) { - req := cnc.SubmitPFBRequest{} +func (s *Server) rpc(w http.ResponseWriter, r *http.Request) { + var req request err := json.NewDecoder(r.Body).Decode(&req) if err != nil { s.writeError(w, err) return } - - block := types.Block{} - blockData, err := hex.DecodeString(req.Data) - if err != nil { - s.writeError(w, err) - return - } - err = block.UnmarshalBinary(blockData) - if err != nil { - s.writeError(w, err) - return - } - - res := s.mock.SubmitBlock(r.Context(), &block) - code := 0 - if res.Code != da.StatusSuccess { - code = 3 - } - - resp, err := json.Marshal(cnc.TxResponse{ - Height: int64(res.DAHeight), - Code: uint32(code), - RawLog: res.Message, - }) - if err != nil { - s.writeError(w, err) - return - } - - s.writeResponse(w, resp) -} - -func (s *Server) shares(w http.ResponseWriter, r *http.Request) { - height, err := parseHeight(r) - if err != nil { - s.writeError(w, err) - return - } - - res := s.mock.RetrieveBlocks(r.Context(), height) - if res.Code != da.StatusSuccess { - s.writeError(w, errors.New(res.Message)) - return - } - - var nShares []NamespacedShare - for _, block := range res.Blocks { - blob, err := block.MarshalBinary() + switch req.Method { + case "header.GetByHeight": + var params []interface{} + err := json.Unmarshal(req.Params, ¶ms) if err != nil { s.writeError(w, err) return } - delimited, err := marshalDelimited(blob) + if len(params) != 1 { + s.writeError(w, errors.New("expected 1 param: height (uint64)")) + return + } + height := uint64(params[0].(float64)) + dah := s.mock.GetHeaderByHeight(height) + resp := &response{ + Jsonrpc: "2.0", + Result: header.ExtendedHeader{ + DAH: dah, + }, + ID: req.ID, + Error: nil, + } + bytes, err := json.Marshal(resp) if err != nil { s.writeError(w, err) + return } - nShares = appendToShares(nShares, []byte{1, 2, 3, 4, 5, 6, 7, 8}, delimited) - } - shares := make([]Share, len(nShares)) - for i := range nShares { - shares[i] = nShares[i].Share - } - - resp, err := json.Marshal(namespacedSharesResponse{ - Shares: shares, - Height: res.DAHeight, - }) - if err != nil { - s.writeError(w, err) - return - } - - s.writeResponse(w, resp) -} - -func (s *Server) data(w http.ResponseWriter, r *http.Request) { - height, err := parseHeight(r) - if err != nil { - s.writeError(w, err) - return - } - - res := s.mock.RetrieveBlocks(r.Context(), height) - if res.Code != da.StatusSuccess { - s.writeError(w, errors.New(res.Message)) - return - } - - data := make([][]byte, len(res.Blocks)) - for i := range res.Blocks { - data[i], err = res.Blocks[i].MarshalBinary() + s.writeResponse(w, bytes) + case "blob.GetAll": + var params []interface{} + err := json.Unmarshal(req.Params, ¶ms) + if err != nil { + s.writeError(w, err) + return + } + if len(params) != 2 { + s.writeError(w, errors.New("expected 2 params: height (uint64), namespace (base64 string)")) + return + } + height := params[0].(float64) + nsBase64 := params[1].([]interface{})[0].(string) + ns, err := base64.StdEncoding.DecodeString(nsBase64) + if err != nil { + s.writeError(w, err) + return + } + block := s.mock.RetrieveBlocks(r.Context(), uint64(height)) + var blobs []blob.Blob + for _, block := range block.Blocks { + data, err := block.MarshalBinary() + if err != nil { + s.writeError(w, err) + return + } + blob, err := blob.NewBlobV0(ns, data) + if err != nil { + s.writeError(w, err) + return + } + blobs = append(blobs, *blob) + } + resp := &response{ + Jsonrpc: "2.0", + Result: blobs, + ID: req.ID, + Error: nil, + } + bytes, err := json.Marshal(resp) + if err != nil { + s.writeError(w, err) + return + } + s.writeResponse(w, bytes) + case "share.SharesAvailable": + resp := &response{ + Jsonrpc: "2.0", + ID: req.ID, + Error: nil, + } + bytes, err := json.Marshal(resp) + if err != nil { + s.writeError(w, err) + return + } + s.writeResponse(w, bytes) + case "state.SubmitPayForBlob": + var params []interface{} + err := json.Unmarshal(req.Params, ¶ms) + if err != nil { + s.writeError(w, err) + return + } + if len(params) != 3 { + s.writeError(w, errors.New("expected 3 params: fee (uint64), gaslimit (uint64), data (base64 string)")) + return + } + block := types.Block{} + blockBase64 := params[2].([]interface{})[0].(map[string]interface{})["data"].(string) + blockData, err := base64.StdEncoding.DecodeString(blockBase64) + if err != nil { + s.writeError(w, err) + return + } + err = block.UnmarshalBinary(blockData) if err != nil { s.writeError(w, err) return } - } - - resp, err := json.Marshal(namespacedDataResponse{ - Data: data, - Height: res.DAHeight, - }) - if err != nil { - s.writeError(w, err) - return - } - - s.writeResponse(w, resp) -} - -func parseHeight(r *http.Request) (uint64, error) { - vars := mux2.Vars(r) - height, err := strconv.ParseUint(vars["height"], 10, 64) - if err != nil { - return 0, err + res := s.mock.SubmitBlock(r.Context(), &block) + resp := &response{ + Jsonrpc: "2.0", + Result: &sdk.TxResponse{ + Height: int64(res.DAHeight), + }, + ID: req.ID, + Error: nil, + } + bytes, err := json.Marshal(resp) + if err != nil { + s.writeError(w, err) + return + } + s.writeResponse(w, bytes) } - return height, nil } func (s *Server) writeResponse(w http.ResponseWriter, payload []byte) { diff --git a/da/da.go b/da/da.go index 97095ed3c2d..d71d8adce17 100644 --- a/da/da.go +++ b/da/da.go @@ -15,6 +15,7 @@ var ( ErrDataNotFound = errors.New("data not found") // ErrNamespaceNotFound is used to indicate that the block contains data, but not for the requested namespace. ErrNamespaceNotFound = errors.New("namespace not found in data") + ErrBlobNotFound = errors.New("blob: not found") ErrEDSNotFound = errors.New("eds not found") ) diff --git a/da/mock/mock.go b/da/mock/mock.go index 7bb77c81e59..5760ea25745 100644 --- a/da/mock/mock.go +++ b/da/mock/mock.go @@ -1,14 +1,18 @@ package mock import ( + "bytes" "context" "encoding/hex" + "fmt" "math/rand" + "sync" "sync/atomic" "time" ds "github.com/ipfs/go-datastore" + "github.com/rollkit/celestia-openrpc/types/core" "github.com/rollkit/rollkit/da" "github.com/rollkit/rollkit/log" "github.com/rollkit/rollkit/store" @@ -18,8 +22,12 @@ import ( // DataAvailabilityLayerClient is intended only for usage in tests. // It does actually ensures DA - it stores data in-memory. type DataAvailabilityLayerClient struct { - logger log.Logger - dalcKV ds.Datastore + logger log.Logger + dalcKV ds.Datastore + + daHeaders map[uint64]*core.DataAvailabilityHeader + daHeadersLock sync.RWMutex + daHeight uint64 config config } @@ -38,6 +46,17 @@ func (m *DataAvailabilityLayerClient) Init(_ types.NamespaceID, config []byte, d m.logger = logger m.dalcKV = dalcKV m.daHeight = 1 + m.daHeaders = make(map[uint64]*core.DataAvailabilityHeader) + + eds, err := RandEDS(4) + if err != nil { + return err + } + dah := core.NewDataAvailabilityHeader(eds) + m.daHeadersLock.Lock() + m.daHeaders[m.daHeight] = &dah + m.daHeadersLock.Unlock() + if len(config) > 0 { var err error m.config.BlockTime, err = time.ParseDuration(string(config)) @@ -68,6 +87,51 @@ func (m *DataAvailabilityLayerClient) Stop() error { return nil } +// GetHeaderByHeight returns the header at the given height. +func (m *DataAvailabilityLayerClient) GetHeaderByHeight(height uint64) *core.DataAvailabilityHeader { + m.daHeadersLock.RLock() + dah := m.daHeaders[height] + m.daHeadersLock.RUnlock() + return dah +} + +func isEqual(headerA, headerB *core.DataAvailabilityHeader) bool { + if len(headerA.RowRoots) != len(headerB.RowRoots) { + return false + } + if len(headerA.ColumnRoots) != len(headerB.ColumnRoots) { + return false + } + for i, row := range headerA.RowRoots { + if !bytes.Equal(row, headerB.RowRoots[i]) { + return false + } + } + for i, col := range headerA.ColumnRoots { + if !bytes.Equal(col, headerB.ColumnRoots[i]) { + return false + } + } + return true +} + +// GetHeightByHeader returns the height for the given header. +func (m *DataAvailabilityLayerClient) GetHeightByHeader(dah *core.DataAvailabilityHeader) uint64 { + daHeight := atomic.LoadUint64(&m.daHeight) + for height := uint64(0); height < daHeight; height++ { + m.daHeadersLock.RLock() + header, ok := m.daHeaders[height] + m.daHeadersLock.RUnlock() + if !ok { + continue + } + if isEqual(header, dah) { + return height + } + } + return 0 +} + // SubmitBlock submits the passed in block to the DA layer. // This should create a transaction which (potentially) // triggers a state transition in the DA layer. @@ -146,4 +210,13 @@ func getKey(daHeight uint64, height uint64) ds.Key { func (m *DataAvailabilityLayerClient) updateDAHeight() { blockStep := rand.Uint64()%10 + 1 //nolint:gosec atomic.AddUint64(&m.daHeight, blockStep) + eds, err := RandEDS(4) + if err != nil { + fmt.Println(err) + return + } + dah := core.NewDataAvailabilityHeader(eds) + m.daHeadersLock.Lock() + m.daHeaders[atomic.LoadUint64(&m.daHeight)] = &dah + defer m.daHeadersLock.Unlock() } diff --git a/da/mock/util.go b/da/mock/util.go new file mode 100644 index 00000000000..048df9532cd --- /dev/null +++ b/da/mock/util.go @@ -0,0 +1,183 @@ +package mock + +import ( + "bytes" + "fmt" + "math/rand" + "sort" + "time" + + "github.com/celestiaorg/nmt" + "github.com/celestiaorg/rsmt2d" + + "github.com/rollkit/celestia-openrpc/types/appconsts" + "github.com/rollkit/celestia-openrpc/types/namespace" + "github.com/rollkit/celestia-openrpc/types/share" +) + +// Fulfills the rsmt2d.Tree interface and rsmt2d.TreeConstructorFn function +var ( + _ rsmt2d.TreeConstructorFn = NewConstructor(0) + _ rsmt2d.Tree = &ErasuredNamespacedMerkleTree{} +) + +// ErasuredNamespacedMerkleTree wraps NamespaceMerkleTree to conform to the +// rsmt2d.Tree interface while also providing the correct namespaces to the +// underlying NamespaceMerkleTree. It does this by adding the already included +// namespace to the first half of the tree, and then uses the parity namespace +// ID for each share pushed to the second half of the tree. This allows for the +// namespaces to be included in the erasure data, while also keeping the nmt +// library sufficiently general +type ErasuredNamespacedMerkleTree struct { + squareSize uint64 // note: this refers to the width of the original square before erasure-coded + options []nmt.Option + tree *nmt.NamespacedMerkleTree + // axisIndex is the index of the axis (row or column) that this tree is on. This is passed + // by rsmt2d and used to help determine which quadrant each leaf belongs to. + axisIndex uint64 + // shareIndex is the index of the share in a row or column that is being + // pushed to the tree. It is expected to be in the range: 0 <= shareIndex < + // 2*squareSize. shareIndex is used to help determine which quadrant each + // leaf belongs to, along with keeping track of how many leaves have been + // added to the tree so far. + shareIndex uint64 +} + +// NewErasuredNamespacedMerkleTree creates a new ErasuredNamespacedMerkleTree +// with an underlying NMT of namespace size `namespace.NamespaceSize` and with +// `ignoreMaxNamespace=true`. axisIndex is the index of the row or column that +// this tree is committing to. squareSize must be greater than zero. +func NewErasuredNamespacedMerkleTree(squareSize uint64, axisIndex uint, options ...nmt.Option) ErasuredNamespacedMerkleTree { + if squareSize == 0 { + panic("cannot create a ErasuredNamespacedMerkleTree of squareSize == 0") + } + options = append(options, nmt.NamespaceIDSize(namespace.NamespaceSize)) + options = append(options, nmt.IgnoreMaxNamespace(true)) + tree := nmt.New(namespace.NewBaseHashFunc(), options...) + return ErasuredNamespacedMerkleTree{squareSize: squareSize, options: options, tree: tree, axisIndex: uint64(axisIndex), shareIndex: 0} +} + +type constructor struct { + squareSize uint64 + opts []nmt.Option +} + +// NewConstructor creates a tree constructor function as required by rsmt2d to +// calculate the data root. It creates that tree using the +// wrapper.ErasuredNamespacedMerkleTree. +func NewConstructor(squareSize uint64, opts ...nmt.Option) rsmt2d.TreeConstructorFn { + return constructor{ + squareSize: squareSize, + opts: opts, + }.NewTree +} + +// NewTree creates a new rsmt2d.Tree using the +// wrapper.ErasuredNamespacedMerkleTree with predefined square size and +// nmt.Options +func (c constructor) NewTree(_ rsmt2d.Axis, axisIndex uint) rsmt2d.Tree { + newTree := NewErasuredNamespacedMerkleTree(c.squareSize, axisIndex, c.opts...) + return &newTree +} + +// Push adds the provided data to the underlying NamespaceMerkleTree, and +// automatically uses the first DefaultNamespaceIDLen number of bytes as the +// namespace unless the data pushed to the second half of the tree. Fulfills the +// rsmt.Tree interface. NOTE: panics if an error is encountered while pushing or +// if the tree size is exceeded. +func (w *ErasuredNamespacedMerkleTree) Push(data []byte) error { + if w.axisIndex+1 > 2*w.squareSize || w.shareIndex+1 > 2*w.squareSize { + return fmt.Errorf("pushed past predetermined square size: boundary at %d index at %d %d", 2*w.squareSize, w.axisIndex, w.shareIndex) + } + if len(data) < namespace.NamespaceSize { + return fmt.Errorf("data is too short to contain namespace ID") + } + nidAndData := make([]byte, namespace.NamespaceSize+len(data)) + copy(nidAndData[namespace.NamespaceSize:], data) + // use the parity namespace if the cell is not in Q0 of the extended data square + if w.isQuadrantZero() { + copy(nidAndData[:namespace.NamespaceSize], data[:namespace.NamespaceSize]) + } else { + copy(nidAndData[:namespace.NamespaceSize], namespace.ParitySharesNamespace.Bytes()) + } + err := w.tree.Push(nidAndData) + if err != nil { + return err + } + w.incrementShareIndex() + return nil +} + +// Root fulfills the rsmt.Tree interface by generating and returning the +// underlying NamespaceMerkleTree Root. +func (w *ErasuredNamespacedMerkleTree) Root() ([]byte, error) { + root, err := w.tree.Root() + if err != nil { + return nil, err + } + return root, nil +} + +// ProveRange returns a Merkle range proof for the leaf range [start, end] where `end` is non-inclusive. +func (w *ErasuredNamespacedMerkleTree) ProveRange(start, end int) (nmt.Proof, error) { + return w.tree.ProveRange(start, end) +} + +// incrementShareIndex increments the share index by one. +func (w *ErasuredNamespacedMerkleTree) incrementShareIndex() { + w.shareIndex++ +} + +// isQuadrantZero returns true if the current share index and axis index are both +// in the original data square. +func (w *ErasuredNamespacedMerkleTree) isQuadrantZero() bool { + return w.shareIndex < w.squareSize && w.axisIndex < w.squareSize +} + +// RandEDS generates EDS filled with the random data with the given size for original square. It +// uses require.TestingT to be able to take both a *testing.T and a *testing.B. +func RandEDS(size int) (*rsmt2d.ExtendedDataSquare, error) { + shares, err := RandShares(size * size) + if err != nil { + return nil, err + } + // recompute the eds + return rsmt2d.ComputeExtendedDataSquare(shares, share.DefaultRSMT2DCodec(), NewConstructor(uint64(size))) +} + +// RandShares generate 'total' amount of shares filled with random data. It uses require.TestingT +// to be able to take both a *testing.T and a *testing.B. +func RandShares(total int) ([]share.Share, error) { + if total&(total-1) != 0 { + return nil, fmt.Errorf("total must be power of 2: %d", total) + } + + var r = rand.New(rand.NewSource(time.Now().Unix())) //nolint:gosec + shares := make([]share.Share, total) + for i := range shares { + shr := make([]byte, appconsts.ShareSize) + copy(shr[:appconsts.NamespaceSize], RandNamespace()) + _, err := r.Read(shr[appconsts.NamespaceSize:]) + if err != nil { + return nil, err + } + shares[i] = shr + } + sort.Slice(shares, func(i, j int) bool { return bytes.Compare(shares[i], shares[j]) < 0 }) + + return shares, nil +} + +// RandNamespace generates random valid data namespace for testing purposes. +func RandNamespace() share.Namespace { + var r = rand.New(rand.NewSource(time.Now().Unix())) //nolint:gosec + rb := make([]byte, namespace.NamespaceVersionZeroIDSize) + r.Read(rb) // nolint:gosec + for { + namespace, _ := share.NewBlobNamespaceV0(rb) + if err := namespace.ValidateForData(); err != nil { + continue + } + return namespace + } +} diff --git a/da/test/da_test.go b/da/test/da_test.go index 06e051b32d9..52a10cb7a68 100644 --- a/da/test/da_test.go +++ b/da/test/da_test.go @@ -20,7 +20,15 @@ import ( "github.com/rollkit/rollkit/types" ) -var testNamespaceID = types.NamespaceID{0, 1, 2, 3, 4, 5, 6, 7} +var ( + testNamespaceID = types.NamespaceID{0, 1, 2, 3, 4, 5, 6, 7} + + testConfig = celestia.Config{ + BaseURL: "http://localhost:26658", + Timeout: 30 * time.Second, + GasLimit: 3000000, + } +) func TestMain(m *testing.M) { srv := startMockGRPCServ() @@ -53,7 +61,14 @@ func TestLifecycle(t *testing.T) { func doTestLifecycle(t *testing.T, dalc da.DataAvailabilityLayerClient) { require := require.New(t) - err := dalc.Init(testNamespaceID, []byte{}, nil, test.NewLogger(t)) + conf := []byte{} + if _, ok := dalc.(*mock.DataAvailabilityLayerClient); ok { + conf = []byte(mockDaBlockTime.String()) + } + if _, ok := dalc.(*celestia.DataAvailabilityLayerClient); ok { + conf, _ = json.Marshal(testConfig) + } + err := dalc.Init(testNamespaceID, conf, nil, test.NewLogger(t)) require.NoError(err) err = dalc.Start() @@ -82,12 +97,7 @@ func doTestDALC(t *testing.T, dalc da.DataAvailabilityLayerClient) { conf = []byte(mockDaBlockTime.String()) } if _, ok := dalc.(*celestia.DataAvailabilityLayerClient); ok { - config := celestia.Config{ - BaseURL: "http://localhost:26658", - Timeout: 30 * time.Second, - GasLimit: 3000000, - } - conf, _ = json.Marshal(config) + conf, _ = json.Marshal(testConfig) } kvStore, _ := store.NewDefaultInMemoryKVStore() err := dalc.Init(testNamespaceID, conf, kvStore, test.NewLogger(t)) @@ -151,12 +161,7 @@ func doTestRetrieve(t *testing.T, dalc da.DataAvailabilityLayerClient) { conf = []byte(mockDaBlockTime.String()) } if _, ok := dalc.(*celestia.DataAvailabilityLayerClient); ok { - config := celestia.Config{ - BaseURL: "http://localhost:26658", - Timeout: 30 * time.Second, - GasLimit: 3000000, - } - conf, _ = json.Marshal(config) + conf, _ = json.Marshal(testConfig) } kvStore, _ := store.NewDefaultInMemoryKVStore() err := dalc.Init(testNamespaceID, conf, kvStore, test.NewLogger(t)) diff --git a/go.mod b/go.mod index de54c2f4a74..616c61dd257 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,11 @@ module github.com/rollkit/rollkit go 1.18 require ( - github.com/celestiaorg/go-cnc v0.3.0 + cosmossdk.io/math v1.0.1 github.com/celestiaorg/go-fraud v0.1.1 github.com/celestiaorg/go-header v0.2.9 + github.com/celestiaorg/nmt v0.17.0 + github.com/celestiaorg/rsmt2d v0.9.0 github.com/celestiaorg/utils v0.1.0 github.com/dgraph-io/badger/v3 v3.2103.5 github.com/go-kit/kit v0.12.0 @@ -21,7 +23,7 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.9.3 github.com/multiformats/go-multiaddr v0.10.0 github.com/prometheus/client_golang v1.16.0 - github.com/rollkit/celestia-openrpc v0.0.0-20230531022214-505ff801e818 + github.com/rollkit/celestia-openrpc v0.1.0 github.com/rs/cors v1.9.0 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.16.0 @@ -34,15 +36,12 @@ require ( ) require ( - cosmossdk.io/math v1.0.1 // indirect github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect github.com/celestiaorg/go-libp2p-messenger v0.2.0 // indirect github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 // indirect - github.com/celestiaorg/nmt v0.15.0 // indirect - github.com/celestiaorg/rsmt2d v0.9.0 // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cometbft/cometbft-db v0.8.0 // indirect @@ -66,7 +65,6 @@ require ( github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/go-logr/logr v1.2.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-resty/resty/v2 v2.7.0 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/golang/glog v1.0.0 // indirect @@ -174,10 +172,10 @@ require ( golang.org/x/crypto v0.10.0 // indirect golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/mod v0.11.0 // indirect - golang.org/x/sync v0.2.0 // indirect + golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect - golang.org/x/tools v0.7.0 // indirect + golang.org/x/tools v0.10.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect gonum.org/v1/gonum v0.11.0 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect diff --git a/go.sum b/go.sum index 716ab6237eb..d89c3fbb065 100644 --- a/go.sum +++ b/go.sum @@ -45,10 +45,13 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= +github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= @@ -106,8 +109,6 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/celestiaorg/go-cnc v0.3.0 h1:eAVPNHGpx+2sBO7NZyQ1+VW8rzf6W4FQDlSq6aqSTsM= -github.com/celestiaorg/go-cnc v0.3.0/go.mod h1:zYzvHudSd1iNPuHBMyvZ1YvWou5aT9JXgtch9Tkaf70= github.com/celestiaorg/go-fraud v0.1.1 h1:XZdA5yvkOruoyzrZXVxk1kJ9SbkhJI2aZ+k60+xhbbk= github.com/celestiaorg/go-fraud v0.1.1/go.mod h1:yoNM35cKMAkt5Mi/Qx3Wi9bnPilLi8n6RpHZVglTUDs= github.com/celestiaorg/go-header v0.2.9 h1:18aFlkUgnSbEI7SU9PBqdwRu9XEKWRsmRiB72u8Mz/k= @@ -116,13 +117,15 @@ github.com/celestiaorg/go-libp2p-messenger v0.2.0 h1:/0MuPDcFamQMbw9xTZ73yImqgTO github.com/celestiaorg/go-libp2p-messenger v0.2.0/go.mod h1:s9PIhMi7ApOauIsfBcQwbr7m+HBzmVfDIS+QLdgzDSo= github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4 h1:CJdIpo8n5MFP2MwK0gSRcOVlDlFdQJO1p+FqdxYzmvc= github.com/celestiaorg/merkletree v0.0.0-20210714075610-a84dc3ddbbe4/go.mod h1:fzuHnhzj1pUygGz+1ZkB3uQbEUL4htqCGJ4Qs2LwMZA= -github.com/celestiaorg/nmt v0.15.0 h1:ID9QlMIeP6WK/iiGcfnYLu2qqVIq0UYe/dc3TVPt6EA= -github.com/celestiaorg/nmt v0.15.0/go.mod h1:GfwIvQPhUakn1modWxJ+rv8dUjJzuXg5H+MLFM1o7nY= +github.com/celestiaorg/nmt v0.17.0 h1:/k8YLwJvuHgT/jQ435zXKaDX811+sYEMXL4B/vYdSLU= +github.com/celestiaorg/nmt v0.17.0/go.mod h1:ZndCeAR4l9lxm7W51ouoyTo1cxhtFgK+4DpEIkxRA3A= github.com/celestiaorg/rsmt2d v0.9.0 h1:kon78I748ZqjNzI8OAqPN+2EImuZuanj/6gTh8brX3o= github.com/celestiaorg/rsmt2d v0.9.0/go.mod h1:E06nDxfoeBDltWRvTR9dLviiUZI5/6mLXAuhSJzz3Iw= github.com/celestiaorg/utils v0.1.0 h1:WsP3O8jF7jKRgLNFmlDCwdThwOFMFxg0MnqhkLFVxPo= github.com/celestiaorg/utils v0.1.0/go.mod h1:vQTh7MHnvpIeCQZ2/Ph+w7K1R2UerDheZbgJEJD2hSU= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= @@ -146,6 +149,7 @@ github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTF github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/containerd/continuity v0.4.1 h1:wQnVrjIyQ8vhU2sgOiL5T07jo+ouqc2bnKsv5/EqGhU= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -189,6 +193,9 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/docker/cli v24.0.2+incompatible h1:QdqR7znue1mtkXIJ+ruQMGQhpw2JzMJLRXp6zpzF6tM= +github.com/docker/docker v24.0.2+incompatible h1:eATx+oLz9WdNVkQrr0qjQ8HvRJ4bOOxfzEo8R+dA3cg= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= @@ -258,8 +265,6 @@ github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD87 github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -357,6 +362,7 @@ github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -432,6 +438,7 @@ github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -726,6 +733,7 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -828,6 +836,9 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= +github.com/opencontainers/runc v1.1.7 h1:y2EZDS8sNng4Ksf0GUYNhKbTShZJPJg1FiXJNH/uoCk= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -842,6 +853,8 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= +github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= @@ -930,8 +943,8 @@ github.com/regen-network/protobuf v1.3.2-alpha.regen.4/go.mod h1:/J8/bR1T/NXyIdQ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rollkit/celestia-openrpc v0.0.0-20230531022214-505ff801e818 h1:L7WGAGjCO01pKGmnFCm3cVs+kjga1okEsy1s5J0doR0= -github.com/rollkit/celestia-openrpc v0.0.0-20230531022214-505ff801e818/go.mod h1:eDBRyA4NoVLTGRXUxMb50/sW456UIptRAqixUqocg3Q= +github.com/rollkit/celestia-openrpc v0.1.0 h1:bsTpx6wUgXuWJVcIwqq3KVehnFjOtyujxop347m1S1I= +github.com/rollkit/celestia-openrpc v0.1.0/go.mod h1:YHMruyb6mJnm7QOFwz6DYSj5cKO8NYmi8osLYwEjbB0= github.com/rollkit/cometbft v0.0.0-20230524013001-2968c8b8b121 h1:kMzxCUzXcdk318N/G2E7U0GXrq6yqczjFRs2RZvIUeE= github.com/rollkit/cometbft v0.0.0-20230524013001-2968c8b8b121/go.mod h1:Xc6G7GTlHAjeH3l5H8WibF/YrcCR6iDgAGrXVWkMwiU= github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= @@ -973,6 +986,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= @@ -1036,6 +1050,9 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= @@ -1056,6 +1073,9 @@ github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1 github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1250,7 +1270,6 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1282,8 +1301,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1450,8 +1469,8 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= +golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/node/full_node_integration_test.go b/node/full_node_integration_test.go index 14aef83ee73..d89b3c22c4d 100644 --- a/node/full_node_integration_test.go +++ b/node/full_node_integration_test.go @@ -158,7 +158,7 @@ func TestLazyAggregator(t *testing.T) { // the blocktime too short. in future, we can add a configuration // in go-header syncer initialization to not rely on blocktime, but the // config variable - BlockTime: 500 * time.Millisecond, + BlockTime: 1 * time.Second, NamespaceID: types.NamespaceID{1, 2, 3, 4, 5, 6, 7, 8}, }