erigon-pulse/cmd/rpcdaemon/commands/send_transaction.go
Mark Holt f9acbff96c
eth_sendRawTransaction: remove logging, because we have --txpool.trace.senders which enabling more verbose logging for given senders list (#7686)
When testing with `Bor` consensus turned on I discovered that
`SendRawTransaction` returns a 0x000... hash when transactions are
submitted during block transitions. This turns out to be spurious in the
sense that the transaction insertion is successful.

The cause is that `ReadCurrentBlockNumber` returns a nil block number.

This in turn is caused by the following: In `accessors_chain.go` there
are two methods: `WriteHeader` and `WriteHeadHeaderHash` when the first
is called the block number is written for the header. The second writes
the header has, but there is no guarantee when it does that the head
header will have been written yet. In fact it seems to happen sometime
later.

The problem for `SendRawTransation` is that it begins a transaction
after inserting into the txpool. And depending on timing this
transaction may see only the `WriteHeadHeaderHash` insertion, and hence
can't read the block number.

I have mitigated this by opening the db transaction before calling the
tx pool insertion, meaning that it is more likely to have a clean view
of the DB.

I have also moved the chain id check earlier in the code - as I think
that if this is invalid the method should not try to insert transactions
in the first place.

The `ReadCurrentBlockNumber` is only used to produce a log message - so
I've changed this to not fail the whole function but to just log an
unknown sender. Which means that the hash is still returned to the
sender after a successful txpool insertion
2023-06-11 13:18:04 +07:00

87 lines
2.9 KiB
Go

package commands
import (
"context"
"errors"
"fmt"
"math/big"
"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/hexutility"
txPoolProto "github.com/ledgerwatch/erigon-lib/gointerfaces/txpool"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/eth/ethconfig"
"github.com/ledgerwatch/erigon/params"
)
// SendRawTransaction implements eth_sendRawTransaction. Creates new message call transaction or a contract creation for previously-signed transactions.
func (api *APIImpl) SendRawTransaction(ctx context.Context, encodedTx hexutility.Bytes) (common.Hash, error) {
txn, err := types.DecodeWrappedTransaction(encodedTx)
if err != nil {
return common.Hash{}, err
}
// If the transaction fee cap is already specified, ensure the
// fee of the given transaction is _reasonable_.
if err := checkTxFee(txn.GetPrice().ToBig(), txn.GetGas(), ethconfig.Defaults.RPCTxFeeCap); err != nil {
return common.Hash{}, err
}
if !txn.Protected() {
return common.Hash{}, errors.New("only replay-protected (EIP-155) transactions allowed over RPC")
}
// this has been moved to prior to adding of transactions to capture the
// pre state of the db - which is used for logging in the messages below
tx, err := api.db.BeginRo(ctx)
if err != nil {
return common.Hash{}, err
}
defer tx.Rollback()
cc, err := api.chainConfig(tx)
if err != nil {
return common.Hash{}, err
}
txnChainId := txn.GetChainID()
chainId := cc.ChainID
if chainId.Cmp(txnChainId.ToBig()) != 0 {
return common.Hash{}, fmt.Errorf("invalid chain id, expected: %d got: %d", chainId, *txnChainId)
}
hash := txn.Hash()
res, err := api.txPool.Add(ctx, &txPoolProto.AddRequest{RlpTxs: [][]byte{encodedTx}})
if err != nil {
return common.Hash{}, err
}
if res.Imported[0] != txPoolProto.ImportResult_SUCCESS {
return hash, fmt.Errorf("%s: %s", txPoolProto.ImportResult_name[int32(res.Imported[0])], res.Errors[0])
}
return txn.Hash(), nil
}
// SendTransaction implements eth_sendTransaction. Creates new message call transaction or a contract creation if the data field contains code.
func (api *APIImpl) SendTransaction(_ context.Context, txObject interface{}) (common.Hash, error) {
return common.Hash{0}, fmt.Errorf(NotImplemented, "eth_sendTransaction")
}
// checkTxFee is an internal function used to check whether the fee of
// the given transaction is _reasonable_(under the cap).
func checkTxFee(gasPrice *big.Int, gas uint64, gasCap float64) error {
// Short circuit if there is no gasCap for transaction fee at all.
if gasCap == 0 {
return nil
}
feeEth := new(big.Float).Quo(new(big.Float).SetInt(new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(gas))), new(big.Float).SetInt(big.NewInt(params.Ether)))
feeFloat, _ := feeEth.Float64()
if feeFloat > gasCap {
return fmt.Errorf("tx fee (%.2f ether) exceeds the configured cap (%.2f ether)", feeFloat, gasCap)
}
return nil
}