Skip to content

Commit 598beb9

Browse files
bridge: improvements (#575)
* fix: bridge improvements * chore: tests * chore: nits * chore: fix lint * chore: fixes * chore: changes * chore: address comments * chore: address comments * chore: nit * chore: adress comments * fix: split FilterLogs query in case of an err
1 parent a2bf419 commit 598beb9

14 files changed

Lines changed: 947 additions & 181 deletions

app/abci.go

Lines changed: 127 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ package app
22

33
import (
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 {
265270
func (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+
}

app/abci_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6369,3 +6369,43 @@ func createVoteExtensionsWithPartialSupport(t *testing.T, validators []*stakeTyp
63696369

63706370
return voteExtensions
63716371
}
6372+
6373+
func TestExtractTxHashMsgEventRecordValidation(t *testing.T) {
6374+
validHash := common.BigToHash(common.Big1)
6375+
6376+
testCases := []struct {
6377+
name string
6378+
txHash string
6379+
ok bool
6380+
hash common.Hash
6381+
}{
6382+
{
6383+
name: "valid 0x-prefixed 32-byte hash",
6384+
txHash: validHash.Hex(),
6385+
ok: true,
6386+
hash: validHash,
6387+
},
6388+
{
6389+
name: "rejects non-prefixed hash string",
6390+
txHash: common.Bytes2Hex(validHash.Bytes()),
6391+
ok: false,
6392+
hash: common.Hash{},
6393+
},
6394+
{
6395+
name: "rejects malformed short hash",
6396+
txHash: "0x1234",
6397+
ok: false,
6398+
hash: common.Hash{},
6399+
},
6400+
}
6401+
6402+
for _, tc := range testCases {
6403+
tc := tc
6404+
t.Run(tc.name, func(t *testing.T) {
6405+
msg := &clerkTypes.MsgEventRecord{TxHash: tc.txHash}
6406+
hash, ok := extractTxHash(msg)
6407+
require.Equal(t, tc.ok, ok)
6408+
require.Equal(t, tc.hash, hash)
6409+
})
6410+
}
6411+
}

bridge/listener/heimdall.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,8 @@ func (hl *HeimdallListener) Stop() {
6161
hl.Logger.Info("HeimdallListener: stopped")
6262
}
6363

64-
// ProcessHeader -
65-
func (hl *HeimdallListener) ProcessHeader(_ *blockHeader) {
66-
}
64+
// ProcessHeader - no-op function for HeimdallListener
65+
func (hl *HeimdallListener) ProcessHeader(_ *blockHeader) {}
6766

6867
// StartPolling - starts polling for heimdall events
6968
func (hl *HeimdallListener) StartPolling(ctx context.Context, pollInterval time.Duration, _ *big.Int) {

0 commit comments

Comments
 (0)