diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index b36f292b2f..fd536ec8d7 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -65,9 +65,9 @@ func (s BlobTxSidecars) EncodeIndex(i int, w *bytes.Buffer) { // BlobTxSidecar contains the blobs of a blob transaction. type BlobTxSidecar struct { - Blobs []kzg4844.Blob // Blobs needed by the blob pool - Commitments []kzg4844.Commitment // Commitments needed by the blob pool - Proofs []kzg4844.Proof // Proofs needed by the blob pool + Blobs []kzg4844.Blob `json:"blobs"` // Blobs needed by the blob pool + Commitments []kzg4844.Commitment `json:"commitments"` // Commitments needed by the blob pool + Proofs []kzg4844.Proof `json:"proofs"` // Proofs needed by the blob pool } // BlobHashes computes the blob hashes of the given blobs. diff --git a/eth/api_backend.go b/eth/api_backend.go index bcf046679a..04041e189a 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -239,6 +239,9 @@ func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (type return b.eth.blockchain.GetReceiptsByHash(hash), nil } +func (b *EthAPIBackend) GetBlobSidecars(ctx context.Context, hash common.Hash) (types.BlobTxSidecars, error) { + return b.eth.blockchain.GetBlobsByHash(hash), nil +} func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { return rawdb.ReadLogs(b.eth.chainDb, hash, number), nil } diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 13ce66c7ae..acc2b003d0 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -130,6 +130,26 @@ func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumb return r, err } +// BlobSidecars return the Sidecars of a given block number or hash. +func (ec *Client) BlobSidecars(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.BlobTxSidecar, error) { + var r []*types.BlobTxSidecar + err := ec.c.CallContext(ctx, &r, "eth_getBlobSidecars", blockNrOrHash.String()) + if err == nil && r == nil { + return nil, ethereum.NotFound + } + return r, err +} + +// BlobSidecarByTxHash return a sidecar of a given blob transaction +func (ec *Client) BlobSidecarByTxHash(ctx context.Context, hash common.Hash) (*types.BlobTxSidecar, error) { + var r *types.BlobTxSidecar + err := ec.c.CallContext(ctx, &r, "eth_getBlockSidecarByTxHash", hash) + if err == nil && r == nil { + return nil, ethereum.NotFound + } + return r, err +} + type rpcBlock struct { Hash common.Hash `json:"hash"` Transactions []rpcTransaction `json:"transactions"` diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 047ca3f948..340768303b 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1010,6 +1010,60 @@ func (s *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc. return result, nil } +func (s *BlockChainAPI) GetBlobSidecars(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) { + block, err := s.b.BlockByNumberOrHash(ctx, blockNrOrHash) + if block == nil || err != nil { + // When the block doesn't exist, the RPC method should return JSON null + // as per specification. + return nil, nil + } + blobSidecars, err := s.b.GetBlobSidecars(ctx, block.Hash()) + if err != nil || len(blobSidecars) == 0 { + return nil, nil + } + result := make([]map[string]interface{}, len(blobSidecars)) + blobIndex := -1 + for txIndex, tx := range block.Transactions() { + if tx.Type() == types.BlobTxType { + blobIndex++ + if blobIndex >= len(blobSidecars) { + return nil, fmt.Errorf("blobSidecars length mismatch %d", len(blobSidecars)) + } + result[blobIndex] = marshalBlobSidecar(blobSidecars[blobIndex], block.Hash(), block.NumberU64(), tx, txIndex) + } + } + return result, nil +} + +func (s *BlockChainAPI) GetBlobSidecarByTxHash(ctx context.Context, hash common.Hash) (map[string]interface{}, error) { + txTarget, blockHash, blockNumber, Index := rawdb.ReadTransaction(s.b.ChainDb(), hash) + if txTarget == nil { + return nil, nil + } + block, err := s.b.BlockByHash(ctx, blockHash) + if block == nil || err != nil { + // When the block doesn't exist, the RPC method should return JSON null + // as per specification. + return nil, nil + } + blobSidecars, err := s.b.GetBlobSidecars(ctx, blockHash) + if err != nil || len(blobSidecars) == 0 { + return nil, nil + } + blobIndex := -1 + var result map[string]interface{} + for txIndex, tx := range block.Transactions() { + if tx.Type() == types.BlobTxType { + blobIndex++ + if txIndex == int(Index) { + result = marshalBlobSidecar(blobSidecars[blobIndex], blockHash, blockNumber, txTarget, int(Index)) + return result, nil + } + } + } + return result, nil +} + // OverrideAccount indicates the overriding fields of account during the execution // of a message call. // Note, state and stateDiff can't be specified at the same time. If state is @@ -2105,6 +2159,17 @@ func marshalReceipt(receipt *types.Receipt, blockHash common.Hash, blockNumber u return fields } +func marshalBlobSidecar(blobSidecar *types.BlobTxSidecar, blockHash common.Hash, blockNumber uint64, tx *types.Transaction, txIndex int) map[string]interface{} { + fields := map[string]interface{}{ + "blockHash": blockHash, + "blockNumber": hexutil.Uint64(blockNumber), + "txHash": tx.Hash(), + "txIndex": hexutil.Uint64(txIndex), + "blobSidecar": blobSidecar, + } + return fields +} + // sign is a helper function that signs a transaction with the private key of the given address. func (s *TransactionAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { // Look up the wallet containing the requested signer diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 26c6bad7f2..9030171ad7 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -563,6 +563,15 @@ func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.R receipts := rawdb.ReadReceipts(b.db, hash, header.Number.Uint64(), header.Time, b.chain.Config()) return receipts, nil } + +func (b testBackend) GetBlobSidecars(ctx context.Context, hash common.Hash) (types.BlobTxSidecars, error) { + header, err := b.HeaderByHash(ctx, hash) + if header == nil || err != nil { + return nil, err + } + blobSidecars := rawdb.ReadRawBlobs(b.db, hash, header.Number.Uint64()) + return blobSidecars, nil +} func (b testBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int { if b.pending != nil && hash == b.pending.Hash() { return nil diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 0f37e4f0f5..1f976f27c7 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -74,6 +74,7 @@ type Backend interface { SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription + GetBlobSidecars(ctx context.Context, hash common.Hash) (types.BlobTxSidecars, error) // Transaction pool API SendTx(ctx context.Context, signedTx *types.Transaction) error diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index f08fc2b059..8a82331d13 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -364,6 +364,9 @@ func (b *backendMock) PendingBlockAndReceipts() (*types.Block, types.Receipts) { func (b *backendMock) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { return nil, nil } +func (b *backendMock) GetBlobSidecars(ctx context.Context, hash common.Hash) (types.BlobTxSidecars, error) { + return nil, nil +} func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number uint64) ([][]*types.Log, error) { return nil, nil } diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index ae76af4090..909b3db791 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -614,6 +614,16 @@ web3._extend({ call: 'eth_getBlockReceipts', params: 1, }), + new web3._extend.Method({ + name: 'getBlobSidecars', + call: 'eth_getBlobSidecars', + params: 1, + }), + new web3._extend.Method({ + name: 'getBlobSidecarByTxHash', + call: 'eth_getBlobSidecarByTxHash', + params: 1, + }), ], properties: [ new web3._extend.Property({