@@ -2,11 +2,13 @@ package app
22
33import (
44 "bytes"
5+ "context"
56 "errors"
67 "fmt"
78 "runtime"
89 "time"
910
11+ "cosmossdk.io/log"
1012 abci "github.com/cometbft/cometbft/abci/types"
1113 cmtTypes "github.com/cometbft/cometbft/types"
1214 "github.com/cosmos/cosmos-sdk/codec/address"
@@ -15,6 +17,7 @@ import (
1517 "github.com/cosmos/gogoproto/jsonpb"
1618 "github.com/cosmos/gogoproto/proto"
1719 "github.com/ethereum/go-ethereum/common"
20+ "github.com/ethereum/go-ethereum/common/hexutil"
1821
1922 "github.com/0xPolygon/heimdall-v2/common/strutil"
2023 "github.com/0xPolygon/heimdall-v2/helper"
@@ -23,9 +26,11 @@ import (
2326 heimdallTypes "github.com/0xPolygon/heimdall-v2/types"
2427 borTypes "github.com/0xPolygon/heimdall-v2/x/bor/types"
2528 checkpointTypes "github.com/0xPolygon/heimdall-v2/x/checkpoint/types"
29+ clerkTypes "github.com/0xPolygon/heimdall-v2/x/clerk/types"
2630 milestoneAbci "github.com/0xPolygon/heimdall-v2/x/milestone/abci"
2731 milestoneTypes "github.com/0xPolygon/heimdall-v2/x/milestone/types"
2832 stakeTypes "github.com/0xPolygon/heimdall-v2/x/stake/types"
33+ topupTypes "github.com/0xPolygon/heimdall-v2/x/topup/types"
2934)
3035
3136// prepareProposalBudget caps total handler wall-clock measured from handler
@@ -265,10 +270,16 @@ func (app *HeimdallApp) NewProcessProposalHandler() sdk.ProcessProposalHandler {
265270func (app * HeimdallApp ) ExtendVoteHandler () sdk.ExtendVoteHandler {
266271 return func (ctx sdk.Context , req * abci.RequestExtendVote ) (* abci.ResponseExtendVote , error ) {
267272 startTime := time .Now ()
268- defer metrics .RecordABCIHandlerDuration (metrics .ExtendVoteDuration , startTime )
269273
270274 logger := app .Logger ()
271275 logger .Debug ("Extending Vote" , "height" , ctx .BlockHeight ())
276+
277+ var extendVoteCaller * helper.ContractCaller
278+ if caller , ok := app .caller .(* helper.ContractCaller ); ok {
279+ extendVoteCaller = caller
280+ extendVoteCaller .BeginPrefetchRound ()
281+ }
282+
272283 defer func () {
273284 // better debugging with this panic recover routine printing runtime.Stack
274285 if r := recover (); r != nil {
@@ -283,6 +294,13 @@ func (app *HeimdallApp) ExtendVoteHandler() sdk.ExtendVoteHandler {
283294 }
284295 }()
285296
297+ defer func () {
298+ if extendVoteCaller != nil {
299+ extendVoteCaller .EndPrefetchRound ()
300+ }
301+ metrics .RecordABCIHandlerDuration (metrics .ExtendVoteDuration , startTime )
302+ }()
303+
286304 // check if VEs are enabled
287305 if err := checkIfVoteExtensionsDisabled (ctx , req .Height ); err != nil {
288306 return nil , err
@@ -312,6 +330,9 @@ func (app *HeimdallApp) ExtendVoteHandler() sdk.ExtendVoteHandler {
312330
313331 txs := req .Txs [1 :]
314332
333+ // Prefetch L1 receipts in a single batch RPC call to warm the cache.
334+ app .prefetchReceipts (ctx , txs , logger )
335+
315336 // decode txs and execute side txs
316337 for _ , rawTx := range txs {
317338 // create a cache wrapped context for stateless execution
@@ -772,12 +793,10 @@ func (app *HeimdallApp) PreBlocker(ctx sdk.Context, req *abci.RequestFinalizeBlo
772793 }
773794
774795 var txBytes cmtTypes.Tx = rawTx
775-
776796 txHash := common .Bytes2Hex (txBytes .Hash ())
777797
778798 if approvedTxsMap [txHash ] {
779-
780- // execute post-handler for the approved side tx
799+ // execute post-handlers for the approved side txs
781800 msgs := decodedTx .GetMsgs ()
782801 executedPostHandlers := 0
783802 for _ , msg := range msgs {
@@ -1042,3 +1061,107 @@ func rejectUnknownVoteExtFields(bz []byte) error {
10421061
10431062 return nil
10441063}
1064+
1065+ // prefetchReceiptsTimeout is half of timeout_precommit (1s).
1066+ const prefetchReceiptsTimeout = 500 * time .Millisecond
1067+
1068+ // prefetchReceipts decodes txs, extracts L1 tx hashes from side-tx messages,
1069+ // and batch-fetches their receipts into the cache.
1070+ func (app * HeimdallApp ) prefetchReceipts (ctx sdk.Context , txs [][]byte , logger log.Logger ) {
1071+ seen := make (map [common.Hash ]struct {})
1072+ var hashes []common.Hash
1073+ sideTxCount := 0
1074+
1075+ for _ , rawTx := range txs {
1076+ tx , err := app .TxDecode (rawTx )
1077+ if err != nil {
1078+ continue
1079+ }
1080+
1081+ for _ , msg := range tx .GetMsgs () {
1082+ if app .sideTxCfg .GetSideHandler (msg ) == nil {
1083+ continue
1084+ }
1085+
1086+ sideTxCount ++
1087+
1088+ h , ok := extractTxHash (msg )
1089+ if ! ok {
1090+ continue
1091+ }
1092+
1093+ if _ , dup := seen [h ]; ! dup {
1094+ seen [h ] = struct {}{}
1095+ hashes = append (hashes , h )
1096+ }
1097+
1098+ if sideTxCount >= maxSideTxResponsesCount {
1099+ break
1100+ }
1101+ }
1102+
1103+ if sideTxCount >= maxSideTxResponsesCount {
1104+ break
1105+ }
1106+ }
1107+
1108+ if len (hashes ) == 0 {
1109+ logger .Debug ("No L1 tx hashes to prefetch" )
1110+ return
1111+ }
1112+
1113+ prefetchCtx , cancel := context .WithTimeout (ctx .Context (), prefetchReceiptsTimeout )
1114+ defer cancel ()
1115+
1116+ helper .PrefetchReceipts (prefetchCtx , app .caller , hashes , logger )
1117+
1118+ if prefetchCtx .Err () == context .DeadlineExceeded {
1119+ logger .Error ("Receipt prefetch timed out" )
1120+ }
1121+ }
1122+
1123+ func extractTxHash (msg sdk.Msg ) (common.Hash , bool ) {
1124+ switch m := msg .(type ) {
1125+ case * clerkTypes.MsgEventRecord :
1126+ if ! verifyHexTxHash (m .TxHash ) {
1127+ return common.Hash {}, false
1128+ }
1129+ return common .HexToHash (m .TxHash ), true
1130+ case * stakeTypes.MsgValidatorJoin :
1131+ if ! verifyTxHash (m .TxHash ) {
1132+ return common.Hash {}, false
1133+ }
1134+ return common .BytesToHash (m .TxHash ), true
1135+ case * stakeTypes.MsgStakeUpdate :
1136+ if ! verifyTxHash (m .TxHash ) {
1137+ return common.Hash {}, false
1138+ }
1139+ return common .BytesToHash (m .TxHash ), true
1140+ case * stakeTypes.MsgSignerUpdate :
1141+ if ! verifyTxHash (m .TxHash ) {
1142+ return common.Hash {}, false
1143+ }
1144+ return common .BytesToHash (m .TxHash ), true
1145+ case * stakeTypes.MsgValidatorExit :
1146+ if ! verifyTxHash (m .TxHash ) {
1147+ return common.Hash {}, false
1148+ }
1149+ return common .BytesToHash (m .TxHash ), true
1150+ case * topupTypes.MsgTopupTx :
1151+ if ! verifyTxHash (m .TxHash ) {
1152+ return common.Hash {}, false
1153+ }
1154+ return common .BytesToHash (m .TxHash ), true
1155+ default :
1156+ return common.Hash {}, false
1157+ }
1158+ }
1159+
1160+ func verifyTxHash (txHash []byte ) bool {
1161+ return len (txHash ) == common .HashLength
1162+ }
1163+
1164+ func verifyHexTxHash (hexTxHash string ) bool {
1165+ decodedHash , err := hexutil .Decode (hexTxHash )
1166+ return err == nil && len (decodedHash ) == common .HashLength
1167+ }
0 commit comments