EVM - #154
Open
lordbutterfly-hive wants to merge 16 commits into
Open
Conversation
miloridenour
force-pushed
the
develop
branch
2 times, most recently
from
May 18, 2026 20:09
01985c2 to
81442c5
Compare
miloridenour
force-pushed
the
develop
branch
2 times, most recently
from
May 19, 2026 18:27
ff6561c to
df882dc
Compare
miloridenour
force-pushed
the
develop
branch
3 times, most recently
from
June 2, 2026 00:25
2928dea to
75c384f
Compare
miloridenour
force-pushed
the
feature/evm-mapping-bot
branch
from
June 8, 2026 20:26
e4f39c0 to
77ffd6f
Compare
Closed
miloridenour
force-pushed
the
develop
branch
2 times, most recently
from
June 24, 2026 22:08
24331ec to
7e1d77e
Compare
The reindex ChainActiveAt callback queried electionDb before its Mongo collection was bound (the elections plugin inits after the reindex gate), nil-dereferencing and crash-looping every node on its first restart; it now reads the elections collection directly off the connected DbInstance via a new elections.ChainActiveVersionAt helper. Corrected the consensus-version devnet test's "0.3" vs "0.3.0" target comparison and made candidate visibility race-robust by latching on adoption. Added TestConsensusVersionLateAdoptionReindexDevnet, which takes a node offline through a rollout to cover both the restart/catch-up path and the seeded version-lag full reindex.
…/F21/F22) Fix five deterministic, network-wide chain halts. Each panics before the per-tx recover (executeTxSafely), so it escapes to the block-listener's coarse recover and permanently stops the block pipeline on every node: - F4: an unknown offchain op.Type left a nil VSCTransaction that was dereferenced in Ingest/ExecuteBatch. Unprivileged (any funded user via submitTransactionV1), devnet-confirmed. - F21: a known op with an undecodable, attacker-controlled CBOR payload nil-dereferenced node.MarshalJSON in DecodeTxCbor. - F9: a dropped bls.AggregatePubkeys error fed a nil/invalid pubkey into bls.Verify and panicked. - F13: an empty witness list divided by zero in GenerateSchedule. - F22: a zero-op tx indexed Ops[0] out of range in the block signer path. F4/F21 are handled together: ToTransaction now returns ([]VSCTransaction, error) and errors on the first unknown type or undecodable payload instead of dropping ops or relying on zero-value rejection. An invalid tx fails ATOMICALLY -- no op executes, a fixed 50 RC is charged, and the whole tx is marked FAILED via the same oplog finalization as every other tx (new TxPacket.Invalid branch in ExecuteBatch).
…4-3) An incoming election whose new committee is below MinMembers (e.g. empty) would persist and then drive consensus.GenerateSchedule into witnessList[slot % 0] -- an integer divide-by-zero that panic-halts every validating node. Reject it in TxElectionResult.ExecuteTx before StoreElection so the prior committee stays in charge and the chain keeps producing. Gated on the 0.4.0 consensus line (MinMembersGuardActive), resolved from the version active at the election's submit height (ActiveConsensusVersion(tx.Self.BlockHeight)) -- deterministic on-chain and replay-safe (mainnet history has valid pre-raise 7-member elections). Keep the pure GenerateSchedule empty-list backstop (already on develop as the F13 fix) as defense-in-depth, and log invalid block-proposal rejections symmetrically with the skip/stale cases.
The offchain unstake_hbd op built TxStakeHbd (identical to stake_hbd), so it STAKED funds instead of releasing them -- the wrong direction. Build the dedicated TxUnstakeHbd (ledgerSession.Unstake) instead. The L1 vsc.unstake_hbd path was already correct; only the offchain path was wrong. Correcting the direction changes ledger state on a VALID input, so it is version-gated on the 0.4.0. chain-active version is threaded into OffchainTransaction.ToTransaction via ActiveConsensusVersion at the op's anchored height (same height in Ingest and ExecuteBatch), so a full reindex reproduces historical ledger state and the fix flips network-wide only at the 0.4.0 floor.
The contract's deposit-proof reader expects PER-RECEIPT logIndex (position within the tx's own Logs list, 0..N-1). The bare upstream bot wrote eth_getLogs's BLOCK-LEVEL index into the deposit payload — that's correct only by accident when a tx emits exactly one matching log. A multi-Transfer-log tx (e.g. one deposit that also triggers a token-side side-effect Transfer to the same vault) maps both logs to block-level indices that the contract treats as nonsense per-receipt positions -> deposit either crashes parsing or credits the wrong sender/amount, and on the surviving path collides on IsObserved -> permanently uncreditable. Fix lifted from review5 W4-A CRIT #14 + the wetransfer F2-bot hardening: - New lookupPerReceiptLogIndex(rpc, txHash, tokenAddr, vaultPaddedTopic, blockLogIndex) function. Fetches the receipt, finds the SPECIFIC log whose block-level logIndex matches the eth_getLogs entry, returns its per-receipt array position. Sanity-checks address/topics so a stale receipt or RPC mistake surfaces as an error rather than mis-resolving. - Caller (the scanBlock ERC-20 deposit emit loop) now calls lookupPerReceiptLogIndex and writes the resolved position. Skips the deposit with a slog.Warn on lookup failure (will be re-detected on the next scan of the same height). - F2 hardening: a non-standard RPC returning a log with no logIndex is rejected explicitly (hexToUint64("")==0 would otherwise silently match only block index 0, mis-resolving every multi-log tx). Reuses upstream's existing rpc.getReceipt + TransferEventSig + hexToUint64 (no new helpers introduced). Build clean: go build ./cmd/evm-mapping-bot/.
miloridenour
force-pushed
the
feature/evm-mapping-bot
branch
from
July 3, 2026 16:30
f7c3faa to
8149583
Compare
Lands 20 approved MED fixes (MEDs 43,137,120,4,44,30,46,51,47,49,50,48, 98,96,72,99,91,57,115,116) onto cmd/evm-mapping-bot/main.go. MED-100 is a sound deferral (separate-repo contract finding, zero uint16 casts in the bot). Scrutinizer-PASS verdict: bot-all.md. Build + vet clean; zero new test regressions (one pre-existing failure fixed by MED-30 forward-compat). Also adds /evm-mapping-bot to .gitignore to prevent committing the compiled output binary.
account-mapping zkverifier id (M23-F5 fix; bot was mapping zero deposits) The bot capped its L1 scan at contractHeight = state key 'h' read from the account-mapping contract, but M23-F5 moved 'h' into the ZK verifier contract. The bot had no verifier id -> contractHeight always 0 -> scanUpTo=min(finalized,0)=0 -> mapped ZERO deposits. Fix: fetchContractLastHeight reads am['zkverifier'] then 'h' from that verifier (legacy local 'h' honored first; no new config). Devnet-proven (BotResilience: LastScannedBlock 0 -> 74).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Standalone bot binary for the EVM bridge — scans Ethereum for deposits, constructs MPT proofs, submits to the EVM
mapping contract, and handles the withdrawal broadcast + confirmation pipeline.
What it does
the vault address
mapcalls to the contract via L2 transactions
getTssRequests, assembles signed EIP-1559 transactions, broadcasts to Ethereum, submitsconfirmSpendwith receiptproofs
TransactionCrafter.SignFinal(same path as the UTXO mapping bot) for all contractinteractions. Separate DID, separate nonce space, no interference with existing bots.
Security
Two security passes (15 attack vectors, 53 test cases):
decodeRLPListHeadervalidates content fits dataConfiguration (env vars)
ETH_RPChttp://localhost:8545VAULT_ADDRESSCONTRACT_IDBOT_ETH_PRIVKEYGRAPHQL_URLShttps://api.vsc.eco/api/v1/graphqlNET_IDvsc-mainnetRC_LIMIT10000NETWORKmainnetmainnetortestnetFiles
cmd/evm-mapping-bot/main.go— bot binary (1,850 lines)cmd/evm-mapping-bot/security_pass1_test.go— 53 security testsDependencies
feature/evm-bridge-nodePR merged first (crypto host functions + ledger support)Test plan
go build ./cmd/evm-mapping-bot/compilesgo vet ./cmd/evm-mapping-bot/cleango test ./cmd/evm-mapping-bot/ -run TestSecurity— 53/53 passgo test -race ./cmd/evm-mapping-bot/ -run TestSecurity— zero races