move to event driven

This commit is contained in:
aodhgan 2025-10-01 22:41:01 -07:00
parent 0684a33a50
commit e7a48a5c08
2 changed files with 43 additions and 18 deletions

View file

@ -41,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/gasestimator" "github.com/ethereum/go-ethereum/eth/gasestimator"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi/override" "github.com/ethereum/go-ethereum/internal/ethapi/override"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
@ -1653,7 +1654,7 @@ func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil
} }
// SendRawTransactionSync will add the signed transaction to the transaction pool // 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. // and wait until the transaction has been included in a block and return the receipt, or the timeout.
func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hexutil.Bytes, timeoutMs *hexutil.Uint64) (map[string]interface{}, error) { func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hexutil.Bytes, timeoutMs *hexutil.Uint64) (map[string]interface{}, error) {
tx := new(types.Transaction) tx := new(types.Transaction)
if err := tx.UnmarshalBinary(input); err != nil { if err := tx.UnmarshalBinary(input); err != nil {
@ -1664,7 +1665,7 @@ func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hex
return nil, err return nil, err
} }
// compute effective timeout // effective timeout: min(requested, max), default if none
max := api.b.RPCTxSyncMaxTimeout() max := api.b.RPCTxSyncMaxTimeout()
def := api.b.RPCTxSyncDefaultTimeout() def := api.b.RPCTxSyncDefaultTimeout()
@ -1674,40 +1675,63 @@ func (api *TransactionAPI) SendRawTransactionSync(ctx context.Context, input hex
if req > max { if req > max {
eff = max eff = max
} else { } else {
eff = req // allow shorter than default eff = req
} }
} }
// Wait for receipt until timeout
receiptCtx, cancel := context.WithTimeout(ctx, eff) receiptCtx, cancel := context.WithTimeout(ctx, eff)
defer cancel() defer cancel()
ticker := time.NewTicker(time.Millisecond * 100) // Fast path: maybe already mined/indexed
defer ticker.Stop() if r, err := api.GetTransactionReceipt(receiptCtx, hash); err == nil && r != nil {
return r, nil
}
// Subscribe to head changes and re-check on each new block
heads := make(chan core.ChainHeadEvent, 16)
var sub event.Subscription = api.b.SubscribeChainHeadEvent(heads)
if sub == nil {
return nil, errors.New("chain head subscription unavailable")
}
defer sub.Unsubscribe()
for { for {
select { 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(): case <-receiptCtx.Done():
// Distinguish upstream cancellation from our timeout
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return nil, err // context canceled / deadline exceeded upstream return nil, err
} }
return nil, &txSyncTimeoutError{ return nil, &txSyncTimeoutError{
msg: fmt.Sprintf("The transaction was added to the transaction pool but wasn't processed in %v.", eff), msg: fmt.Sprintf("The transaction was added to the transaction pool but wasn't processed in %v.", eff),
hash: hash, hash: hash,
} }
case err := <-sub.Err():
return nil, err
case <-heads:
receipt, getErr := api.GetTransactionReceipt(receiptCtx, hash)
// If tx-index still building, keep waiting; ignore transient errors
if getErr != nil && !errors.Is(getErr, NewTxIndexingError()) {
// ignore and wait for next head
continue
}
if receipt != nil {
return receipt, nil
}
} }
} }
} }
func (api *TransactionAPI) tryGetReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
receipt, err := api.GetTransactionReceipt(ctx, hash)
if err != nil {
return nil, err
}
return receipt, nil
}
// Sign calculates an ECDSA signature for: // Sign calculates an ECDSA signature for:
// keccak256("\x19Ethereum Signed Message:\n" + len(message) + message). // keccak256("\x19Ethereum Signed Message:\n" + len(message) + message).
// //

View file

@ -448,6 +448,7 @@ type testBackend struct {
syncMaxTO time.Duration syncMaxTO time.Duration
sentTx *types.Transaction sentTx *types.Transaction
sentTxHash common.Hash sentTxHash common.Hash
headFeed *event.Feed
} }
func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.Engine, generator func(i int, b *core.BlockGen)) *testBackend { func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.Engine, generator func(i int, b *core.BlockGen)) *testBackend {
@ -474,6 +475,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.E
acc: acc, acc: acc,
pending: blocks[n], pending: blocks[n],
pendingReceipts: receipts[n], pendingReceipts: receipts[n],
headFeed: new(event.Feed),
} }
return backend return backend
} }
@ -598,7 +600,7 @@ func (b testBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscr
panic("implement me") panic("implement me")
} }
func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
panic("implement me") return b.headFeed.Subscribe(ch)
} }
func (b *testBackend) SendTx(ctx context.Context, tx *types.Transaction) error { func (b *testBackend) SendTx(ctx context.Context, tx *types.Transaction) error {
b.sentTx = tx b.sentTx = tx
@ -3959,7 +3961,6 @@ func makeSignedRaw(t *testing.T, api *TransactionAPI, from, to common.Address, v
func makeSelfSignedRaw(t *testing.T, api *TransactionAPI, addr common.Address) (hexutil.Bytes, *types.Transaction) { func makeSelfSignedRaw(t *testing.T, api *TransactionAPI, addr common.Address) (hexutil.Bytes, *types.Transaction) {
return makeSignedRaw(t, api, addr, addr, big.NewInt(0)) return makeSignedRaw(t, api, addr, addr, big.NewInt(0))
} }
func TestSendRawTransactionSync_Success(t *testing.T) { func TestSendRawTransactionSync_Success(t *testing.T) {
t.Parallel() t.Parallel()
genesis := &core.Genesis{ genesis := &core.Genesis{