feat: initial api with default timeouts

This commit is contained in:
aodhgan 2025-10-01 16:43:46 -07:00
parent 11208553dd
commit ca42c98c60
4 changed files with 79 additions and 0 deletions

View file

@ -486,3 +486,11 @@ func (b *EthAPIBackend) StateAtBlock(ctx context.Context, block *types.Block, re
func (b *EthAPIBackend) StateAtTransaction(ctx context.Context, block *types.Block, txIndex int, reexec uint64) (*types.Transaction, vm.BlockContext, *state.StateDB, tracers.StateReleaseFunc, error) {
return b.eth.stateAtTransaction(ctx, block, txIndex, reexec)
}
func (b *EthAPIBackend) RPCTxSyncDefaultTimeout() time.Duration {
return 2 * time.Second
}
func (b *EthAPIBackend) RPCTxSyncMaxTimeout() time.Duration {
return 5 * time.Minute
}

View file

@ -1652,6 +1652,62 @@ func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil
return SubmitTransaction(ctx, api.b, tx)
}
// SendRawTransactionSync will add the signed transaction to the transaction pool
// and wait for the transaction to be mined until the timeout (in milliseconds) is reached.
func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hexutil.Bytes, timeoutMs *hexutil.Uint64) (map[string]interface{}, error) {
tx := new(types.Transaction)
if err := tx.UnmarshalBinary(input); err != nil {
return nil, err
}
hash, err := SubmitTransaction(ctx, api.b, tx)
if err != nil {
return nil, err
}
// compute effective timeout
max := api.b.RPCTxSyncMaxTimeout()
def := api.b.RPCTxSyncDefaultTimeout()
eff := def
if timeoutMs != nil && *timeoutMs > 0 {
req := time.Duration(*timeoutMs) * time.Millisecond
if req > max {
eff = max
} else {
eff = req // allow shorter than default
}
}
// Wait for receipt until timeout
receiptCtx, cancel := context.WithTimeout(ctx, eff)
defer cancel()
ticker := time.NewTicker(time.Millisecond * 100)
defer ticker.Stop()
for {
select {
case <-ticker.C:
receipt, getErr := api.GetTransactionReceipt(receiptCtx, hash)
// If tx-index still building, keep polling
if getErr != nil && !errors.Is(getErr, NewTxIndexingError()) {
// transient or other error: just keep polling
}
if receipt != nil {
return receipt, nil
}
case <-receiptCtx.Done():
if err := ctx.Err(); err != nil {
return nil, err // context canceled / deadline exceeded upstream
}
return nil, &txSyncTimeoutError{
msg: fmt.Sprintf("The transaction was added to the transaction pool but wasn't processed in %v.", eff),
hash: hash,
}
}
}
}
// Sign calculates an ECDSA signature for:
// keccak256("\x19Ethereum Signed Message:\n" + len(message) + message).
//

View file

@ -53,6 +53,8 @@ type Backend interface {
RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
UnprotectedAllowed() bool // allows only for EIP155 transactions.
RPCTxSyncDefaultTimeout() time.Duration
RPCTxSyncMaxTimeout() time.Duration
// Blockchain API
SetHead(number uint64)

View file

@ -21,6 +21,7 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
@ -33,6 +34,11 @@ type revertError struct {
reason string // revert reason hex encoded
}
type txSyncTimeoutError struct {
msg string
hash common.Hash
}
// ErrorCode returns the JSON error code for a revert.
// See: https://ethereum.org/en/developers/docs/apis/json-rpc/#error-codes
func (e *revertError) ErrorCode() int {
@ -108,6 +114,7 @@ const (
errCodeInvalidParams = -32602
errCodeReverted = -32000
errCodeVMError = -32015
errCodeTxSyncTimeout = 4
)
func txValidationError(err error) *invalidTxError {
@ -168,3 +175,9 @@ type blockGasLimitReachedError struct{ message string }
func (e *blockGasLimitReachedError) Error() string { return e.message }
func (e *blockGasLimitReachedError) ErrorCode() int { return errCodeBlockGasLimitReached }
func (e *txSyncTimeoutError) Error() string { return e.msg }
func (e *txSyncTimeoutError) ErrorCode() int { return errCodeTxSyncTimeout }
// ErrorData should be JSON-safe; return the 0x-hex string.
func (e *txSyncTimeoutError) ErrorData() interface{} { return e.hash.Hex() }