diff --git a/cmd/shisui/main.go b/cmd/shisui/main.go index d77a0ec6da..7f252ab083 100644 --- a/cmd/shisui/main.go +++ b/cmd/shisui/main.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/internal/flags" "github.com/ethereum/go-ethereum/log" @@ -23,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/portalnetwork/beacon" + "github.com/ethereum/go-ethereum/portalnetwork/ethapi" "github.com/ethereum/go-ethereum/portalnetwork/history" "github.com/ethereum/go-ethereum/portalnetwork/state" "github.com/ethereum/go-ethereum/portalnetwork/storage" @@ -122,8 +124,9 @@ func startPortalRpcServer(config Config, conn discover.UDPConn, addr string) err return err } + var historyNetwork *history.HistoryNetwork if slices.Contains(config.Networks, portalwire.History.Name()) { - err = initHistory(config, server, conn, localNode, discV5) + historyNetwork, err = initHistory(config, server, conn, localNode, discV5) if err != nil { return err } @@ -143,6 +146,16 @@ func startPortalRpcServer(config Config, conn discover.UDPConn, addr string) err } } + ethapi := ðapi.API{ + History: historyNetwork, + //static configuration of ChainId, currently only mainnet implemented + ChainID: core.DefaultGenesisBlock().Config.ChainID, + } + err = server.RegisterName("eth", ethapi) + if err != nil { + return err + } + httpServer := &http.Server{ Addr: addr, Handler: server, @@ -174,11 +187,11 @@ func initDiscV5(config Config, conn discover.UDPConn) (*discover.UDPv5, *enode.L return discV5, localNode, nil } -func initHistory(config Config, server *rpc.Server, conn discover.UDPConn, localNode *enode.LocalNode, discV5 *discover.UDPv5) error { +func initHistory(config Config, server *rpc.Server, conn discover.UDPConn, localNode *enode.LocalNode, discV5 *discover.UDPv5) (*history.HistoryNetwork, error) { networkName := portalwire.History.Name() db, err := history.NewDB(config.DataDir, networkName) if err != nil { - return err + return nil, err } contentStorage, err := history.NewHistoryStorage(storage.PortalStorageConfig{ StorageCapacityMB: config.DataCapacity, @@ -187,27 +200,27 @@ func initHistory(config Config, server *rpc.Server, conn discover.UDPConn, local NetworkName: networkName, }) if err != nil { - return err + return nil, err } contentQueue := make(chan *discover.ContentElement, 50) protocol, err := discover.NewPortalProtocol(config.Protocol, portalwire.History, config.PrivateKey, conn, localNode, discV5, contentStorage, contentQueue) if err != nil { - return err + return nil, err } historyAPI := discover.NewPortalAPI(protocol) historyNetworkAPI := history.NewHistoryNetworkAPI(historyAPI) err = server.RegisterName("portal", historyNetworkAPI) if err != nil { - return err + return nil, err } accumulator, err := history.NewMasterAccumulator() if err != nil { - return err + return nil, err } historyNetwork := history.NewHistoryNetwork(protocol, &accumulator) - return historyNetwork.Start() + return historyNetwork, historyNetwork.Start() } func initBeacon(config Config, server *rpc.Server, conn discover.UDPConn, localNode *enode.LocalNode, discV5 *discover.UDPv5) error { diff --git a/portalnetwork/ethapi/api.go b/portalnetwork/ethapi/api.go new file mode 100644 index 0000000000..16944bf5b5 --- /dev/null +++ b/portalnetwork/ethapi/api.go @@ -0,0 +1,231 @@ +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) + +}