remove_ger

package
v0.10.0-rc1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 20, 2026 License: Apache-2.0, MIT Imports: 37 Imported by: 0

README

remove-ger

Diagnose and recover from invalid Global Exit Root (GER) injection on L2.

Overview

What it does: The remove-ger CLI validates a GER on L1 and L2, finds claims on L2 that used that GER, and classifies each claim. After an optional interactive confirmation, it runs the recovery steps (freeze bridge, remove GER, unset/set claims or force-emit corrected events as needed, restore bridge).

When to use it: Use after you have detected an invalid GER—for example via aggsender or l2gersync error logs. For how to detect invalid GERs and manual recovery context, see the Remove GER runbook. This tool automates the procedures described there.

If you do not yet know which GER was used by the bad claims, use scan-invalid-claims first to discover invalid GERs directly from L2 claim logs.

Building

From the repository root:

  • Single binary:
    go build -o remove_ger ./tools/remove_ger/cmd
  • All tools (Makefile):
    make build-tools — builds the remove_ger tool (and other tools) into $(GOBIN)/remove_ger.

Config file

The tool uses the same config file(s) as the main aggkit binary: standard aggkit-config.toml format, loaded via the --cfg / -c flag. The only change versus the main config is that you must add a [RemoveGER] section.

Additional section: [RemoveGER]
Field Type Description
BridgeServiceURL string Bridge service REST API base URL (required). Used for querying claims and bridges. The tool runs a health check at startup and will fail if the service is unreachable.
L2NetworkID uint32 L2 network ID served by the bridge service (required for diagnose/recover). Used when querying claims and bridges for the target L2. Set this to the same network ID that the bridge service uses for your L2.
SovereignAdminKey section Signing key with sovereign admin privileges (activate/deactivate emergency state, remove GER, unset/set claims, force-emit claim events). Supports local keystore, AWS KMS, and GCP KMS. See sub-fields below.

SovereignAdminKey sub-fields (depends on Method):

Field Type Description
Method string Signing method: "local" (keystore file), "AWS" (AWS KMS), "GCP" (GCP KMS).
Path string Path to the keystore file ("local" only).
Password string Password to decrypt the keystore ("local" only).

Example config addition

Append the following to your existing aggkit-config.toml (adjust paths and URL to your environment):

[RemoveGER]
BridgeServiceURL = "http://localhost:8080"
L2NetworkID = 12
SovereignAdminKey = { Method = "local", Path = "/path/to/sovereign_admin_keystore.json", Password = "your-keystore-password" }

Commands

The tool has three modes: the default diagnose & recover command, the scan-invalid-claims subcommand for discovery, and the generate subcommand for testing.

Diagnose & recover (default)

Interactive (diagnose, then confirm before recovery):

./remove_ger --cfg aggkit-config.toml --ger 0x0123...64_hex_chars

Non-interactive (for automation):

./remove_ger --cfg aggkit-config.toml --ger 0x0123...64_hex_chars --yes

You can pass multiple config files; later files override earlier ones (e.g. --cfg base.toml --cfg overrides.toml).

The [RemoveGER] section for the default diagnose/recover command must include both:

  • RemoveGER.BridgeServiceURL
  • RemoveGER.L2NetworkID
CLI flags
Flag Short Required Description
--cfg -c Yes Configuration file(s), same format as aggkit-config.toml.
--ger Yes Invalid GER hash to diagnose and remove (hex, 0x-prefixed, 32 bytes / 64 hex chars).
--yes No Skip interactive confirmation and run recovery immediately.
--force No Continue even if the GER exists on L1 (still diagnose and remove).
scan-invalid-claims

Scan L2 claim logs directly from the L2 RPC, starting at a given block, and check whether the GER used by each claim exists on L1. The command prints the list of GERs that were used in invalid claims.

./remove_ger scan-invalid-claims --cfg aggkit-config.toml --from-block 123456

The command scans claim logs from BridgeL2Sync.BridgeAddr on L2, validates each claim GER against L2GERSync.GlobalExitRootL1Addr on L1, and groups the invalid claims by GER.

scan-invalid-claims flags
Flag Required Default Description
--cfg Yes Configuration file(s), same format as aggkit-config.toml.
--from-block Yes Starting L2 block number to scan (inclusive).
--to-block No latest Ending L2 block number to scan (inclusive).
--chunk-size No 5000 Maximum L2 block range per eth_getLogs query.
Config requirements for scan-invalid-claims

The scan command reads a subset of the aggkit config:

  • L1NetworkConfig.RPC.URL — L1 RPC endpoint.
  • Common.L2RPC.URL — L2 RPC endpoint.
  • BridgeL2Sync.BridgeAddr — L2 bridge contract address.
  • L2GERSync.GlobalExitRootL1Addr — L1 GER manager contract address.

The [RemoveGER] section is not required for scan-invalid-claims.

generate

Generate a deterministic invalid GER scenario and print ready-to-run cast commands for injecting a fake GER and a fake claim into L2. This is intended for E2E testing of the recovery tool.

The command builds a single-leaf merkle tree from the given parameters, derives the GER, and outputs two cast send commands:

  1. Insert fake GER — calls insertGlobalExitRoot(bytes32) on the L2 GER manager contract (requires the aggoracle private key).
  2. Claim with fake proof — calls claimAsset(...) on the L2 bridge contract with the generated merkle proofs (requires any funded L2 private key).
./remove_ger generate --cfg aggkit-config.toml --network-id 1

The output is deterministic: the same parameters always produce the same GER hash and proofs.

generate flags
Flag Required Default Description
--cfg Yes Configuration file(s), same format as the default command.
--network-id Yes Destination network ID (must be > 0).
--dest-addr No 0x0000...0000 Destination address for the bridge leaf.
--origin-network No 0 Origin network ID.
--origin-addr No 0x0000...0000 Origin token address.
--amount No 1 Bridge amount in wei.
--deposit-count No 42069 Deposit count for the fake bridge leaf.
--leaf-type No 0 Leaf type (0 = asset, 1 = message).
Config requirements for generate

The generate command reads a subset of the aggkit config:

  • Common.L2RPC.URL — L2 RPC endpoint.
  • BridgeL2Sync.BridgeAddr — L2 bridge contract address.
  • L2GERSync.GlobalExitRootL2Addr — L2 GER manager contract address.

The [RemoveGER] section is not required for the generate command.

generate example
# Generate scenario with custom parameters
./remove_ger generate --cfg aggkit-config.toml \
  --network-id 1 \
  --deposit-count 100 \
  --amount 1000000000000000000

# Pipe the output cast commands into a shell (test environments only!)
./remove_ger generate --cfg aggkit-config.toml --network-id 1 2>&1 | bash

Scenarios

The tool classifies the situation and runs the matching recovery flow:

  • No claims — The GER exists on L2 but no claims use it.
    Steps: Freeze bridge → remove GER → restore bridge.

  • Category A (under-collateralization) — Claim(s) reference a bridge that does not exist on L1 or has different content.
    Steps: Freeze bridge → remove GER → unset those claims → restore bridge.

  • Category B.1 (GER mismatch, same index) — Claim(s) have correct bridge data but wrong GER.
    Steps: Freeze bridge → remove GER → force-emit corrected claim event(s) → restore bridge.

  • Category B.2 (GER and index mismatch) — Claim(s) have wrong GER and wrong index; the correct bridge exists on L1 at a different deposit count.
    Steps: Freeze bridge → remove GER → unset wrong claims → set correct claims (correct global indexes) → force-emit corrected claim events → restore bridge.

Troubleshooting

  • Wrong private key / keystore: Ensure SovereignAdminKey points to a key that has sovereign admin roles on the L2 contracts (emergency bridge pause/unpause, GER removal, unset/set claims, force emit). If recovery transactions fail with auth errors, verify the key’s roles on the L2 bridge and GER manager contracts.

  • Bridge service not reachable: If BridgeServiceURL is set, the tool runs a health check at startup. Connection or HTTP errors will cause an immediate exit. Check the URL, network access, and that the bridge service is running.

  • GER actually exists on L1: By default the tool exits when the GER is found on L1 (treated as valid). Use --force only when you intentionally want to diagnose and remove a GER that exists on L1 (e.g. operational override).

Documentation

Index

Constants

View Source
const DefaultDepositCount = uint32(42069)

DefaultDepositCount is the default deposit count for generated invalid GER scenarios. Uses a high value (42069) to avoid collisions with real deposits.

Variables

This section is empty.

Functions

func ExecuteRecovery

func ExecuteRecovery(ctx context.Context, cfg *Config, env *Env, diagnosis *DiagnosisResult) error

ExecuteRecovery runs the recovery flow for the given diagnosis. All steps execute on L2. On any error, returns immediately; the bridge may remain in emergency state for manual intervention.

func GetClaimsByGER

func GetClaimsByGER(
	ctx context.Context, bridgeService *client.Client, networkID uint32, gerHash common.Hash,
) ([]*claimsynctypes.Claim, error)

GetClaimsByGER queries the bridge service for DetailedClaimEvent claims that used the given GER. networkID specifies which network to query (0 for L1, L2 network ID otherwise). Exported so E2E tests can use the same query for wait and assertion as the tool.

func PrintDiagnosis

func PrintDiagnosis(result *DiagnosisResult)

PrintDiagnosis prints a human-readable diagnosis summary and recovery plan to stdout.

func Run

func Run(c *cli.Context) error

Run is the main entry point for the remove-ger CLI.

func RunGenerate

func RunGenerate(c *cli.Context) error

RunGenerate is the CLI action for the "generate" subcommand.

func RunScanInvalidClaims

func RunScanInvalidClaims(c *cli.Context) error

RunScanInvalidClaims scans L2 claim logs, validates the GER used by each claim on L1, and prints the invalid GERs that were used in claims.

func ScanInvalidClaims

func ScanInvalidClaims(
	ctx context.Context,
	env *Env,
	params ScanInvalidClaimsParams,
) ([]invalidGERUsage, int, uint64, error)

ScanInvalidClaims scans the L2 bridge logs for claims in the given block range, validates their GER on L1, and returns the invalid GERs used by those claims.

Types

type BridgeData

type BridgeData struct {
	LeafType           uint8
	OriginNetwork      uint32
	OriginAddress      common.Address
	DestinationNetwork uint32
	DestinationAddress common.Address
	Amount             *big.Int
	Metadata           []byte
	DepositCount       uint32
}

BridgeData holds L1 bridge fields needed for comparison and for CorrectBridge (B.1/B.2).

type ClaimDiagnosis

type ClaimDiagnosis struct {
	GlobalIndex   *big.Int
	DepositCount  uint32
	OriginNetwork uint32
	Category      Scenario
	CorrectBridge *BridgeData // nil for Category A
}

ClaimDiagnosis holds the classification for a single claim.

type Config

type Config struct {
	// L1NetworkConfig contains the L1 RPC URL and contract addresses.
	L1NetworkConfig ethermanconfig.L1NetworkConfig `mapstructure:"L1NetworkConfig"`

	// Common contains shared settings such as the L2 RPC URL.
	Common ethermanconfig.CommonConfig `mapstructure:"Common"`

	// BridgeL2Sync contains the L2 bridge contract address used to initialize the binding.
	BridgeL2Sync bridgesync.Config `mapstructure:"BridgeL2Sync"`

	// L2GERSync contains the L2/L1 GER contract addresses.
	L2GERSync l2gersync.Config `mapstructure:"L2GERSync"`

	RemoveGER RemoveGERConfig `mapstructure:"RemoveGER"`
}

Config holds the subset of aggkit configuration fields needed by the remove-GER tool, plus tool-specific settings in the RemoveGER section.

func LoadConfig

func LoadConfig(c *cli.Context) (*Config, error)

LoadConfig reads the TOML config file(s) specified by --cfg and unmarshals the fields required by the remove-GER tool. Uses the same template rendering pipeline as the main aggkit binary so that template variables (e.g. L1URL → L1NetworkConfig.RPC.URL) are resolved correctly.

type DiagnosisResult

type DiagnosisResult struct {
	InvalidGER     common.Hash
	GERExistsOnL1  bool
	GERExistsOnL2  bool
	GERTimestampL2 *big.Int
	Claims         []ClaimDiagnosis
	Scenario       Scenario
}

DiagnosisResult holds the result of the diagnosis phase.

func Diagnose

func Diagnose(ctx context.Context, env *Env, gerHash common.Hash, force bool) (*DiagnosisResult, error)

Diagnose runs the diagnosis phase: validate GER on L1/L2, find claims by GER, classify each claim. If GER exists on L1 and force is false, returns GERExistsOnL1Error.

type Env

type Env struct {
	// RPC clients
	L1 *ethclient.Client
	L2 *ethclient.Client

	// Bridge service REST client (required)
	BridgeService *client.Client

	// L2NetworkID is the network ID of the L2 network served by the bridge service.
	L2NetworkID uint32

	// L1 contract bindings
	L1GERManager *agglayerger.Agglayerger

	// L2 contract bindings
	L2Bridge     l2BridgeContract
	L2GERManager *agglayergerl2.Agglayergerl2
	L2BridgeAddr common.Address
	// contains filtered or unexported fields
}

Env holds all connections and contract bindings needed by the remove-ger tool. Pass it to diagnosis and recovery methods in later chunks.

func SetupEnv

func SetupEnv(ctx context.Context, cfg *Config) (*Env, error)

SetupEnv dials L1/L2, initializes contract bindings and bridge service client. BridgeServiceURL in cfg.RemoveGER is required. Exported for use by E2E tests that invoke the tool programmatically.

func SetupScanEnv

func SetupScanEnv(ctx context.Context, cfg *Config) (*Env, error)

SetupScanEnv dials L1/L2 and initializes the contract bindings needed for scan-invalid-claims.

func (*Env) Close

func (e *Env) Close() error

Close closes all RPC connections. BridgeService has no Close.

type GERExistsOnL1Error

type GERExistsOnL1Error struct {
	GER common.Hash
}

GERExistsOnL1Error is returned when the GER exists on L1 (not invalid) and --force was not set.

func (GERExistsOnL1Error) Error

func (e GERExistsOnL1Error) Error() string

type GenerateParams

type GenerateParams struct {
	DestinationNetwork uint32
	DestinationAddress common.Address
	OriginNetwork      uint32
	OriginAddress      common.Address
	Amount             *big.Int
	DepositCount       uint32
	LeafType           uint8
}

GenerateParams holds the input parameters for generating an invalid GER scenario.

type GeneratedInvalidGER

type GeneratedInvalidGER struct {
	GER             common.Hash
	MainnetExitRoot common.Hash
	RollupExitRoot  common.Hash
	ProofLocal      [32][32]byte
	ProofRollup     [32][32]byte
	GlobalIndex     *big.Int
	Params          GenerateParams
}

GeneratedInvalidGER holds all computed values for the generated invalid GER scenario.

func GenerateInvalidGER

func GenerateInvalidGER(params GenerateParams) *GeneratedInvalidGER

GenerateInvalidGER computes a deterministic invalid GER from the given parameters. It builds a bridge leaf, places it in a single-leaf merkle tree (zero-hash siblings), and derives the GER from the resulting mainnet exit root.

type RemoveGERConfig

type RemoveGERConfig struct {
	// SovereignAdminKey is the signing key with privileges to:
	// - activateEmergencyState / deactivateEmergencyState on the L2 bridge
	// - removeGlobalExitRoots on the L2 GER manager
	// - unsetMultipleClaims / setMultipleClaims on the L2 bridge
	// - forceEmitDetailedClaimEvent on the L2 bridge
	// Supports local keystore, AWS KMS, and GCP KMS via signertypes.SignerConfig.
	SovereignAdminKey signertypes.SignerConfig `mapstructure:"SovereignAdminKey"`

	// BridgeServiceURL is the URL of the aggkit bridge service REST API (required).
	// Used for querying claims, bridges, and proofs.
	BridgeServiceURL string `mapstructure:"BridgeServiceURL"`

	// L2NetworkID is the network ID of the L2 network served by the bridge service.
	// Required for querying L2 claims via the bridge service.
	L2NetworkID uint32 `mapstructure:"L2NetworkID"`
}

RemoveGERConfig contains configuration specific to the remove-GER tool.

type ScanInvalidClaimsParams

type ScanInvalidClaimsParams struct {
	FromBlock uint64
	ToBlock   uint64
	ChunkSize uint64
}

ScanInvalidClaimsParams defines the block range and chunk size for scan-invalid-claims.

type Scenario

type Scenario string

Scenario is the overall or per-claim classification from the runbook.

const (
	ScenarioNoClaims   Scenario = "no_claims"
	ScenarioCategoryA  Scenario = "category_a"
	ScenarioCategoryB1 Scenario = "category_b1"
	ScenarioCategoryB2 Scenario = "category_b2"
)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL