package ethapi import ( "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/portalnetwork/history" ) func rpcMarshalBlock(block *types.Block, fullTx bool, chainId *big.Int) (map[string]interface{}, error) { fields := RPCMarshalHeader(block.Header()) fields["size"] = hexutil.Uint64(block.Size()) formatTx := func(idx int, tx *types.Transaction) interface{} { return tx.Hash() } if fullTx { formatTx = func(idx int, tx *types.Transaction) interface{} { return newRPCTransactionFromBlockIndex(block, uint64(idx), chainId) } } txs := block.Transactions() transactions := make([]interface{}, len(txs)) for i, tx := range txs { transactions[i] = formatTx(i, tx) } fields["transactions"] = transactions uncles := block.Uncles() uncleHashes := make([]common.Hash, len(uncles)) for i, uncle := range uncles { uncleHashes[i] = uncle.Hash() } fields["uncles"] = uncleHashes if block.Header().WithdrawalsHash != nil { fields["withdrawals"] = block.Withdrawals() } return fields, nil } func RPCMarshalHeader(head *types.Header) map[string]interface{} { result := map[string]interface{}{ "number": (*hexutil.Big)(head.Number), "hash": head.Hash(), "parentHash": head.ParentHash, "nonce": head.Nonce, "mixHash": head.MixDigest, "sha3Uncles": head.UncleHash, "logsBloom": head.Bloom, "stateRoot": head.Root, "miner": head.Coinbase, "difficulty": (*hexutil.Big)(head.Difficulty), "extraData": hexutil.Bytes(head.Extra), "gasLimit": hexutil.Uint64(head.GasLimit), "gasUsed": hexutil.Uint64(head.GasUsed), "timestamp": hexutil.Uint64(head.Time), "transactionsRoot": head.TxHash, "receiptsRoot": head.ReceiptHash, } if head.BaseFee != nil { result["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee) } if head.WithdrawalsHash != nil { result["withdrawalsRoot"] = head.WithdrawalsHash } if head.BlobGasUsed != nil { result["blobGasUsed"] = hexutil.Uint64(*head.BlobGasUsed) } if head.ExcessBlobGas != nil { result["excessBlobGas"] = hexutil.Uint64(*head.ExcessBlobGas) } if head.ParentBeaconRoot != nil { result["parentBeaconBlockRoot"] = head.ParentBeaconRoot } return result } func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, chainId *big.Int) *RPCTransaction { txs := b.Transactions() if index >= uint64(len(txs)) { return nil } return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time(), index, b.BaseFee(), chainId) } func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, blockTime uint64, index uint64, baseFee *big.Int, chainId *big.Int) *RPCTransaction { config := ¶ms.ChainConfig{ ChainID: chainId, } signer := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) from, _ := types.Sender(signer, tx) v, r, s := tx.RawSignatureValues() result := &RPCTransaction{ Type: hexutil.Uint64(tx.Type()), From: from, Gas: hexutil.Uint64(tx.Gas()), GasPrice: (*hexutil.Big)(tx.GasPrice()), Hash: tx.Hash(), Input: hexutil.Bytes(tx.Data()), Nonce: hexutil.Uint64(tx.Nonce()), To: tx.To(), Value: (*hexutil.Big)(tx.Value()), V: (*hexutil.Big)(v), R: (*hexutil.Big)(r), S: (*hexutil.Big)(s), } if blockHash != (common.Hash{}) { result.BlockHash = &blockHash result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) result.TransactionIndex = (*hexutil.Uint64)(&index) } switch tx.Type() { case types.LegacyTxType: // if a legacy transaction has an EIP-155 chain id, include it explicitly if id := tx.ChainId(); id.Sign() != 0 { result.ChainID = (*hexutil.Big)(id) } case types.AccessListTxType: al := tx.AccessList() yparity := hexutil.Uint64(v.Sign()) result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) result.YParity = &yparity case types.DynamicFeeTxType: al := tx.AccessList() yparity := hexutil.Uint64(v.Sign()) result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) result.YParity = &yparity result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) // if the transaction has been mined, compute the effective gas price if baseFee != nil && blockHash != (common.Hash{}) { // price = min(gasTipCap + baseFee, gasFeeCap) result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee)) } else { result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) } case types.BlobTxType: al := tx.AccessList() yparity := hexutil.Uint64(v.Sign()) result.Accesses = &al result.ChainID = (*hexutil.Big)(tx.ChainId()) result.YParity = &yparity result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) // if the transaction has been mined, compute the effective gas price if baseFee != nil && blockHash != (common.Hash{}) { result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee)) } else { result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) } result.MaxFeePerBlobGas = (*hexutil.Big)(tx.BlobGasFeeCap()) result.BlobVersionedHashes = tx.BlobHashes() } return result } type RPCTransaction struct { BlockHash *common.Hash `json:"blockHash"` BlockNumber *hexutil.Big `json:"blockNumber"` From common.Address `json:"from"` Gas hexutil.Uint64 `json:"gas"` GasPrice *hexutil.Big `json:"gasPrice"` GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` Hash common.Hash `json:"hash"` Input hexutil.Bytes `json:"input"` Nonce hexutil.Uint64 `json:"nonce"` To *common.Address `json:"to"` TransactionIndex *hexutil.Uint64 `json:"transactionIndex"` Value *hexutil.Big `json:"value"` Type hexutil.Uint64 `json:"type"` Accesses *types.AccessList `json:"accessList,omitempty"` ChainID *hexutil.Big `json:"chainId,omitempty"` BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` V *hexutil.Big `json:"v"` R *hexutil.Big `json:"r"` S *hexutil.Big `json:"s"` YParity *hexutil.Uint64 `json:"yParity,omitempty"` } func effectiveGasPrice(tx *types.Transaction, baseFee *big.Int) *big.Int { fee := tx.GasTipCap() fee = fee.Add(fee, baseFee) if tx.GasFeeCapIntCmp(fee) < 0 { return tx.GasFeeCap() } return fee } type API struct { History *history.HistoryNetwork ChainID *big.Int } func (p *API) ChainId() hexutil.Uint64 { return (hexutil.Uint64)(p.ChainID.Uint64()) } func (p *API) GetBlockByHash(hash *common.Hash, fullTransactions bool) (map[string]interface{}, error) { blockHeader, err := p.History.GetBlockHeader(hash.Bytes()) if err != nil { log.Error(err.Error()) return nil, err } blockBody, err := p.History.GetBlockBody(hash.Bytes()) if err != nil { log.Error(err.Error()) return nil, err } block := types.NewBlockWithHeader(blockHeader).WithBody(*blockBody) return rpcMarshalBlock(block, fullTransactions, p.ChainID) }