diff --git a/node/api.go b/node/api.go index 5f69ea5d8..8c2bf4ada 100644 --- a/node/api.go +++ b/node/api.go @@ -117,6 +117,7 @@ func (node *Node) APIs(harmony *hmy.Harmony) []rpc.API { Service: filters.NewPublicFilterAPI(harmony, false), Public: true, }, + hmy_rpc.NewPublicNetAPI(node.host, harmony.ChainID, hmy_rpc.Eth), } } diff --git a/rpc/blockchain.go b/rpc/blockchain.go index fe4b37953..802604548 100644 --- a/rpc/blockchain.go +++ b/rpc/blockchain.go @@ -16,6 +16,7 @@ import ( "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/numeric" rpc_common "github.com/harmony-one/harmony/rpc/common" + eth "github.com/harmony-one/harmony/rpc/eth" v1 "github.com/harmony-one/harmony/rpc/v1" v2 "github.com/harmony-one/harmony/rpc/v2" "github.com/harmony-one/harmony/shard" @@ -150,16 +151,19 @@ func (s *PublicBlockchainService) GetBlockByNumber( leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) var rpcBlock interface{} switch s.version { - case V1, Eth: + case V1: rpcBlock, err = v1.NewBlock(blk, blockArgs, leader) case V2: rpcBlock, err = v2.NewBlock(blk, blockArgs, leader) + case Eth: + rpcBlock, err = eth.NewBlock(blk, blockArgs, leader) default: return nil, ErrUnknownRPCVersion } if err != nil { return nil, err } + response, err = NewStructuredResponse(rpcBlock) if err != nil { return nil, err @@ -171,6 +175,7 @@ func (s *PublicBlockchainService) GetBlockByNumber( response[field] = nil } } + return response, err } @@ -207,10 +212,12 @@ func (s *PublicBlockchainService) GetBlockByHash( leader := s.hmy.GetLeaderAddress(blk.Header().Coinbase(), blk.Header().Epoch()) var rpcBlock interface{} switch s.version { - case V1, Eth: + case V1: rpcBlock, err = v1.NewBlock(blk, blockArgs, leader) case V2: rpcBlock, err = v2.NewBlock(blk, blockArgs, leader) + case Eth: + rpcBlock, err = eth.NewBlock(blk, blockArgs, leader) default: return nil, ErrUnknownRPCVersion } diff --git a/rpc/eth/rpc.go b/rpc/eth/rpc.go new file mode 100644 index 000000000..8c4c75974 --- /dev/null +++ b/rpc/eth/rpc.go @@ -0,0 +1,43 @@ +package v1 + +import ( + "context" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" + "github.com/harmony-one/harmony/hmy" + internal_common "github.com/harmony-one/harmony/internal/common" +) + +// PublicEthService provides an API to access to the Eth endpoints for the Harmony blockchain. +type PublicEthService struct { + hmy *hmy.Harmony +} + +// NewPublicEthService creates a new API for the RPC interface +func NewPublicEthService(hmy *hmy.Harmony, namespace string) rpc.API { + if namespace == "" { + namespace = "eth" + } + + return rpc.API{ + Namespace: namespace, + Version: "1.0", + Service: &PublicEthService{hmy}, + Public: true, + } +} + +// GetBalance returns the amount of Atto for the given address in the state of the +// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta +// block numbers are also allowed. +func (s *PublicEthService) GetBalance( + ctx context.Context, address string, blockNr rpc.BlockNumber, +) (*hexutil.Big, error) { + addr := internal_common.ParseAddr(address) + balance, err := s.hmy.GetBalance(ctx, addr, blockNr) + if err != nil { + return nil, err + } + return (*hexutil.Big)(balance), nil +} diff --git a/rpc/eth/types.go b/rpc/eth/types.go new file mode 100644 index 000000000..addaf4a8f --- /dev/null +++ b/rpc/eth/types.go @@ -0,0 +1,343 @@ +package v1 + +import ( + "fmt" + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/harmony-one/harmony/core/types" + internal_common "github.com/harmony-one/harmony/internal/common" + rpc_common "github.com/harmony-one/harmony/rpc/common" + rpc_utils "github.com/harmony-one/harmony/rpc/utils" +) + +// BlockWithTxHash represents a block that will serialize to the RPC representation of a block +// having ONLY transaction hashes in the Transaction fields. +type BlockWithTxHash struct { + Number *hexutil.Big `json:"number"` + ViewID *hexutil.Big `json:"viewID"` + Epoch *hexutil.Big `json:"epoch"` + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` + Nonce uint64 `json:"nonce"` + MixHash common.Hash `json:"mixHash"` + LogsBloom ethtypes.Bloom `json:"logsBloom"` + StateRoot common.Hash `json:"stateRoot"` + Miner string `json:"miner"` + Difficulty uint64 `json:"difficulty"` + ExtraData hexutil.Bytes `json:"extraData"` + Size hexutil.Uint64 `json:"size"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Timestamp hexutil.Uint64 `json:"timestamp"` + TransactionsRoot common.Hash `json:"transactionsRoot"` + ReceiptsRoot common.Hash `json:"receiptsRoot"` + Uncles []common.Hash `json:"uncles"` + Transactions []common.Hash `json:"transactions"` + Signers []string `json:"signers,omitempty"` +} + +// BlockWithFullTx represents a block that will serialize to the RPC representation of a block +// having FULL transactions in the Transaction fields. +type BlockWithFullTx struct { + Number *hexutil.Big `json:"number"` + ViewID *hexutil.Big `json:"viewID"` + Epoch *hexutil.Big `json:"epoch"` + Hash common.Hash `json:"hash"` + ParentHash common.Hash `json:"parentHash"` + Nonce uint64 `json:"nonce"` + MixHash common.Hash `json:"mixHash"` + LogsBloom ethtypes.Bloom `json:"logsBloom"` + StateRoot common.Hash `json:"stateRoot"` + Miner string `json:"miner"` + Difficulty uint64 `json:"difficulty"` + ExtraData hexutil.Bytes `json:"extraData"` + Size hexutil.Uint64 `json:"size"` + GasLimit hexutil.Uint64 `json:"gasLimit"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + Timestamp hexutil.Uint64 `json:"timestamp"` + TransactionsRoot common.Hash `json:"transactionsRoot"` + ReceiptsRoot common.Hash `json:"receiptsRoot"` + Uncles []common.Hash `json:"uncles"` + Transactions []*Transaction `json:"transactions"` + Signers []string `json:"signers,omitempty"` +} + +// Transaction represents a transaction that will serialize to the RPC representation of a transaction +type Transaction struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + From string `json:"from"` + Timestamp hexutil.Uint64 `json:"timestamp"` + Gas hexutil.Uint64 `json:"gas"` + GasPrice *hexutil.Big `json:"gasPrice"` + Hash common.Hash `json:"hash"` + Input hexutil.Bytes `json:"input"` + Nonce hexutil.Uint64 `json:"nonce"` + To string `json:"to"` + TransactionIndex hexutil.Uint `json:"transactionIndex"` + Value *hexutil.Big `json:"value"` + ShardID uint32 `json:"shardID"` + ToShardID uint32 `json:"toShardID"` + V *hexutil.Big `json:"v"` + R *hexutil.Big `json:"r"` + S *hexutil.Big `json:"s"` +} + +// TxReceipt represents a transaction receipt that will serialize to the RPC representation. +type TxReceipt struct { + BlockHash common.Hash `json:"blockHash"` + TransactionHash common.Hash `json:"transactionHash"` + BlockNumber hexutil.Uint64 `json:"blockNumber"` + TransactionIndex hexutil.Uint64 `json:"transactionIndex"` + GasUsed hexutil.Uint64 `json:"gasUsed"` + CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed"` + ContractAddress common.Address `json:"contractAddress"` + Logs []*types.Log `json:"logs"` + LogsBloom ethtypes.Bloom `json:"logsBloom"` + ShardID uint32 `json:"shardID"` + From string `json:"from"` + To string `json:"to"` + Root hexutil.Bytes `json:"root"` + Status hexutil.Uint `json:"status"` +} + +// CxReceipt represents a CxReceipt that will serialize to the RPC representation of a CxReceipt +type CxReceipt struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber *hexutil.Big `json:"blockNumber"` + TxHash common.Hash `json:"hash"` + From string `json:"from"` + To string `json:"to"` + ShardID uint32 `json:"shardID"` + ToShardID uint32 `json:"toShardID"` + Amount *hexutil.Big `json:"value"` +} + +// NewTransaction returns a transaction that will serialize to the RPC +// representation, with the given location metadata set (if available). +// Note that all txs on Harmony are replay protected (post EIP155 epoch). +func NewTransaction( + tx *types.Transaction, blockHash common.Hash, + blockNumber uint64, timestamp uint64, index uint64, +) (*Transaction, error) { + from, err := tx.SenderAddress() + if err != nil { + return nil, err + } + v, r, s := tx.RawSignatureValues() + + result := &Transaction{ + Gas: hexutil.Uint64(tx.GasLimit()), + GasPrice: (*hexutil.Big)(tx.GasPrice()), + Hash: tx.Hash(), + Input: hexutil.Bytes(tx.Data()), + Nonce: hexutil.Uint64(tx.Nonce()), + Value: (*hexutil.Big)(tx.Value()), + ShardID: tx.ShardID(), + ToShardID: tx.ToShardID(), + Timestamp: hexutil.Uint64(timestamp), + 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.Uint(index) + } + + result.From, result.To, err = rpc_utils.ConvertAddresses(&from, tx.To(), false) + if err != nil { + return nil, err + } + + return result, nil +} + +// NewReceipt returns an ETH transaction transaction that will serialize to the RPC representation. +func NewReceipt( + tx interface{}, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt, +) (interface{}, error) { + plainTx, ok := tx.(*types.Transaction) + if ok { + return NewTxReceipt(plainTx, blockHash, blockNumber, blockIndex, receipt) + } + return nil, fmt.Errorf("unknown transaction type for RPC receipt") +} + +// NewTxReceipt returns a transaction receipt that will serialize to the RPC representation +func NewTxReceipt( + tx *types.Transaction, blockHash common.Hash, blockNumber, blockIndex uint64, receipt *types.Receipt, +) (*TxReceipt, error) { + // Set correct to & from address + senderAddr, err := tx.SenderAddress() + if err != nil { + return nil, err + } + + sender, receiver, err := rpc_utils.ConvertAddresses(&senderAddr, tx.To(), false) + if err != nil { + return nil, err + } + + // Declare receipt + txReceipt := &TxReceipt{ + BlockHash: blockHash, + TransactionHash: tx.Hash(), + BlockNumber: hexutil.Uint64(blockNumber), + TransactionIndex: hexutil.Uint64(blockIndex), + GasUsed: hexutil.Uint64(receipt.GasUsed), + CumulativeGasUsed: hexutil.Uint64(receipt.CumulativeGasUsed), + Logs: receipt.Logs, + LogsBloom: receipt.Bloom, + ShardID: tx.ShardID(), + From: sender, + To: receiver, + Root: receipt.PostState, + Status: hexutil.Uint(receipt.Status), + } + + // Set empty array for empty logs + if receipt.Logs == nil { + txReceipt.Logs = []*types.Log{} + } + + // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation + if receipt.ContractAddress != (common.Address{}) { + txReceipt.ContractAddress = receipt.ContractAddress + } + return txReceipt, nil +} + +// NewBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are +// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain +// transaction hashes. +func NewBlock(b *types.Block, blockArgs *rpc_common.BlockArgs, leader string) (interface{}, error) { + if strings.HasPrefix(leader, "one1") { + // Handle hex address + addr, err := internal_common.Bech32ToAddress(leader) + if err != nil { + return nil, err + } + leader = addr.String() + } + + if blockArgs.FullTx { + return NewBlockWithFullTx(b, blockArgs, leader) + } + return NewBlockWithTxHash(b, blockArgs, leader) +} + +// NewBlockWithTxHash .. +func NewBlockWithTxHash( + b *types.Block, blockArgs *rpc_common.BlockArgs, leader string, +) (*BlockWithTxHash, error) { + if blockArgs.FullTx { + return nil, fmt.Errorf("block args specifies full tx, but requested RPC block with only tx hash") + } + + head := b.Header() + blk := &BlockWithTxHash{ + Number: (*hexutil.Big)(head.Number()), + ViewID: (*hexutil.Big)(head.ViewID()), + Epoch: (*hexutil.Big)(head.Epoch()), + Hash: b.Hash(), + ParentHash: head.ParentHash(), + Nonce: 0, // Remove this because we don't have it in our header + MixHash: head.MixDigest(), + LogsBloom: head.Bloom(), + StateRoot: head.Root(), + Miner: leader, + Difficulty: 0, // Remove this because we don't have it in our header + ExtraData: hexutil.Bytes(head.Extra()), + Size: hexutil.Uint64(b.Size()), + GasLimit: hexutil.Uint64(head.GasLimit()), + GasUsed: hexutil.Uint64(head.GasUsed()), + Timestamp: hexutil.Uint64(head.Time().Uint64()), + TransactionsRoot: head.TxHash(), + ReceiptsRoot: head.ReceiptHash(), + Uncles: []common.Hash{}, + Transactions: []common.Hash{}, + } + + for _, tx := range b.Transactions() { + blk.Transactions = append(blk.Transactions, tx.Hash()) + } + + if blockArgs.WithSigners { + blk.Signers = blockArgs.Signers + } + return blk, nil +} + +// NewBlockWithFullTx .. +func NewBlockWithFullTx( + b *types.Block, blockArgs *rpc_common.BlockArgs, leader string, +) (*BlockWithFullTx, error) { + if !blockArgs.FullTx { + return nil, fmt.Errorf("block args specifies NO full tx, but requested RPC block with full tx") + } + + head := b.Header() + blk := &BlockWithFullTx{ + Number: (*hexutil.Big)(head.Number()), + ViewID: (*hexutil.Big)(head.ViewID()), + Epoch: (*hexutil.Big)(head.Epoch()), + Hash: b.Hash(), + ParentHash: head.ParentHash(), + Nonce: 0, // Remove this because we don't have it in our header + MixHash: head.MixDigest(), + LogsBloom: head.Bloom(), + StateRoot: head.Root(), + Miner: leader, + Difficulty: 0, // Remove this because we don't have it in our header + ExtraData: hexutil.Bytes(head.Extra()), + Size: hexutil.Uint64(b.Size()), + GasLimit: hexutil.Uint64(head.GasLimit()), + GasUsed: hexutil.Uint64(head.GasUsed()), + Timestamp: hexutil.Uint64(head.Time().Uint64()), + TransactionsRoot: head.TxHash(), + ReceiptsRoot: head.ReceiptHash(), + Uncles: []common.Hash{}, + Transactions: []*Transaction{}, + } + + for _, tx := range b.Transactions() { + fmtTx, err := NewTransactionFromBlockHash(b, tx.Hash()) + if err != nil { + return nil, err + } + blk.Transactions = append(blk.Transactions, fmtTx) + } + + if blockArgs.WithSigners { + blk.Signers = blockArgs.Signers + } + + return blk, nil +} + +// NewTransactionFromBlockHash returns a transaction that will serialize to the RPC representation. +func NewTransactionFromBlockHash(b *types.Block, hash common.Hash) (*Transaction, error) { + for idx, tx := range b.Transactions() { + if tx.Hash() == hash { + return NewTransactionFromBlockIndex(b, uint64(idx)) + } + } + return nil, fmt.Errorf("tx %v not found in block %v", hash, b.Hash().String()) +} + +// NewTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. +func NewTransactionFromBlockIndex(b *types.Block, index uint64) (*Transaction, error) { + txs := b.Transactions() + if index >= uint64(len(txs)) { + return nil, fmt.Errorf( + "tx index %v greater than or equal to number of transactions on block %v", index, b.Hash().String(), + ) + } + return NewTransaction(txs[index], b.Hash(), b.NumberU64(), b.Time().Uint64(), index) +} diff --git a/rpc/net.go b/rpc/net.go index 2771ecab0..1c73c0a4a 100644 --- a/rpc/net.go +++ b/rpc/net.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/rpc" + nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/harmony-one/harmony/internal/utils" "github.com/harmony-one/harmony/p2p" ) @@ -22,10 +23,12 @@ func NewPublicNetAPI(net p2p.Host, chainID uint64, version Version) rpc.API { // manually set different namespace to preserve legacy behavior var namespace string switch version { - case V1, Eth: + case V1: namespace = netV1Namespace case V2: namespace = netV2Namespace + case Eth: + namespace = netNamespace default: utils.Logger().Error().Msgf("Unknown version %v, ignoring API.", version) return rpc.API{} @@ -54,6 +57,11 @@ func (s *PublicNetService) PeerCount(ctx context.Context) (interface{}, error) { } // Version returns the network version, i.e. ChainID identifying which network we are using -func (s *PublicNetService) Version(ctx context.Context) string { - return fmt.Sprintf("%d", s.chainID) +func (s *PublicNetService) Version(ctx context.Context) interface{} { + switch s.version { + case Eth: + return hexutil.Uint64(nodeconfig.GetDefaultConfig().GetNetworkType().ChainConfig().EthCompatibleChainID.Uint64()) + default: + return fmt.Sprintf("%d", s.chainID) + } } diff --git a/rpc/pool.go b/rpc/pool.go index fa3126469..afade72a7 100644 --- a/rpc/pool.go +++ b/rpc/pool.go @@ -16,6 +16,7 @@ import ( common2 "github.com/harmony-one/harmony/internal/common" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/harmony-one/harmony/internal/utils" + eth "github.com/harmony-one/harmony/rpc/eth" v1 "github.com/harmony-one/harmony/rpc/v1" v2 "github.com/harmony-one/harmony/rpc/v2" staking "github.com/harmony-one/harmony/staking/types" @@ -184,7 +185,7 @@ func (s *PublicPoolService) PendingTransactions( if plainTx, ok := pending[i].(*types.Transaction); ok { var tx interface{} switch s.version { - case V1, Eth: + case V1: tx, err = v1.NewTransaction(plainTx, common.Hash{}, 0, 0, 0) if err != nil { utils.Logger().Debug(). @@ -200,6 +201,14 @@ func (s *PublicPoolService) PendingTransactions( Msgf("%v error at %v", LogTag, "PendingTransactions") continue // Legacy behavior is to not return error here } + case Eth: + tx, err = eth.NewTransaction(plainTx, common.Hash{}, 0, 0, 0) + if err != nil { + utils.Logger().Debug(). + Err(err). + Msgf("%v error at %v", LogTag, "PendingTransactions") + continue // Legacy behavior is to not return error here + } default: return nil, ErrUnknownRPCVersion } @@ -239,7 +248,7 @@ func (s *PublicPoolService) PendingStakingTransactions( } else if stakingTx, ok := pending[i].(*staking.StakingTransaction); ok { var tx interface{} switch s.version { - case V1, Eth: + case V1: tx, err = v1.NewStakingTransaction(stakingTx, common.Hash{}, 0, 0, 0) if err != nil { utils.Logger().Debug(). diff --git a/rpc/rpc.go b/rpc/rpc.go index 7fd53175f..cf9a96be8 100644 --- a/rpc/rpc.go +++ b/rpc/rpc.go @@ -10,6 +10,7 @@ import ( "github.com/harmony-one/harmony/hmy" nodeconfig "github.com/harmony-one/harmony/internal/configs/node" "github.com/harmony-one/harmony/internal/utils" + eth "github.com/harmony-one/harmony/rpc/eth" v1 "github.com/harmony-one/harmony/rpc/v1" v2 "github.com/harmony-one/harmony/rpc/v2" ) @@ -34,15 +35,16 @@ const ( // WSPortOffset .. WSPortOffset = 800 - netV1Namespace = "net" + netNamespace = "net" + netV1Namespace = "netv1" netV2Namespace = "netv2" ) var ( // HTTPModules .. - HTTPModules = []string{"hmy", "hmyv2", "eth", "debug", netV1Namespace, netV2Namespace, "explorer"} + HTTPModules = []string{"hmy", "hmyv2", "eth", "debug", netNamespace, netV1Namespace, netV2Namespace, "explorer"} // WSModules .. - WSModules = []string{"hmy", "hmyv2", "eth", "debug", netV1Namespace, netV2Namespace, "web3"} + WSModules = []string{"hmy", "hmyv2", "eth", "debug", netNamespace, netV1Namespace, netV2Namespace, "web3"} httpListener net.Listener httpHandler *rpc.Server @@ -140,7 +142,7 @@ func getAPIs(hmy *hmy.Harmony, debugEnable bool) []rpc.API { NewPublicTracerAPI(hmy, Debug), // Legacy methods (subject to removal) v1.NewPublicLegacyAPI(hmy, "hmy"), - v1.NewPublicLegacyAPI(hmy, "eth"), + eth.NewPublicEthService(hmy, "eth"), v2.NewPublicLegacyAPI(hmy, "hmyv2"), } diff --git a/rpc/transaction.go b/rpc/transaction.go index 615ad62a7..f3065679f 100644 --- a/rpc/transaction.go +++ b/rpc/transaction.go @@ -18,6 +18,7 @@ import ( internal_common "github.com/harmony-one/harmony/internal/common" "github.com/harmony-one/harmony/internal/params" "github.com/harmony-one/harmony/internal/utils" + eth "github.com/harmony-one/harmony/rpc/eth" v1 "github.com/harmony-one/harmony/rpc/v1" v2 "github.com/harmony-one/harmony/rpc/v2" staking "github.com/harmony-one/harmony/staking/types" @@ -173,7 +174,7 @@ func (s *PublicTransactionService) GetTransactionByHash( // Format the response according to the version switch s.version { - case V1, Eth: + case V1: tx, err := v1.NewTransaction(tx, blockHash, blockNumber, block.Time().Uint64(), index) if err != nil { return nil, err @@ -185,6 +186,12 @@ func (s *PublicTransactionService) GetTransactionByHash( return nil, err } return NewStructuredResponse(tx) + case Eth: + tx, err := eth.NewTransaction(tx, blockHash, blockNumber, block.Time().Uint64(), index) + if err != nil { + return nil, err + } + return NewStructuredResponse(tx) default: return nil, ErrUnknownRPCVersion } @@ -213,7 +220,7 @@ func (s *PublicTransactionService) GetStakingTransactionByHash( } switch s.version { - case V1, Eth: + case V1: tx, err := v1.NewStakingTransaction(stx, blockHash, blockNumber, block.Time().Uint64(), index) if err != nil { return nil, err @@ -402,7 +409,7 @@ func (s *PublicTransactionService) GetTransactionByBlockNumberAndIndex( // Format response according to version switch s.version { - case V1, Eth: + case V1: tx, err := v1.NewTransactionFromBlockIndex(block, uint64(index)) if err != nil { return nil, err @@ -414,6 +421,12 @@ func (s *PublicTransactionService) GetTransactionByBlockNumberAndIndex( return nil, err } return NewStructuredResponse(tx) + case Eth: + tx, err := eth.NewTransactionFromBlockIndex(block, uint64(index)) + if err != nil { + return nil, err + } + return NewStructuredResponse(tx) default: return nil, ErrUnknownRPCVersion } @@ -435,7 +448,7 @@ func (s *PublicTransactionService) GetTransactionByBlockHashAndIndex( // Format response according to version switch s.version { - case V1, Eth: + case V1: tx, err := v1.NewTransactionFromBlockIndex(block, uint64(index)) if err != nil { return nil, err @@ -447,6 +460,12 @@ func (s *PublicTransactionService) GetTransactionByBlockHashAndIndex( return nil, err } return NewStructuredResponse(tx) + case Eth: + tx, err := eth.NewTransactionFromBlockIndex(block, uint64(index)) + if err != nil { + return nil, err + } + return NewStructuredResponse(tx) default: return nil, ErrUnknownRPCVersion } @@ -526,7 +545,7 @@ func (s *PublicTransactionService) GetStakingTransactionByBlockNumberAndIndex( // Format response according to version switch s.version { - case V1, Eth: + case V1: tx, err := v1.NewStakingTransactionFromBlockIndex(block, uint64(index)) if err != nil { return nil, err @@ -608,7 +627,7 @@ func (s *PublicTransactionService) GetTransactionReceipt( // Format response according to version var RPCReceipt interface{} switch s.version { - case V1, Eth: + case V1: if tx == nil { RPCReceipt, err = v1.NewReceipt(stx, blockHash, blockNumber, index, receipt) } else { @@ -628,6 +647,16 @@ func (s *PublicTransactionService) GetTransactionReceipt( return nil, err } return NewStructuredResponse(RPCReceipt) + case Eth: + if tx == nil { + RPCReceipt, err = eth.NewReceipt(stx, blockHash, blockNumber, index, receipt) + } else { + RPCReceipt, err = eth.NewReceipt(tx, blockHash, blockNumber, index, receipt) + } + if err != nil { + return nil, err + } + return NewStructuredResponse(RPCReceipt) default: return nil, ErrUnknownRPCVersion } diff --git a/rpc/utils/utils.go b/rpc/utils/utils.go new file mode 100644 index 000000000..cd2633bca --- /dev/null +++ b/rpc/utils/utils.go @@ -0,0 +1,37 @@ +package utils + +import ( + "strings" + + "github.com/ethereum/go-ethereum/common" + internal_common "github.com/harmony-one/harmony/internal/common" +) + +// ConvertAddresses - converts to bech32 depending on the RPC version +func ConvertAddresses(from *common.Address, to *common.Address, convertToBech32 bool) (string, string, error) { + fromAddr := strings.ToLower(from.String()) + toAddr := "" + if to != nil { + toAddr = strings.ToLower(to.String()) + } + + if convertToBech32 { + return base16toBech32(from, to) + } + + return fromAddr, toAddr, nil +} + +func base16toBech32(from *common.Address, to *common.Address) (fromAddr string, toAddr string, err error) { + if fromAddr, err = internal_common.AddressToBech32(*from); err != nil { + return "", "", err + } + + if to != nil { + if toAddr, err = internal_common.AddressToBech32(*to); err != nil { + return "", "", err + } + } + + return fromAddr, toAddr, nil +}