From 4e1845ed6cde8d3137cc0175c0e11778ee319f03 Mon Sep 17 00:00:00 2001 From: Alexgao001 Date: Mon, 24 Jun 2024 17:13:48 +0800 Subject: [PATCH] adapt BSC RPC and add blob extra field grant allowance --- db/blob.go | 1 + external/client.go | 69 ++-- models/b_s_c_blob_sidecar.go | 56 ++++ models/b_s_c_blob_tx_sidecar.go | 116 +++++++ models/rpc_error.go | 55 +++ models/rpc_request.go | 63 ++++ models/rpc_response.go | 170 ++++++++++ models/sidecar.go | 6 + restapi/configure_blob_hub.go | 4 +- restapi/doc.go | 2 +- restapi/embedded_spec.go | 314 +++++++++++++++++- restapi/handlers/blob.go | 79 +++++ .../get_b_s_c_blob_sidecars_by_block_num.go | 56 ++++ ...c_blob_sidecars_by_block_num_parameters.go | 84 +++++ ..._c_blob_sidecars_by_block_num_responses.go | 104 ++++++ ...c_blob_sidecars_by_block_num_urlbuilder.go | 84 +++++ .../blob/get_blob_sidecars_by_block_num.go | 2 +- ...t_blob_sidecars_by_block_num_urlbuilder.go | 5 +- restapi/operations/blob_hub_api.go | 14 +- scripts/greenfield-cmd | 1 + service/blob.go | 2 + swagger.yaml | 114 ++++++- syncer/syncer.go | 13 +- syncer/verifier.go | 5 +- types/types.go | 10 + util/util.go | 14 + 26 files changed, 1400 insertions(+), 43 deletions(-) create mode 100644 models/b_s_c_blob_sidecar.go create mode 100644 models/b_s_c_blob_tx_sidecar.go create mode 100644 models/rpc_error.go create mode 100644 models/rpc_request.go create mode 100644 models/rpc_response.go create mode 100644 restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num.go create mode 100644 restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_parameters.go create mode 100644 restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_responses.go create mode 100644 restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_urlbuilder.go create mode 160000 scripts/greenfield-cmd create mode 100644 types/types.go diff --git a/db/blob.go b/db/blob.go index 4a81586..a588e50 100644 --- a/db/blob.go +++ b/db/blob.go @@ -8,6 +8,7 @@ type Blob struct { VersionedHash string `gorm:"NOT NULL"` Slot uint64 `gorm:"NOT NULL;index:idx_blob_slot_index"` Idx int `gorm:"NOT NULL;index:idx_blob_slot_idx"` + TxIndex int KzgCommitment string `gorm:"NOT NULL"` KzgProof string `gorm:"NOT NULL"` CommitmentInclusionProof string `gorm:"NOT NULL"` diff --git a/external/client.go b/external/client.go index e71135c..472dbd7 100644 --- a/external/client.go +++ b/external/client.go @@ -5,6 +5,8 @@ import ( "math/big" "strconv" + types2 "github.com/bnb-chain/blob-hub/types" + "github.com/bnb-chain/blob-hub/util" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethclient" @@ -18,7 +20,7 @@ import ( const BSCBlockConfirmNum = 3 type IClient interface { - GetBlob(ctx context.Context, blockID uint64) ([]*structs.Sidecar, error) + GetBlob(ctx context.Context, blockID uint64) ([]*types2.GeneralSideCar, error) GetBlockHeader(ctx context.Context, height uint64) (*types.Header, error) GetFinalizedBlockNum(ctx context.Context) (uint64, error) BlockByNumber(ctx context.Context, int2 *big.Int) (*types.Block, error) @@ -61,24 +63,35 @@ func NewClient(cfg *config.SyncerConfig) IClient { return cli } -func (c *Client) GetBlob(ctx context.Context, blockID uint64) ([]*structs.Sidecar, error) { +func (c *Client) GetBlob(ctx context.Context, blockID uint64) ([]*types2.GeneralSideCar, error) { + sidecars := make([]*types2.GeneralSideCar, 0) if c.cfg.Chain == config.BSC { - var r []*BlobTxSidecar + var txSidecars []*BSCBlobTxSidecar number := rpc.BlockNumberOrHashWithNumber(rpc.BlockNumber(blockID)) - err := c.rpcClient.CallContext(ctx, &r, "eth_getBlobSidecars", number.String()) - if err == nil && r == nil { + err := c.rpcClient.CallContext(ctx, &txSidecars, "eth_getBlobSidecars", number.String()) + if err != nil { + return nil, err + } + if txSidecars == nil { return nil, ethereum.NotFound } - sidecars := make([]*structs.Sidecar, 0) idx := 0 - for _, b := range r { - for j := range b.BlobSidecar.Blobs { + for _, txSidecar := range txSidecars { + txIndex, err := util.HexToUint64(txSidecar.TxIndex) + if err != nil { + return nil, err + } + for j := range txSidecar.BlobSidecar.Blobs { sidecars = append(sidecars, - &structs.Sidecar{ - Index: strconv.Itoa(idx), - Blob: b.BlobSidecar.Blobs[j], - KzgCommitment: b.BlobSidecar.Commitments[j], - KzgProof: b.BlobSidecar.Proofs[j], + &types2.GeneralSideCar{ + Sidecar: structs.Sidecar{ + Index: strconv.Itoa(idx), + Blob: txSidecar.BlobSidecar.Blobs[j], + KzgCommitment: txSidecar.BlobSidecar.Commitments[j], + KzgProof: txSidecar.BlobSidecar.Proofs[j], + }, + TxIndex: int64(txIndex), + TxHash: txSidecar.TxHash, }, ) idx++ @@ -86,7 +99,18 @@ func (c *Client) GetBlob(ctx context.Context, blockID uint64) ([]*structs.Sideca } return sidecars, err } - return c.beaconClient.GetBlob(ctx, blockID) + ethSidecars, err := c.beaconClient.GetBlob(ctx, blockID) + if err != nil { + return nil, err + } + for _, sidecar := range ethSidecars { + sidecars = append(sidecars, + &types2.GeneralSideCar{ + Sidecar: *sidecar, + }, + ) + } + return sidecars, nil } func (c *Client) GetBlockHeader(ctx context.Context, height uint64) (*types.Header, error) { @@ -124,17 +148,18 @@ func (c *Client) GetBeaconBlock(ctx context.Context, slotNumber uint64) (*struct return c.beaconClient.GetBeaconBlock(ctx, slotNumber) } -// Define the Go structs to match the JSON structure -type BlobSidecar struct { +// BSCBlobSidecar is a sidecar struct for BSC +type BSCBlobSidecar struct { Blobs []string `json:"blobs"` Commitments []string `json:"commitments"` Proofs []string `json:"proofs"` } -type BlobTxSidecar struct { - BlobSidecar BlobSidecar `json:"blobSidecar"` - BlockNumber string `json:"blockNumber"` - BlockHash string `json:"blockHash"` - TxIndex string `json:"txIndex"` - TxHash string `json:"txHash"` +// BSCBlobTxSidecar is a sidecar struct for BSC blob tx +type BSCBlobTxSidecar struct { + BlobSidecar BSCBlobSidecar `json:"blobSidecar"` + BlockNumber string `json:"blockNumber"` + BlockHash string `json:"blockHash"` + TxIndex string `json:"txIndex"` + TxHash string `json:"txHash"` } diff --git a/models/b_s_c_blob_sidecar.go b/models/b_s_c_blob_sidecar.go new file mode 100644 index 0000000..1bec075 --- /dev/null +++ b/models/b_s_c_blob_sidecar.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// BSCBlobSidecar b s c blob sidecar +// +// swagger:model BSCBlobSidecar +type BSCBlobSidecar struct { + + // blobs + Blobs []string `json:"blobs"` + + // commitments + Commitments []string `json:"commitments"` + + // proofs + Proofs []string `json:"proofs"` +} + +// Validate validates this b s c blob sidecar +func (m *BSCBlobSidecar) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this b s c blob sidecar based on context it is used +func (m *BSCBlobSidecar) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *BSCBlobSidecar) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *BSCBlobSidecar) UnmarshalBinary(b []byte) error { + var res BSCBlobSidecar + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/b_s_c_blob_tx_sidecar.go b/models/b_s_c_blob_tx_sidecar.go new file mode 100644 index 0000000..677ae67 --- /dev/null +++ b/models/b_s_c_blob_tx_sidecar.go @@ -0,0 +1,116 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// BSCBlobTxSidecar b s c blob tx sidecar +// +// swagger:model BSCBlobTxSidecar +type BSCBlobTxSidecar struct { + + // blob sidecar + BlobSidecar *BSCBlobSidecar `json:"blobSidecar,omitempty"` + + // block hash + BlockHash string `json:"blockHash,omitempty"` + + // block number + BlockNumber string `json:"blockNumber,omitempty"` + + // tx hash + TxHash string `json:"txHash,omitempty"` + + // tx index + TxIndex string `json:"txIndex,omitempty"` +} + +// Validate validates this b s c blob tx sidecar +func (m *BSCBlobTxSidecar) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateBlobSidecar(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *BSCBlobTxSidecar) validateBlobSidecar(formats strfmt.Registry) error { + if swag.IsZero(m.BlobSidecar) { // not required + return nil + } + + if m.BlobSidecar != nil { + if err := m.BlobSidecar.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("blobSidecar") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("blobSidecar") + } + return err + } + } + + return nil +} + +// ContextValidate validate this b s c blob tx sidecar based on the context it is used +func (m *BSCBlobTxSidecar) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateBlobSidecar(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *BSCBlobTxSidecar) contextValidateBlobSidecar(ctx context.Context, formats strfmt.Registry) error { + + if m.BlobSidecar != nil { + if err := m.BlobSidecar.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("blobSidecar") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("blobSidecar") + } + return err + } + } + + return nil +} + +// MarshalBinary interface implementation +func (m *BSCBlobTxSidecar) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *BSCBlobTxSidecar) UnmarshalBinary(b []byte) error { + var res BSCBlobTxSidecar + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/rpc_error.go b/models/rpc_error.go new file mode 100644 index 0000000..750de5e --- /dev/null +++ b/models/rpc_error.go @@ -0,0 +1,55 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// RPCError RPC error +// +// swagger:model RPCError +type RPCError struct { + + // RPC error code + // Example: -32602 + Code int64 `json:"code"` + + // Error message + // Example: Invalid params + Message string `json:"message"` +} + +// Validate validates this RPC error +func (m *RPCError) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this RPC error based on context it is used +func (m *RPCError) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *RPCError) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *RPCError) UnmarshalBinary(b []byte) error { + var res RPCError + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/rpc_request.go b/models/rpc_request.go new file mode 100644 index 0000000..a42ddf2 --- /dev/null +++ b/models/rpc_request.go @@ -0,0 +1,63 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// RPCRequest RPC request +// +// swagger:model RPCRequest +type RPCRequest struct { + + // id + // Example: 1 + ID int64 `json:"id,omitempty"` + + // jsonrpc + // Example: 2.0 + Jsonrpc string `json:"jsonrpc,omitempty"` + + // method + // Example: eth_getBlobSidecars + Method string `json:"method,omitempty"` + + // params + // Example: ["0x1",true] + Params []string `json:"params"` +} + +// Validate validates this RPC request +func (m *RPCRequest) Validate(formats strfmt.Registry) error { + return nil +} + +// ContextValidate validates this RPC request based on context it is used +func (m *RPCRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + return nil +} + +// MarshalBinary interface implementation +func (m *RPCRequest) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *RPCRequest) UnmarshalBinary(b []byte) error { + var res RPCRequest + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/rpc_response.go b/models/rpc_response.go new file mode 100644 index 0000000..9590728 --- /dev/null +++ b/models/rpc_response.go @@ -0,0 +1,170 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package models + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "context" + "strconv" + + "github.com/go-openapi/errors" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/swag" +) + +// RPCResponse RPC response +// +// swagger:model RPCResponse +type RPCResponse struct { + + // error + Error *RPCError `json:"error,omitempty"` + + // id + // Example: 1 + ID int64 `json:"id,omitempty"` + + // jsonrpc + // Example: 2.0 + Jsonrpc string `json:"jsonrpc,omitempty"` + + // result + Result []*BSCBlobTxSidecar `json:"result"` +} + +// Validate validates this RPC response +func (m *RPCResponse) Validate(formats strfmt.Registry) error { + var res []error + + if err := m.validateError(formats); err != nil { + res = append(res, err) + } + + if err := m.validateResult(formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *RPCResponse) validateError(formats strfmt.Registry) error { + if swag.IsZero(m.Error) { // not required + return nil + } + + if m.Error != nil { + if err := m.Error.Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("error") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("error") + } + return err + } + } + + return nil +} + +func (m *RPCResponse) validateResult(formats strfmt.Registry) error { + if swag.IsZero(m.Result) { // not required + return nil + } + + for i := 0; i < len(m.Result); i++ { + if swag.IsZero(m.Result[i]) { // not required + continue + } + + if m.Result[i] != nil { + if err := m.Result[i].Validate(formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("result" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("result" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// ContextValidate validate this RPC response based on the context it is used +func (m *RPCResponse) ContextValidate(ctx context.Context, formats strfmt.Registry) error { + var res []error + + if err := m.contextValidateError(ctx, formats); err != nil { + res = append(res, err) + } + + if err := m.contextValidateResult(ctx, formats); err != nil { + res = append(res, err) + } + + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} + +func (m *RPCResponse) contextValidateError(ctx context.Context, formats strfmt.Registry) error { + + if m.Error != nil { + if err := m.Error.ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("error") + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("error") + } + return err + } + } + + return nil +} + +func (m *RPCResponse) contextValidateResult(ctx context.Context, formats strfmt.Registry) error { + + for i := 0; i < len(m.Result); i++ { + + if m.Result[i] != nil { + if err := m.Result[i].ContextValidate(ctx, formats); err != nil { + if ve, ok := err.(*errors.Validation); ok { + return ve.ValidateName("result" + "." + strconv.Itoa(i)) + } else if ce, ok := err.(*errors.CompositeError); ok { + return ce.ValidateName("result" + "." + strconv.Itoa(i)) + } + return err + } + } + + } + + return nil +} + +// MarshalBinary interface implementation +func (m *RPCResponse) MarshalBinary() ([]byte, error) { + if m == nil { + return nil, nil + } + return swag.WriteJSON(m) +} + +// UnmarshalBinary interface implementation +func (m *RPCResponse) UnmarshalBinary(b []byte) error { + var res RPCResponse + if err := swag.ReadJSON(b, &res); err != nil { + return err + } + *m = res + return nil +} diff --git a/models/sidecar.go b/models/sidecar.go index ed4698c..c298dc6 100644 --- a/models/sidecar.go +++ b/models/sidecar.go @@ -36,6 +36,12 @@ type Sidecar struct { // signed block header SignedBlockHeader *SidecarSignedBlockHeader `json:"signed_block_header,omitempty"` + + // tx hash + TxHash string `json:"tx_hash,omitempty"` + + // tx index + TxIndex int64 `json:"tx_index,omitempty"` } // Validate validates this sidecar diff --git a/restapi/configure_blob_hub.go b/restapi/configure_blob_hub.go index 20502fa..aed6b02 100644 --- a/restapi/configure_blob_hub.go +++ b/restapi/configure_blob_hub.go @@ -11,11 +11,12 @@ import ( "net/http" "os" + "google.golang.org/grpc" + "github.com/go-openapi/errors" "github.com/go-openapi/runtime" "github.com/go-openapi/swag" grpcruntime "github.com/grpc-ecosystem/grpc-gateway/runtime" - "google.golang.org/grpc" "github.com/bnb-chain/blob-hub/cache" "github.com/bnb-chain/blob-hub/client" @@ -62,6 +63,7 @@ func configureAPI(api *operations.BlobHubAPI) http.Handler { api.JSONProducer = runtime.JSONProducer() api.BlobGetBlobSidecarsByBlockNumHandler = blob.GetBlobSidecarsByBlockNumHandlerFunc(handlers.HandleGetBlobSidecars()) + api.BlobGetBSCBlobSidecarsByBlockNumHandler = blob.GetBSCBlobSidecarsByBlockNumHandlerFunc(handlers.HandleGetBSCBlobSidecars()) api.PreServerShutdown = func() {} api.ServerShutdown = func() {} diff --git a/restapi/doc.go b/restapi/doc.go index 727566a..9e113c5 100644 --- a/restapi/doc.go +++ b/restapi/doc.go @@ -6,7 +6,7 @@ // Schemes: // http // Host: blob-hub -// BasePath: /eth/v1 +// BasePath: / // Version: 1.0.0 // // Consumes: diff --git a/restapi/embedded_spec.go b/restapi/embedded_spec.go index c8d0570..93db63d 100644 --- a/restapi/embedded_spec.go +++ b/restapi/embedded_spec.go @@ -28,9 +28,44 @@ func init() { "version": "1.0.0" }, "host": "blob-hub", - "basePath": "/eth/v1", "paths": { - "/beacon/blob_sidecars/{block_id}": { + "/": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "blob" + ], + "summary": "Get BSC blob sidecars by block num", + "operationId": "getBSCBlobSidecarsByBlockNum", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/RPCRequest" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/RPCResponse" + } + }, + "500": { + "description": "internal server error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/eth/v1/beacon/blob_sidecars/{block_id}": { "get": { "produces": [ "application/json" @@ -89,6 +124,49 @@ func init() { } }, "definitions": { + "BSCBlobSidecar": { + "type": "object", + "properties": { + "blobs": { + "type": "array", + "items": { + "type": "string" + } + }, + "commitments": { + "type": "array", + "items": { + "type": "string" + } + }, + "proofs": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "BSCBlobTxSidecar": { + "type": "object", + "properties": { + "blobSidecar": { + "$ref": "#/definitions/BSCBlobSidecar" + }, + "blockHash": { + "type": "string" + }, + "blockNumber": { + "type": "string" + }, + "txHash": { + "type": "string" + }, + "txIndex": { + "type": "string" + } + } + }, "Error": { "type": "object", "properties": { @@ -129,6 +207,73 @@ func init() { } } }, + "RPCError": { + "type": "object", + "properties": { + "code": { + "description": "RPC error code", + "type": "integer", + "format": "int64", + "x-omitempty": false, + "example": -32602 + }, + "message": { + "description": "Error message", + "type": "string", + "x-omitempty": false, + "example": "Invalid params" + } + } + }, + "RPCRequest": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "jsonrpc": { + "type": "string", + "example": "2.0" + }, + "method": { + "type": "string", + "example": "eth_getBlobSidecars" + }, + "params": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "0x1", + true + ] + } + } + }, + "RPCResponse": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/RPCError" + }, + "id": { + "type": "integer", + "example": 1 + }, + "jsonrpc": { + "type": "string", + "example": "2.0" + }, + "result": { + "type": "array", + "items": { + "$ref": "#/definitions/BSCBlobTxSidecar" + } + } + } + }, "Sidecar": { "type": "object", "properties": { @@ -179,6 +324,14 @@ func init() { "type": "string" } } + }, + "tx_hash": { + "type": "string" + }, + "tx_index": { + "type": "integer", + "format": "int64", + "x-omitempty": true } } } @@ -195,9 +348,44 @@ func init() { "version": "1.0.0" }, "host": "blob-hub", - "basePath": "/eth/v1", "paths": { - "/beacon/blob_sidecars/{block_id}": { + "/": { + "post": { + "produces": [ + "application/json" + ], + "tags": [ + "blob" + ], + "summary": "Get BSC blob sidecars by block num", + "operationId": "getBSCBlobSidecarsByBlockNum", + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/RPCRequest" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/RPCResponse" + } + }, + "500": { + "description": "internal server error", + "schema": { + "$ref": "#/definitions/Error" + } + } + } + } + }, + "/eth/v1/beacon/blob_sidecars/{block_id}": { "get": { "produces": [ "application/json" @@ -256,6 +444,49 @@ func init() { } }, "definitions": { + "BSCBlobSidecar": { + "type": "object", + "properties": { + "blobs": { + "type": "array", + "items": { + "type": "string" + } + }, + "commitments": { + "type": "array", + "items": { + "type": "string" + } + }, + "proofs": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "BSCBlobTxSidecar": { + "type": "object", + "properties": { + "blobSidecar": { + "$ref": "#/definitions/BSCBlobSidecar" + }, + "blockHash": { + "type": "string" + }, + "blockNumber": { + "type": "string" + }, + "txHash": { + "type": "string" + }, + "txIndex": { + "type": "string" + } + } + }, "Error": { "type": "object", "properties": { @@ -296,6 +527,73 @@ func init() { } } }, + "RPCError": { + "type": "object", + "properties": { + "code": { + "description": "RPC error code", + "type": "integer", + "format": "int64", + "x-omitempty": false, + "example": -32602 + }, + "message": { + "description": "Error message", + "type": "string", + "x-omitempty": false, + "example": "Invalid params" + } + } + }, + "RPCRequest": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "example": 1 + }, + "jsonrpc": { + "type": "string", + "example": "2.0" + }, + "method": { + "type": "string", + "example": "eth_getBlobSidecars" + }, + "params": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "0x1", + true + ] + } + } + }, + "RPCResponse": { + "type": "object", + "properties": { + "error": { + "$ref": "#/definitions/RPCError" + }, + "id": { + "type": "integer", + "example": 1 + }, + "jsonrpc": { + "type": "string", + "example": "2.0" + }, + "result": { + "type": "array", + "items": { + "$ref": "#/definitions/BSCBlobTxSidecar" + } + } + } + }, "Sidecar": { "type": "object", "properties": { @@ -346,6 +644,14 @@ func init() { "type": "string" } } + }, + "tx_hash": { + "type": "string" + }, + "tx_index": { + "type": "integer", + "format": "int64", + "x-omitempty": true } } }, diff --git a/restapi/handlers/blob.go b/restapi/handlers/blob.go index 7bccf13..d6340d0 100644 --- a/restapi/handlers/blob.go +++ b/restapi/handlers/blob.go @@ -64,3 +64,82 @@ func HandleGetBlobSidecars() func(params blob.GetBlobSidecarsByBlockNumParams) m } } } + +func HandleGetBSCBlobSidecars() func(params blob.GetBSCBlobSidecarsByBlockNumParams) middleware.Responder { + return func(params blob.GetBSCBlobSidecarsByBlockNumParams) middleware.Responder { + + rpcRequest := params.Body + if rpcRequest.Params == nil { + return blob.NewGetBSCBlobSidecarsByBlockNumOK().WithPayload( + &models.RPCResponse{ + ID: rpcRequest.ID, + Jsonrpc: rpcRequest.Jsonrpc, + Error: &models.RPCError{ + Code: -32600, + Message: "Invalid request", + }, + }, + ) + } + + switch rpcRequest.Method { + case "eth_getBlobSidecars": + blockNum, err := util.HexToUint64(rpcRequest.Params[0]) + if err != nil { + return blob.NewGetBSCBlobSidecarsByBlockNumOK().WithPayload( + &models.RPCResponse{ + ID: rpcRequest.ID, + Jsonrpc: rpcRequest.Jsonrpc, + Error: &models.RPCError{ + Code: -32602, + Message: "invalid argument", + }, + }, + ) + } + sidecars, err := service.BlobSvc.GetBlobSidecarsByBlockNumOrSlot(blockNum, nil) + if err != nil { + return blob.NewGetBlobSidecarsByBlockNumInternalServerError().WithPayload(service.InternalErrorWithError(err)) + } + // group sidecars by tx hash + bscTxSidecars := make(map[string]*models.BSCBlobTxSidecar) + for _, sidecar := range sidecars { + txSidecar, ok := bscTxSidecars[sidecar.TxHash] + if !ok { + txSidecar = &models.BSCBlobTxSidecar{ + BlobSidecar: &models.BSCBlobSidecar{}, + TxHash: sidecar.TxHash, + } + bscTxSidecars[sidecar.TxHash] = txSidecar + } + txSidecar.BlobSidecar.Blobs = append(txSidecar.BlobSidecar.Blobs, sidecar.Blob) + txSidecar.BlobSidecar.Commitments = append(txSidecar.BlobSidecar.Commitments, sidecar.KzgCommitment) + txSidecar.BlobSidecar.Proofs = append(txSidecar.BlobSidecar.Proofs, sidecar.KzgProof) + txSidecar.TxIndex = util.Int64ToHex(sidecar.TxIndex) + txSidecar.BlockNumber = rpcRequest.Params[0] + } + // convert txSidecars to array + txSidecarsArr := make([]*models.BSCBlobTxSidecar, 0) + for _, txSidecar := range bscTxSidecars { + txSidecarsArr = append(txSidecarsArr, txSidecar) + } + response := &models.RPCResponse{ + ID: rpcRequest.ID, + Jsonrpc: rpcRequest.Jsonrpc, + Result: txSidecarsArr, + } + return blob.NewGetBSCBlobSidecarsByBlockNumOK().WithPayload(response) + default: + return blob.NewGetBSCBlobSidecarsByBlockNumOK().WithPayload( + &models.RPCResponse{ + ID: rpcRequest.ID, + Jsonrpc: rpcRequest.Jsonrpc, + Error: &models.RPCError{ + Code: -32601, + Message: "method not supported", + }, + }, + ) + } + } +} diff --git a/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num.go b/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num.go new file mode 100644 index 0000000..e99a10f --- /dev/null +++ b/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num.go @@ -0,0 +1,56 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package blob + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime/middleware" +) + +// GetBSCBlobSidecarsByBlockNumHandlerFunc turns a function with the right signature into a get b s c blob sidecars by block num handler +type GetBSCBlobSidecarsByBlockNumHandlerFunc func(GetBSCBlobSidecarsByBlockNumParams) middleware.Responder + +// Handle executing the request and returning a response +func (fn GetBSCBlobSidecarsByBlockNumHandlerFunc) Handle(params GetBSCBlobSidecarsByBlockNumParams) middleware.Responder { + return fn(params) +} + +// GetBSCBlobSidecarsByBlockNumHandler interface for that can handle valid get b s c blob sidecars by block num params +type GetBSCBlobSidecarsByBlockNumHandler interface { + Handle(GetBSCBlobSidecarsByBlockNumParams) middleware.Responder +} + +// NewGetBSCBlobSidecarsByBlockNum creates a new http.Handler for the get b s c blob sidecars by block num operation +func NewGetBSCBlobSidecarsByBlockNum(ctx *middleware.Context, handler GetBSCBlobSidecarsByBlockNumHandler) *GetBSCBlobSidecarsByBlockNum { + return &GetBSCBlobSidecarsByBlockNum{Context: ctx, Handler: handler} +} + +/* + GetBSCBlobSidecarsByBlockNum swagger:route POST / blob getBSCBlobSidecarsByBlockNum + +Get BSC blob sidecars by block num +*/ +type GetBSCBlobSidecarsByBlockNum struct { + Context *middleware.Context + Handler GetBSCBlobSidecarsByBlockNumHandler +} + +func (o *GetBSCBlobSidecarsByBlockNum) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewGetBSCBlobSidecarsByBlockNumParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} diff --git a/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_parameters.go b/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_parameters.go new file mode 100644 index 0000000..9592a88 --- /dev/null +++ b/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_parameters.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package blob + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "io" + "net/http" + + "github.com/go-openapi/errors" + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware" + "github.com/go-openapi/validate" + + "github.com/bnb-chain/blob-hub/models" +) + +// NewGetBSCBlobSidecarsByBlockNumParams creates a new GetBSCBlobSidecarsByBlockNumParams object +// +// There are no default values defined in the spec. +func NewGetBSCBlobSidecarsByBlockNumParams() GetBSCBlobSidecarsByBlockNumParams { + + return GetBSCBlobSidecarsByBlockNumParams{} +} + +// GetBSCBlobSidecarsByBlockNumParams contains all the bound params for the get b s c blob sidecars by block num operation +// typically these are obtained from a http.Request +// +// swagger:parameters getBSCBlobSidecarsByBlockNum +type GetBSCBlobSidecarsByBlockNumParams struct { + + // HTTP Request Object + HTTPRequest *http.Request `json:"-"` + + /* + Required: true + In: body + */ + Body *models.RPCRequest +} + +// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface +// for simple values it will use straight method calls. +// +// To ensure default values, the struct must have been initialized with NewGetBSCBlobSidecarsByBlockNumParams() beforehand. +func (o *GetBSCBlobSidecarsByBlockNumParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { + var res []error + + o.HTTPRequest = r + + if runtime.HasBody(r) { + defer r.Body.Close() + var body models.RPCRequest + if err := route.Consumer.Consume(r.Body, &body); err != nil { + if err == io.EOF { + res = append(res, errors.Required("body", "body", "")) + } else { + res = append(res, errors.NewParseError("body", "body", "", err)) + } + } else { + // validate body object + if err := body.Validate(route.Formats); err != nil { + res = append(res, err) + } + + ctx := validate.WithOperationRequest(r.Context()) + if err := body.ContextValidate(ctx, route.Formats); err != nil { + res = append(res, err) + } + + if len(res) == 0 { + o.Body = &body + } + } + } else { + res = append(res, errors.Required("body", "body", "")) + } + if len(res) > 0 { + return errors.CompositeValidationError(res...) + } + return nil +} diff --git a/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_responses.go b/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_responses.go new file mode 100644 index 0000000..e617de3 --- /dev/null +++ b/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_responses.go @@ -0,0 +1,104 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package blob + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the swagger generate command + +import ( + "net/http" + + "github.com/go-openapi/runtime" + + "github.com/bnb-chain/blob-hub/models" +) + +// GetBSCBlobSidecarsByBlockNumOKCode is the HTTP code returned for type GetBSCBlobSidecarsByBlockNumOK +const GetBSCBlobSidecarsByBlockNumOKCode int = 200 + +/* +GetBSCBlobSidecarsByBlockNumOK successful operation + +swagger:response getBSCBlobSidecarsByBlockNumOK +*/ +type GetBSCBlobSidecarsByBlockNumOK struct { + + /* + In: Body + */ + Payload *models.RPCResponse `json:"body,omitempty"` +} + +// NewGetBSCBlobSidecarsByBlockNumOK creates GetBSCBlobSidecarsByBlockNumOK with default headers values +func NewGetBSCBlobSidecarsByBlockNumOK() *GetBSCBlobSidecarsByBlockNumOK { + + return &GetBSCBlobSidecarsByBlockNumOK{} +} + +// WithPayload adds the payload to the get b s c blob sidecars by block num o k response +func (o *GetBSCBlobSidecarsByBlockNumOK) WithPayload(payload *models.RPCResponse) *GetBSCBlobSidecarsByBlockNumOK { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get b s c blob sidecars by block num o k response +func (o *GetBSCBlobSidecarsByBlockNumOK) SetPayload(payload *models.RPCResponse) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetBSCBlobSidecarsByBlockNumOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(200) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} + +// GetBSCBlobSidecarsByBlockNumInternalServerErrorCode is the HTTP code returned for type GetBSCBlobSidecarsByBlockNumInternalServerError +const GetBSCBlobSidecarsByBlockNumInternalServerErrorCode int = 500 + +/* +GetBSCBlobSidecarsByBlockNumInternalServerError internal server error + +swagger:response getBSCBlobSidecarsByBlockNumInternalServerError +*/ +type GetBSCBlobSidecarsByBlockNumInternalServerError struct { + + /* + In: Body + */ + Payload *models.Error `json:"body,omitempty"` +} + +// NewGetBSCBlobSidecarsByBlockNumInternalServerError creates GetBSCBlobSidecarsByBlockNumInternalServerError with default headers values +func NewGetBSCBlobSidecarsByBlockNumInternalServerError() *GetBSCBlobSidecarsByBlockNumInternalServerError { + + return &GetBSCBlobSidecarsByBlockNumInternalServerError{} +} + +// WithPayload adds the payload to the get b s c blob sidecars by block num internal server error response +func (o *GetBSCBlobSidecarsByBlockNumInternalServerError) WithPayload(payload *models.Error) *GetBSCBlobSidecarsByBlockNumInternalServerError { + o.Payload = payload + return o +} + +// SetPayload sets the payload to the get b s c blob sidecars by block num internal server error response +func (o *GetBSCBlobSidecarsByBlockNumInternalServerError) SetPayload(payload *models.Error) { + o.Payload = payload +} + +// WriteResponse to the client +func (o *GetBSCBlobSidecarsByBlockNumInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) { + + rw.WriteHeader(500) + if o.Payload != nil { + payload := o.Payload + if err := producer.Produce(rw, payload); err != nil { + panic(err) // let the recovery middleware deal with this + } + } +} diff --git a/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_urlbuilder.go b/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_urlbuilder.go new file mode 100644 index 0000000..4282805 --- /dev/null +++ b/restapi/operations/blob/get_b_s_c_blob_sidecars_by_block_num_urlbuilder.go @@ -0,0 +1,84 @@ +// Code generated by go-swagger; DO NOT EDIT. + +package blob + +// This file was generated by the swagger tool. +// Editing this file might prove futile when you re-run the generate command + +import ( + "errors" + "net/url" + golangswaggerpaths "path" +) + +// GetBSCBlobSidecarsByBlockNumURL generates an URL for the get b s c blob sidecars by block num operation +type GetBSCBlobSidecarsByBlockNumURL struct { + _basePath string +} + +// WithBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetBSCBlobSidecarsByBlockNumURL) WithBasePath(bp string) *GetBSCBlobSidecarsByBlockNumURL { + o.SetBasePath(bp) + return o +} + +// SetBasePath sets the base path for this url builder, only required when it's different from the +// base path specified in the swagger spec. +// When the value of the base path is an empty string +func (o *GetBSCBlobSidecarsByBlockNumURL) SetBasePath(bp string) { + o._basePath = bp +} + +// Build a url path and query string +func (o *GetBSCBlobSidecarsByBlockNumURL) Build() (*url.URL, error) { + var _result url.URL + + var _path = "/" + + _basePath := o._basePath + _result.Path = golangswaggerpaths.Join(_basePath, _path) + + return &_result, nil +} + +// Must is a helper function to panic when the url builder returns an error +func (o *GetBSCBlobSidecarsByBlockNumURL) Must(u *url.URL, err error) *url.URL { + if err != nil { + panic(err) + } + if u == nil { + panic("url can't be nil") + } + return u +} + +// String returns the string representation of the path with query string +func (o *GetBSCBlobSidecarsByBlockNumURL) String() string { + return o.Must(o.Build()).String() +} + +// BuildFull builds a full url with scheme, host, path and query string +func (o *GetBSCBlobSidecarsByBlockNumURL) BuildFull(scheme, host string) (*url.URL, error) { + if scheme == "" { + return nil, errors.New("scheme is required for a full url on GetBSCBlobSidecarsByBlockNumURL") + } + if host == "" { + return nil, errors.New("host is required for a full url on GetBSCBlobSidecarsByBlockNumURL") + } + + base, err := o.Build() + if err != nil { + return nil, err + } + + base.Scheme = scheme + base.Host = host + return base, nil +} + +// StringFull returns the string representation of a complete url +func (o *GetBSCBlobSidecarsByBlockNumURL) StringFull(scheme, host string) string { + return o.Must(o.BuildFull(scheme, host)).String() +} diff --git a/restapi/operations/blob/get_blob_sidecars_by_block_num.go b/restapi/operations/blob/get_blob_sidecars_by_block_num.go index 40ca57d..23aa5b1 100644 --- a/restapi/operations/blob/get_blob_sidecars_by_block_num.go +++ b/restapi/operations/blob/get_blob_sidecars_by_block_num.go @@ -30,7 +30,7 @@ func NewGetBlobSidecarsByBlockNum(ctx *middleware.Context, handler GetBlobSideca } /* - GetBlobSidecarsByBlockNum swagger:route GET /beacon/blob_sidecars/{block_id} blob getBlobSidecarsByBlockNum + GetBlobSidecarsByBlockNum swagger:route GET /eth/v1/beacon/blob_sidecars/{block_id} blob getBlobSidecarsByBlockNum Get blob sidecars by block num */ diff --git a/restapi/operations/blob/get_blob_sidecars_by_block_num_urlbuilder.go b/restapi/operations/blob/get_blob_sidecars_by_block_num_urlbuilder.go index 037eabe..82982db 100644 --- a/restapi/operations/blob/get_blob_sidecars_by_block_num_urlbuilder.go +++ b/restapi/operations/blob/get_blob_sidecars_by_block_num_urlbuilder.go @@ -44,7 +44,7 @@ func (o *GetBlobSidecarsByBlockNumURL) SetBasePath(bp string) { func (o *GetBlobSidecarsByBlockNumURL) Build() (*url.URL, error) { var _result url.URL - var _path = "/beacon/blob_sidecars/{block_id}" + var _path = "/eth/v1/beacon/blob_sidecars/{block_id}" blockID := o.BlockID if blockID != "" { @@ -54,9 +54,6 @@ func (o *GetBlobSidecarsByBlockNumURL) Build() (*url.URL, error) { } _basePath := o._basePath - if _basePath == "" { - _basePath = "/eth/v1" - } _result.Path = golangswaggerpaths.Join(_basePath, _path) qs := make(url.Values) diff --git a/restapi/operations/blob_hub_api.go b/restapi/operations/blob_hub_api.go index 1cd2281..bbdfb2b 100644 --- a/restapi/operations/blob_hub_api.go +++ b/restapi/operations/blob_hub_api.go @@ -44,6 +44,9 @@ func NewBlobHubAPI(spec *loads.Document) *BlobHubAPI { JSONProducer: runtime.JSONProducer(), + BlobGetBSCBlobSidecarsByBlockNumHandler: blob.GetBSCBlobSidecarsByBlockNumHandlerFunc(func(params blob.GetBSCBlobSidecarsByBlockNumParams) middleware.Responder { + return middleware.NotImplemented("operation blob.GetBSCBlobSidecarsByBlockNum has not yet been implemented") + }), BlobGetBlobSidecarsByBlockNumHandler: blob.GetBlobSidecarsByBlockNumHandlerFunc(func(params blob.GetBlobSidecarsByBlockNumParams) middleware.Responder { return middleware.NotImplemented("operation blob.GetBlobSidecarsByBlockNum has not yet been implemented") }), @@ -83,6 +86,8 @@ type BlobHubAPI struct { // - application/json JSONProducer runtime.Producer + // BlobGetBSCBlobSidecarsByBlockNumHandler sets the operation handler for the get b s c blob sidecars by block num operation + BlobGetBSCBlobSidecarsByBlockNumHandler blob.GetBSCBlobSidecarsByBlockNumHandler // BlobGetBlobSidecarsByBlockNumHandler sets the operation handler for the get blob sidecars by block num operation BlobGetBlobSidecarsByBlockNumHandler blob.GetBlobSidecarsByBlockNumHandler @@ -162,6 +167,9 @@ func (o *BlobHubAPI) Validate() error { unregistered = append(unregistered, "JSONProducer") } + if o.BlobGetBSCBlobSidecarsByBlockNumHandler == nil { + unregistered = append(unregistered, "blob.GetBSCBlobSidecarsByBlockNumHandler") + } if o.BlobGetBlobSidecarsByBlockNumHandler == nil { unregistered = append(unregistered, "blob.GetBlobSidecarsByBlockNumHandler") } @@ -253,10 +261,14 @@ func (o *BlobHubAPI) initHandlerCache() { o.handlers = make(map[string]map[string]http.Handler) } + if o.handlers["POST"] == nil { + o.handlers["POST"] = make(map[string]http.Handler) + } + o.handlers["POST"][""] = blob.NewGetBSCBlobSidecarsByBlockNum(o.context, o.BlobGetBSCBlobSidecarsByBlockNumHandler) if o.handlers["GET"] == nil { o.handlers["GET"] = make(map[string]http.Handler) } - o.handlers["GET"]["/beacon/blob_sidecars/{block_id}"] = blob.NewGetBlobSidecarsByBlockNum(o.context, o.BlobGetBlobSidecarsByBlockNumHandler) + o.handlers["GET"]["/eth/v1/beacon/blob_sidecars/{block_id}"] = blob.NewGetBlobSidecarsByBlockNum(o.context, o.BlobGetBlobSidecarsByBlockNumHandler) } // Serve creates a http handler to serve the API over HTTP diff --git a/scripts/greenfield-cmd b/scripts/greenfield-cmd new file mode 160000 index 0000000..948e22d --- /dev/null +++ b/scripts/greenfield-cmd @@ -0,0 +1 @@ +Subproject commit 948e22d8d989017a3fd3b846e28746f6159a0e10 diff --git a/service/blob.go b/service/blob.go index 94453dc..9f2c5bc 100644 --- a/service/blob.go +++ b/service/blob.go @@ -97,6 +97,8 @@ func (b BlobService) GetBlobSidecarsByBlockNumOrSlot(blockNumOrSlot uint64, indi KzgCommitment: meta.KzgCommitment, KzgProof: meta.KzgProof, SignedBlockHeader: header, + TxIndex: int64(meta.TxIndex), + TxHash: meta.TxHash, }) } diff --git a/swagger.yaml b/swagger.yaml index 38d4c5c..5b3cea9 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -4,12 +4,12 @@ info: title: Blob Hub Service API description: API for handling blob query in the Blob Hub. host: 'blob-hub' -basePath: "/eth/v1" +#basePath: "/eth/v1" schemes: - http paths: - /beacon/blob_sidecars/{block_id}: + /eth/v1/beacon/blob_sidecars/{block_id}: get: tags: - "blob" @@ -48,6 +48,30 @@ paths: schema: $ref: "#/definitions/Error" + /: + post: + tags: + - "blob" + summary: "Get BSC blob sidecars by block num" + operationId: "getBSCBlobSidecarsByBlockNum" + produces: + - "application/json" + parameters: + - in: "body" + name: "body" + required: true + schema: + $ref: "#/definitions/RPCRequest" + responses: + "200": + description: "successful operation" + schema: + $ref: "#/definitions/RPCResponse" + "500": + description: 'internal server error' + schema: + $ref: "#/definitions/Error" + definitions: GetBlobSideCarsResponse: type: object @@ -100,6 +124,92 @@ definitions: type: string body_root: type: string + tx_index: + type: integer + format: int64 + x-omitempty: true + tx_hash: + type: string + + RPCRequest: + type: object + properties: + jsonrpc: + type: string + example: "2.0" + method: + type: string + example: "eth_getBlobSidecars" + params: + type: array + items: + type: string + example: ["0x1", true] + id: + type: integer + example: 1 + + RPCResponse: + type: object + properties: + jsonrpc: + type: string + example: "2.0" + result: + type: array + items: + $ref: "#/definitions/BSCBlobTxSidecar" + id: + type: integer + example: 1 + error: + $ref: "#/definitions/RPCError" + + + BSCBlobSidecar: + type: object + properties: + blobs: + type: array + items: + type: string + commitments: + type: array + items: + type: string + proofs: + type: array + items: + type: "string" + + BSCBlobTxSidecar: + type: object + properties: + blobSidecar: + $ref: "#/definitions/BSCBlobSidecar" + blockNumber: + type: string + blockHash: + type: string + txIndex: + type: string + txHash: + type: string + + RPCError: + type: object + properties: + code: + x-omitempty: false + type: integer + format: int64 + description: "RPC error code" + example: -32602 + message: + x-omitempty: false + type: string + description: "Error message" + example: "Invalid params" Error: type: object diff --git a/syncer/syncer.go b/syncer/syncer.go index 348cad4..da0f5c2 100644 --- a/syncer/syncer.go +++ b/syncer/syncer.go @@ -11,13 +11,14 @@ import ( "strings" "time" + "gorm.io/gorm" + "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/prysmaticlabs/prysm/v5/api/server/structs" v1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" - "gorm.io/gorm" "github.com/bnb-chain/blob-hub/config" "github.com/bnb-chain/blob-hub/db" @@ -173,7 +174,7 @@ func (s *BlobSyncer) sync() error { } } - var sideCars []*structs.Sidecar + var sideCars []*types.GeneralSideCar if !isForkedBlock { ctx, cancel = context.WithTimeout(context.Background(), RPCTimeout) @@ -212,7 +213,7 @@ func (s *BlobSyncer) sync() error { return nil } -func (s *BlobSyncer) process(bundleName string, blockID uint64, sidecars []*structs.Sidecar) error { +func (s *BlobSyncer) process(bundleName string, blockID uint64, sidecars []*types.GeneralSideCar) error { var err error // create a new bundle in local. if blockID == s.bundleDetail.startBlockID { @@ -302,7 +303,7 @@ func (s *BlobSyncer) finalizeCurBundle(bundleName string) error { return s.finalizeBundle(bundleName, s.getBundleDir(bundleName), s.getBundleFilePath(bundleName)) } -func (s *BlobSyncer) writeBlobToFile(slot uint64, bundleName string, blobs []*structs.Sidecar) error { +func (s *BlobSyncer) writeBlobToFile(slot uint64, bundleName string, blobs []*types.GeneralSideCar) error { for i, b := range blobs { blobName := types.GetBlobName(slot, i) file, err := os.Create(s.getBlobPath(bundleName, blobName)) @@ -378,7 +379,7 @@ func (s *BlobSyncer) LoadProgressAndResume(nextBlockID uint64) error { return nil } -func (s *BlobSyncer) toBlockAndBlobs(blockResp *structs.GetBlockV2Response, sidecars []*structs.Sidecar, blockNumOrSlot uint64, bundleName string) (*db.Block, []*db.Blob, error) { +func (s *BlobSyncer) toBlockAndBlobs(blockResp *structs.GetBlockV2Response, sidecars []*types.GeneralSideCar, blockNumOrSlot uint64, bundleName string) (*db.Block, []*db.Blob, error) { var blockReturn *db.Block blobsReturn := make([]*db.Blob, 0) @@ -426,6 +427,8 @@ func (s *BlobSyncer) toBlockAndBlobs(blockResp *structs.GetBlockV2Response, side Name: types.GetBlobName(blockNumOrSlot, index), Slot: blockNumOrSlot, Idx: index, + TxIndex: int(blob.TxIndex), + TxHash: blob.TxHash, KzgProof: blob.KzgProof, KzgCommitment: blob.KzgCommitment, } diff --git a/syncer/verifier.go b/syncer/verifier.go index a796515..9c599de 100644 --- a/syncer/verifier.go +++ b/syncer/verifier.go @@ -9,9 +9,10 @@ import ( "path/filepath" "time" - "github.com/prysmaticlabs/prysm/v5/api/server/structs" "gorm.io/gorm" + "github.com/prysmaticlabs/prysm/v5/api/server/structs" + "github.com/bnb-chain/blob-hub/db" "github.com/bnb-chain/blob-hub/external/cmn" "github.com/bnb-chain/blob-hub/external/eth" @@ -156,7 +157,7 @@ func (s *BlobSyncer) verify() error { return nil } -func (s *BlobSyncer) verifyBlob(blockID uint64, sidecars []*structs.Sidecar, blobMetas []*db.Blob, bundleName string) error { +func (s *BlobSyncer) verifyBlob(blockID uint64, sidecars []*types.GeneralSideCar, blobMetas []*db.Blob, bundleName string) error { for i := 0; i < len(sidecars); i++ { // get blob from bundle service blobFromBundle, err := s.bundleClient.GetObject(s.getBucketName(), bundleName, types.GetBlobName(blockID, i)) diff --git a/types/types.go b/types/types.go new file mode 100644 index 0000000..b72e607 --- /dev/null +++ b/types/types.go @@ -0,0 +1,10 @@ +package types + +import "github.com/prysmaticlabs/prysm/v5/api/server/structs" + +// GeneralSideCar is a general sidecar struct for both BSC and ETH +type GeneralSideCar struct { + structs.Sidecar + TxIndex int64 `json:"tx_index,omitempty"` + TxHash string `json:"tx_hash,omitempty"` +} diff --git a/util/util.go b/util/util.go index 2f6f859..5bbc886 100644 --- a/util/util.go +++ b/util/util.go @@ -64,3 +64,17 @@ func Uint64ToString(u uint64) string { func Int64ToString(u int64) string { return strconv.FormatInt(u, 10) } + +// HexToUint64 converts hex string to uint64 +func HexToUint64(hexStr string) (uint64, error) { + intValue, err := strconv.ParseUint(hexStr, 0, 64) + if err != nil { + return 0, err + } + return intValue, nil +} + +// Int64ToHex converts int64 to hex string +func Int64ToHex(int64 int64) string { + return "0x" + strconv.FormatInt(int64, 16) +}