From 5260aeef2916f65441c12c35f8af201445869a55 Mon Sep 17 00:00:00 2001 From: Janet Liang <56005637+janet-harmony@users.noreply.github.com> Date: Wed, 23 Sep 2020 16:56:16 -0700 Subject: [PATCH] [rosetta] Mempool Endpoint (#3339) * [rosetta] Implement mempool endpoint * [block] Make FormatTransaction public to use in the Mempool * [mempool] Create Transaction for /mempool/transaction using estimated transaction receipt * [rosetta] Add Mempool API to the Router * [rosetta] Update block test file with public function * [rosetta] Add comments to explain estimated transaction receipt * [rosetta] Revert making function public * [rosetta] Fix hash conversion for looking up transaction in pool * [rosetta] Fix lint * [rosetta] Fix lint (again) * [rosetta] Fake logs for CollectReward transaction * [rosetta] Fix lint * [rosetta] Fix lint again... --- rosetta/rosetta.go | 5 +- rosetta/services/mempool.go | 95 +++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 rosetta/services/mempool.go diff --git a/rosetta/rosetta.go b/rosetta/rosetta.go index cd736c3b1..32630aebf 100644 --- a/rosetta/rosetta.go +++ b/rosetta/rosetta.go @@ -76,9 +76,10 @@ func newHTTPServer(handler http.Handler) *http.Server { func getRouter(asserter *asserter.Asserter, hmy *hmy.Harmony) http.Handler { return server.NewRouter( - server.NewNetworkAPIController(services.NewNetworkAPI(hmy), asserter), - server.NewBlockAPIController(services.NewBlockAPI(hmy), asserter), server.NewAccountAPIController(services.NewAccountAPI(hmy), asserter), + server.NewBlockAPIController(services.NewBlockAPI(hmy), asserter), + server.NewMempoolAPIController(services.NewMempoolAPI(hmy), asserter), + server.NewNetworkAPIController(services.NewNetworkAPI(hmy), asserter), ) } diff --git a/rosetta/services/mempool.go b/rosetta/services/mempool.go new file mode 100644 index 000000000..48b5b1355 --- /dev/null +++ b/rosetta/services/mempool.go @@ -0,0 +1,95 @@ +package services + +import ( + "context" + "math/big" + + "github.com/coinbase/rosetta-sdk-go/server" + "github.com/coinbase/rosetta-sdk-go/types" + ethCommon "github.com/ethereum/go-ethereum/common" + hmyTypes "github.com/harmony-one/harmony/core/types" + "github.com/harmony-one/harmony/hmy" + "github.com/harmony-one/harmony/rosetta/common" + "github.com/harmony-one/harmony/staking" +) + +// MempoolAPI implements the server.MempoolAPIServicer interface +type MempoolAPI struct { + hmy *hmy.Harmony +} + +// NewMempoolAPI creates a new instance of MempoolAPI +func NewMempoolAPI(hmy *hmy.Harmony) server.MempoolAPIServicer { + return &MempoolAPI{ + hmy: hmy, + } +} + +// Mempool ... +func (s *MempoolAPI) Mempool( + ctx context.Context, req *types.NetworkRequest, +) (*types.MempoolResponse, *types.Error) { + if err := assertValidNetworkIdentifier(req.NetworkIdentifier, s.hmy.ShardID); err != nil { + return nil, err + } + + pool, err := s.hmy.GetPoolTransactions() + if err != nil { + return nil, common.NewError(common.CatchAllError, map[string]interface{}{ + "message": "unable to fetch pool transactions", + }) + } + txIDs := make([]*types.TransactionIdentifier, pool.Len()) + for i, tx := range pool { + txIDs[i] = &types.TransactionIdentifier{ + Hash: tx.Hash().String(), + } + } + return &types.MempoolResponse{ + TransactionIdentifiers: txIDs, + }, nil +} + +// MempoolTransaction ... +func (s *MempoolAPI) MempoolTransaction( + ctx context.Context, req *types.MempoolTransactionRequest, +) (*types.MempoolTransactionResponse, *types.Error) { + if err := assertValidNetworkIdentifier(req.NetworkIdentifier, s.hmy.ShardID); err != nil { + return nil, err + } + + hash := ethCommon.HexToHash(req.TransactionIdentifier.Hash) + poolTx := s.hmy.GetPoolTransaction(hash) + if poolTx == nil { + return nil, &common.TransactionNotFoundError + } + + var nilAddress ethCommon.Address + senderAddr, _ := poolTx.SenderAddress() + estLog := &hmyTypes.Log{ + Address: senderAddr, + Topics: []ethCommon.Hash{staking.CollectRewardsTopic}, + Data: big.NewInt(0).Bytes(), + BlockNumber: s.hmy.CurrentBlock().NumberU64(), + } + + estReceipt := &hmyTypes.Receipt{ + PostState: []byte{}, + Status: hmyTypes.ReceiptStatusSuccessful, // Assume transaction will succeed + CumulativeGasUsed: poolTx.Gas(), + Bloom: [256]byte{}, + Logs: []*hmyTypes.Log{estLog}, + TxHash: poolTx.Hash(), + ContractAddress: nilAddress, // ContractAddress is only for smart contract creation & can not be determined until transaction is finalized + GasUsed: poolTx.Gas(), + } + + respTx, err := formatTransaction(poolTx, estReceipt) + if err != nil { + return nil, err + } + + return &types.MempoolTransactionResponse{ + Transaction: respTx, + }, nil +}