Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
38c00a1
remove unused var
Stefan-Ethernal Dec 4, 2025
e9834af
backward let event indexing scaffolding
Stefan-Ethernal Dec 6, 2025
003112a
introduce new migration and refactor migrations resolution to be generic
Stefan-Ethernal Dec 8, 2025
c3dbef4
TestBuildAppender increase coverage
Stefan-Ethernal Dec 8, 2025
928cf5f
delete bridges on backward let
Stefan-Ethernal Dec 8, 2025
c72e102
feat: backwards on merkle tree (#1378)
goran-ethernal Dec 8, 2025
aea7e8f
update exit tree on backward LET
Stefan-Ethernal Dec 8, 2025
6ae4adb
formatting
Stefan-Ethernal Dec 9, 2025
b2dccc5
remove bridges whose deposit count is greater than new deposit count
Stefan-Ethernal Dec 9, 2025
aa2cd0c
remove InvalidClaim struct
Stefan-Ethernal Dec 9, 2025
a9c7035
correctly determine leaf index
Stefan-Ethernal Dec 10, 2025
44774fa
archive bridges to bridge_archive table and handle reorgs
Stefan-Ethernal Dec 11, 2025
275aed8
increase code coverage, ignore double insertions to bridge_archive table
Stefan-Ethernal Dec 12, 2025
4d4d220
Merge branch 'develop' into feat/index-backward-let
Stefan-Ethernal Dec 12, 2025
ecc3ded
address 1st round of comments
Stefan-Ethernal Dec 12, 2025
f98e350
reverse the steps, first delete the bridges and then update the exit …
Stefan-Ethernal Dec 12, 2025
a74ca6e
Apply suggestions from code review
Stefan-Ethernal Dec 12, 2025
b2035c2
fix sql syntax error
Stefan-Ethernal Dec 12, 2025
38f1fbe
leaf index is the same as deposit count
Stefan-Ethernal Dec 12, 2025
752f986
invoke BackwardLET string function
Stefan-Ethernal Dec 12, 2025
7c941ab
remove redundant check
Stefan-Ethernal Dec 12, 2025
7ffebfd
fix sql syntax in migration file (2nd part)
Stefan-Ethernal Dec 12, 2025
2b1ce28
reinsert exit tree leaves on reorg of backward let event
Stefan-Ethernal Dec 15, 2025
ba08508
add bridge source column and add ordering by deposit_count
Stefan-Ethernal Dec 15, 2025
e4c8f62
move the new sql migrations to bridgesync0012 migration file
Stefan-Ethernal Dec 15, 2025
3600cd6
formatting
Stefan-Ethernal Dec 15, 2025
dba8654
Merge branch 'develop' into feat/index-backward-let
Stefan-Ethernal Dec 16, 2025
c900f0a
TestProcessor_BackwardLET
Stefan-Ethernal Dec 16, 2025
2e39f39
increase code coverage in common pkg
Stefan-Ethernal Dec 16, 2025
72fa419
TestProcessor_BackwardLET, backward LET on empty bridge table
Stefan-Ethernal Dec 16, 2025
a6ac3d8
add source column to bridge_archive table and copy the value on delet…
Stefan-Ethernal Dec 16, 2025
39c37b3
log the deleted bridges by deposit counts
Stefan-Ethernal Dec 17, 2025
4b8235f
backward let in between of bridges
Stefan-Ethernal Dec 17, 2025
04f3b2e
Merge branch 'develop' into feat/index-backward-let
Stefan-Ethernal Dec 17, 2025
e8dc5e0
fix: lint
Stefan-Ethernal Dec 17, 2025
d24339d
fix unit tests
Stefan-Ethernal Dec 17, 2025
b093169
refactor processing of backward let event
Stefan-Ethernal Dec 17, 2025
3d9814f
remove bridges and leafs from exit tree, and then restore only the su…
Stefan-Ethernal Dec 18, 2025
3eeb0b9
Merge branch 'develop' into feat/index-backward-let
Stefan-Ethernal Dec 18, 2025
60e78cc
address copilot's comments
Stefan-Ethernal Dec 18, 2025
5ffbd31
small optimization
Stefan-Ethernal Dec 18, 2025
c40e7ac
Merge branch 'develop' into feat/index-backward-let
Stefan-Ethernal Dec 18, 2025
a7390c2
drop the bridge archive trigger and do it programatically, tweak test
Stefan-Ethernal Dec 19, 2025
8eae344
Merge branch 'develop' into feat/index-backward-let
Stefan-Ethernal Dec 19, 2025
1f4efd4
Merge branch 'develop' into feat/index-backward-let
Stefan-Ethernal Dec 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions bridgesync/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ var (
setClaimEventSignature = crypto.Keccak256Hash([]byte(
"SetClaim(bytes32)",
))
backwardLETEventSignature = crypto.Keccak256Hash([]byte("BackwardLET(uint256,bytes32,uint256,bytes32)"))

claimAssetEtrogMethodID = common.Hex2Bytes("ccaa2d11")
claimMessageEtrogMethodID = common.Hex2Bytes("f5efcd79")
Expand Down Expand Up @@ -110,6 +111,10 @@ func buildAppender(
appender[removeLegacySovereignTokenEventSignature] = buildRemoveLegacyTokenHandler(bridgeDeployment.agglayerBridgeL2)
appender[unsetClaimEventSignature] = buildUnsetClaimEventHandler(bridgeDeployment.agglayerBridgeL2)
appender[setClaimEventSignature] = buildSetClaimEventHandler(bridgeDeployment.agglayerBridgeL2)
appender[backwardLETEventSignature] = buildBackwardLETEventHandler(bridgeDeployment.agglayerBridgeL2)

default:
return nil, fmt.Errorf("unsupported bridge deployment kind: %d", bridgeDeployment.kind)
}

return appender, nil
Expand Down Expand Up @@ -419,6 +424,26 @@ func buildSetClaimEventHandler(contract *agglayerbridgel2.Agglayerbridgel2) func
}
}

// buildBackwardLETEventHandler creates a handler for the BackwardLET event log
func buildBackwardLETEventHandler(contract *agglayerbridgel2.Agglayerbridgel2) func(*sync.EVMBlock, types.Log) error {
return func(b *sync.EVMBlock, l types.Log) error {
event, err := contract.ParseBackwardLET(l)
if err != nil {
return fmt.Errorf("error parsing BackwardLET event log %+v: %w", l, err)
}

b.Events = append(b.Events, Event{BackwardLET: &BackwardLET{
BlockNum: b.Num,
BlockPos: uint64(l.Index),
PreviousDepositCount: event.PreviousDepositCount,
PreviousRoot: event.PreviousRoot,
NewDepositCount: event.NewDepositCount,
NewRoot: event.NewRoot,
}})
return nil
}
}

type Call struct {
From common.Address `json:"from"`
To common.Address `json:"to"`
Expand Down
53 changes: 45 additions & 8 deletions bridgesync/downloader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func TestBuildAppender(t *testing.T) {
eventSignature common.Hash
deploymentKind BridgeDeployment
logBuilder func() (types.Log, error)
expectedErr string
}{
{
name: "bridgeEventSignature appender",
Expand Down Expand Up @@ -346,6 +347,38 @@ func TestBuildAppender(t *testing.T) {
return l, nil
},
},
{
name: "backwardLETSignature appender",
eventSignature: backwardLETEventSignature,
deploymentKind: SovereignChain,
logBuilder: func() (types.Log, error) {
event, err := bridgeL2Abi.EventByID(backwardLETEventSignature)
if err != nil {
return types.Log{}, err
}

previousDepositCount := big.NewInt(10)
previousRoot := common.HexToHash("0xdeadbeef")
newDepositCount := big.NewInt(8)
newRoot := common.HexToHash("0x5ca1e")
data, err := event.Inputs.Pack(previousDepositCount, previousRoot, newDepositCount, newRoot)
if err != nil {
return types.Log{}, err
}

l := types.Log{
Topics: []common.Hash{backwardLETEventSignature},
Data: data,
}
return l, nil
},
},
{
name: "unknown deployment kind",
deploymentKind: 100,
logBuilder: func() (types.Log, error) { return types.Log{}, nil },
expectedErr: "unsupported bridge deployment kind: 100",
},
}

for _, tt := range tests {
Expand All @@ -356,17 +389,21 @@ func TestBuildAppender(t *testing.T) {
logger := logger.WithFields("module", "test")
bridgeDeployment.kind = tt.deploymentKind
appenderMap, err := buildAppender(ethClient, bridgeAddr, false, bridgeDeployment, logger)
require.NoError(t, err)
require.NotNil(t, appenderMap)
if tt.expectedErr == "" {
require.NoError(t, err)
require.NotNil(t, appenderMap)

block := &sync.EVMBlock{EVMBlockHeader: sync.EVMBlockHeader{Num: blockNum}}
block := &sync.EVMBlock{EVMBlockHeader: sync.EVMBlockHeader{Num: blockNum}}

appenderFunc, exists := appenderMap[tt.eventSignature]
require.True(t, exists)
appenderFunc, exists := appenderMap[tt.eventSignature]
require.True(t, exists)

err = appenderFunc(block, log)
require.NoError(t, err)
require.Len(t, block.Events, 1)
err = appenderFunc(block, log)
require.NoError(t, err)
require.Len(t, block.Events, 1)
} else {
require.ErrorContains(t, err, tt.expectedErr)
}
})
}
}
Expand Down
75 changes: 75 additions & 0 deletions bridgesync/migrations/bridgesync0011.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
-- +migrate Down
DROP TABLE IF EXISTS backward_let;

DROP TRIGGER IF EXISTS archive_bridge_before_delete;

DROP TABLE IF EXISTS bridge_archive;

-- +migrate Up
CREATE TABLE IF NOT EXISTS backward_let (
block_num INTEGER NOT NULL REFERENCES block (num) ON DELETE CASCADE,
block_pos INTEGER NOT NULL,
previous_deposit_count TEXT NOT NULL,
previous_root VARCHAR NOT NULL,
new_deposit_count TEXT NOT NULL,
new_root VARCHAR NOT NULL,
PRIMARY KEY (block_num, block_pos)
);

------------------------------------------------------------------------------
-- Create archive table
------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS bridge_archive (
deposit_count INTEGER PRIMARY KEY,
block_num INTEGER NOT NULL,
block_pos INTEGER NOT NULL,
leaf_type INTEGER NOT NULL,
origin_network INTEGER NOT NULL,
origin_address VARCHAR NOT NULL,
destination_network INTEGER NOT NULL,
destination_address VARCHAR NOT NULL,
amount TEXT NOT NULL,
metadata BLOB,
tx_hash VARCHAR,
block_timestamp INTEGER,
txn_sender VARCHAR
);

------------------------------------------------------------------------------
-- Create BEFORE DELETE trigger: archive only deleted rows
------------------------------------------------------------------------------
CREATE TRIGGER IF NOT EXISTS archive_bridge_before_delete
BEFORE DELETE ON bridge
FOR EACH ROW
BEGIN
INSERT OR IGNORE INTO bridge_archive (
deposit_count,
block_num,
block_pos,
leaf_type,
origin_network,
origin_address,
destination_network,
destination_address,
amount,
metadata,
tx_hash,
block_timestamp,
txn_sender
)
VALUES (
OLD.deposit_count,
OLD.block_num,
OLD.block_pos,
OLD.leaf_type,
OLD.origin_network,
OLD.origin_address,
OLD.destination_network,
OLD.destination_address,
OLD.amount,
OLD.metadata,
OLD.tx_hash,
OLD.block_timestamp,
OLD.txn_sender
);
END;
118 changes: 51 additions & 67 deletions bridgesync/migrations/migrations.go
Original file line number Diff line number Diff line change
@@ -1,86 +1,70 @@
package migrations

import (
_ "embed"
"embed"
"fmt"
"sort"
"strings"

"github.com/agglayer/aggkit/db"
"github.com/agglayer/aggkit/db/types"
treeMigrations "github.com/agglayer/aggkit/tree/migrations"
treemigrations "github.com/agglayer/aggkit/tree/migrations"
)

//go:embed bridgesync0001.sql
var mig0001 string
var (
//go:embed *.sql
migrationFS embed.FS
migrations []types.Migration
)

//go:embed bridgesync0002.sql
var mig0002 string
func init() {
entries, err := migrationFS.ReadDir(".")
if err != nil {
panic(fmt.Errorf("failed to read embedded migrations: %w", err))
}

//go:embed bridgesync0003.sql
var mig0003 string
for _, e := range entries {
name := e.Name() // e.g. "bridgesync0004.sql"

//go:embed bridgesync0004.sql
var mig0004 string
sqlBytes, err := migrationFS.ReadFile(name)
if err != nil {
panic(err)
}

//go:embed bridgesync0005.sql
var mig0005 string
id := strings.TrimSuffix(name, ".sql") // "bridgesync0004"

//go:embed bridgesync0006.sql
var mig0006 string
migrations = append(migrations, types.Migration{
ID: id,
SQL: string(sqlBytes),
})
}

//go:embed bridgesync0007.sql
var mig0007 string
// Ensure deterministic canonical order
sort.Slice(migrations, func(i, j int) bool {
return migrations[i].ID < migrations[j].ID
})
}

//go:embed bridgesync0008.sql
var mig0008 string
func RunMigrations(dbPath string) error {
// Pre-calculate total length
total := len(migrations) + len(treemigrations.Migrations)

//go:embed bridgesync0009.sql
var mig0009 string
combined := make([]types.Migration, 0, total)
// Copy migrations
combined = append(combined, migrations...)
combined = append(combined, treemigrations.Migrations...)

//go:embed bridgesync0010.sql
var mig0010 string
// Pass the copy to db.RunMigrations
return db.RunMigrations(dbPath, combined)
}

func RunMigrations(dbPath string) error {
migrations := []types.Migration{
{
ID: "bridgesync0001",
SQL: mig0001,
},
{
ID: "bridgesync0002",
SQL: mig0002,
},
{
ID: "bridgesync0003",
SQL: mig0003,
},
{
ID: "bridgesync0004",
SQL: mig0004,
},
{
ID: "bridgesync0005",
SQL: mig0005,
},
{
ID: "bridgesync0006",
SQL: mig0006,
},
{
ID: "bridgesync0007",
SQL: mig0007,
},
{
ID: "bridgesync0008",
SQL: mig0008,
},
{
ID: "bridgesync0009",
SQL: mig0009,
},
{
ID: "bridgesync0010",
SQL: mig0010,
},
}
migrations = append(migrations, treeMigrations.Migrations...)
return db.RunMigrations(dbPath, migrations)
// GetUpTo returns all migrations up to and including the migration with the given ID.
func GetUpTo(lastID string) []types.Migration {
idx := sort.Search(len(migrations), func(i int) bool {
return migrations[i].ID > lastID
})

out := make([]types.Migration, idx)
copy(out, migrations[:idx])
return out
}
Loading
Loading