github.com/ethersphere/bee/v2@v2.2.0/pkg/api/transaction.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package api 6 7 import ( 8 "errors" 9 "math/big" 10 "net/http" 11 "time" 12 13 "github.com/ethereum/go-ethereum/common" 14 "github.com/ethereum/go-ethereum/common/hexutil" 15 "github.com/ethersphere/bee/v2/pkg/bigint" 16 "github.com/ethersphere/bee/v2/pkg/jsonhttp" 17 "github.com/ethersphere/bee/v2/pkg/sctx" 18 "github.com/ethersphere/bee/v2/pkg/transaction" 19 "github.com/gorilla/mux" 20 ) 21 22 const ( 23 errCantGetTransaction = "cannot get transaction" 24 errUnknownTransaction = "unknown transaction" 25 errAlreadyImported = "already imported" 26 errCantResendTransaction = "can't resend transaction" 27 ) 28 29 type transactionInfo struct { 30 TransactionHash common.Hash `json:"transactionHash"` 31 To *common.Address `json:"to"` 32 Nonce uint64 `json:"nonce"` 33 GasPrice *bigint.BigInt `json:"gasPrice"` 34 GasLimit uint64 `json:"gasLimit"` 35 GasTipBoost int `json:"gasTipBoost"` 36 GasTipCap *bigint.BigInt `json:"gasTipCap"` 37 GasFeeCap *bigint.BigInt `json:"gasFeeCap"` 38 Data string `json:"data"` 39 Created time.Time `json:"created"` 40 Description string `json:"description"` 41 Value *bigint.BigInt `json:"value"` 42 } 43 44 type transactionPendingList struct { 45 PendingTransactions []transactionInfo `json:"pendingTransactions"` 46 } 47 48 func (s *Service) transactionListHandler(w http.ResponseWriter, _ *http.Request) { 49 logger := s.logger.WithName("get_transactions").Build() 50 51 txHashes, err := s.transaction.PendingTransactions() 52 if err != nil { 53 logger.Debug("get pending transactions failed", "error", err) 54 logger.Error(nil, "get pending transactions failed") 55 jsonhttp.InternalServerError(w, errCantGetTransaction) 56 return 57 } 58 59 transactionInfos := make([]transactionInfo, 0, len(txHashes)) 60 for _, txHash := range txHashes { 61 storedTransaction, err := s.transaction.StoredTransaction(txHash) 62 if err != nil { 63 logger.Debug("get stored transaction failed", "tx_hash", txHash, "error", err) 64 logger.Error(nil, "get stored transaction failed", "tx_hash", txHash) 65 jsonhttp.InternalServerError(w, errCantGetTransaction) 66 return 67 } 68 69 transactionInfos = append(transactionInfos, transactionInfo{ 70 TransactionHash: txHash, 71 To: storedTransaction.To, 72 Nonce: storedTransaction.Nonce, 73 GasPrice: bigint.Wrap(storedTransaction.GasPrice), 74 GasLimit: storedTransaction.GasLimit, 75 GasFeeCap: bigint.Wrap(storedTransaction.GasFeeCap), 76 GasTipCap: bigint.Wrap(storedTransaction.GasTipCap), 77 GasTipBoost: storedTransaction.GasTipBoost, 78 Data: hexutil.Encode(storedTransaction.Data), 79 Created: time.Unix(storedTransaction.Created, 0), 80 Description: storedTransaction.Description, 81 Value: bigint.Wrap(storedTransaction.Value), 82 }) 83 84 } 85 86 jsonhttp.OK(w, transactionPendingList{ 87 PendingTransactions: transactionInfos, 88 }) 89 } 90 91 func (s *Service) transactionDetailHandler(w http.ResponseWriter, r *http.Request) { 92 logger := s.logger.WithName("get_transaction").Build() 93 94 paths := struct { 95 Hash common.Hash `map:"hash"` 96 }{} 97 if response := s.mapStructure(mux.Vars(r), &paths); response != nil { 98 response("invalid path params", logger, w) 99 return 100 } 101 102 storedTransaction, err := s.transaction.StoredTransaction(paths.Hash) 103 if err != nil { 104 logger.Debug("get stored transaction failed", "tx_hash", paths.Hash, "error", err) 105 logger.Error(nil, "get stored transaction failed", "tx_hash", paths.Hash) 106 if errors.Is(err, transaction.ErrUnknownTransaction) { 107 jsonhttp.NotFound(w, errUnknownTransaction) 108 } else { 109 jsonhttp.InternalServerError(w, errCantGetTransaction) 110 } 111 return 112 } 113 114 jsonhttp.OK(w, transactionInfo{ 115 TransactionHash: paths.Hash, 116 To: storedTransaction.To, 117 Nonce: storedTransaction.Nonce, 118 GasPrice: bigint.Wrap(storedTransaction.GasPrice), 119 GasLimit: storedTransaction.GasLimit, 120 GasFeeCap: bigint.Wrap(storedTransaction.GasFeeCap), 121 GasTipCap: bigint.Wrap(storedTransaction.GasTipCap), 122 GasTipBoost: storedTransaction.GasTipBoost, 123 Data: hexutil.Encode(storedTransaction.Data), 124 Created: time.Unix(storedTransaction.Created, 0), 125 Description: storedTransaction.Description, 126 Value: bigint.Wrap(storedTransaction.Value), 127 }) 128 } 129 130 type transactionHashResponse struct { 131 TransactionHash common.Hash `json:"transactionHash"` 132 } 133 134 func (s *Service) transactionResendHandler(w http.ResponseWriter, r *http.Request) { 135 logger := s.logger.WithName("post_transaction").Build() 136 137 paths := struct { 138 Hash common.Hash `map:"hash"` 139 }{} 140 if response := s.mapStructure(mux.Vars(r), &paths); response != nil { 141 response("invalid path params", logger, w) 142 return 143 } 144 145 err := s.transaction.ResendTransaction(r.Context(), paths.Hash) 146 if err != nil { 147 logger.Debug("resend transaction failed", "tx_hash", paths.Hash, "error", err) 148 logger.Error(nil, "resend transaction failed", "tx_hash", paths.Hash) 149 if errors.Is(err, transaction.ErrUnknownTransaction) { 150 jsonhttp.NotFound(w, errUnknownTransaction) 151 } else if errors.Is(err, transaction.ErrAlreadyImported) { 152 jsonhttp.BadRequest(w, errAlreadyImported) 153 } else { 154 jsonhttp.InternalServerError(w, errCantResendTransaction) 155 } 156 return 157 } 158 159 jsonhttp.OK(w, transactionHashResponse{ 160 TransactionHash: paths.Hash, 161 }) 162 } 163 164 func (s *Service) transactionCancelHandler(w http.ResponseWriter, r *http.Request) { 165 logger := s.logger.WithName("delete_transaction").Build() 166 167 paths := struct { 168 Hash common.Hash `map:"hash"` 169 }{} 170 if response := s.mapStructure(mux.Vars(r), &paths); response != nil { 171 response("invalid path params", logger, w) 172 return 173 } 174 175 headers := struct { 176 GasPrice *big.Int `map:"Gas-Price"` 177 }{} 178 if response := s.mapStructure(r.Header, &headers); response != nil { 179 response("invalid header params", logger, w) 180 return 181 } 182 ctx := sctx.SetGasPrice(r.Context(), headers.GasPrice) 183 184 txHash, err := s.transaction.CancelTransaction(ctx, paths.Hash) 185 if err != nil { 186 logger.Debug("cancel transaction failed", "tx_hash", paths.Hash, "error", err, "canceled_tx_hash", txHash) 187 logger.Error(nil, "cancel transaction failed", "tx_hash", paths.Hash) 188 if errors.Is(err, transaction.ErrUnknownTransaction) { 189 jsonhttp.NotFound(w, errUnknownTransaction) 190 } else if errors.Is(err, transaction.ErrAlreadyImported) { 191 jsonhttp.BadRequest(w, errAlreadyImported) 192 } else { 193 jsonhttp.InternalServerError(w, errCantResendTransaction) 194 } 195 return 196 } 197 198 jsonhttp.OK(w, transactionHashResponse{ 199 TransactionHash: txHash, 200 }) 201 }