test(bifrost): keysign byte-parity and gas-unit contract tests with THORNode

Why

test-simulation was the only CI job to catch two bugs in the contract between THORNode and bifrost. Each side's unit tests used its own hand-picked values or static fixtures, so neither side could see the mismatch:

  • !4342: a bot commit dropped InboundFinalized from bifrost's TxArrayItem. THORNode signs the raw keysign JSON, and bifrost re-marshals what it reads before verifying. The field was silently lost, every keysign failed with "bad signature", and outbounds on every chain stalled.
  • !3925 (merged): a rebase kept THORNode's switch to per-chain gas units but dropped bifrost's half. EVM outbounds aborted with "max gas exceeded" on every retry, and GAIA MaxGas was 100× too high.

What

bifrost/thorclient/keysign_parity_test.go

  • Fills every field of THORNode's TxOut by reflection, so new fields are covered automatically.
  • Signs and marshals it the way queryKeysign does, then runs bifrost's real GetKeysign, including signature verification.
  • Also zeroes each field in turn, to catch omitempty mismatches.

gas_unit_contract_test.go in evm, ethereum, gaia, xrp, utxo, monero and tron

  • Starts from a native gas price and takes the rate the scanner would post.
  • Runs that rate through THORNode's real GetGasDetails, then through the client's real outbound build.
  • Asserts the outbound is not aborted and that the effective price is about the native price.
  • Covers AVAX, BSC, BASE, POL, ETH, GAIA, NOBLE, XRP, BTC, LTC, BCH, DOGE, XMR and TRON. ZEC gets a MaxGas check only. SOL is not applicable: it never reads GasRate or MaxGas.

bifrost/pkg/chainclients/shared/gascontract

  • A small helper, imported only by tests, that runs THORNode's gas manager with a stub keeper.
  • go.mod and go.sum are unchanged.

Mutation checks

Each mutation below was applied and then reverted.

Mutation Result
Add InboundFinalized to THORNode's TxOutItem only (the !4342 state) keysign.tx_array[0].inbound_finalized: signed by THORNode but dropped by bifrost; GetKeysign fails with bad signature
Delete CloutSpent from bifrost's TxArrayItem Fails
Remove omitempty from bifrost's memo Fails
EVM scanner posting in the old 1e8 units 7 of 8 EVM cases fail. The 8th, BSC at 1 gwei, happens to round to the same rate.
EVM client converting with ×1e10 All 8 fail
Same two mutations on ETH Fail
Drop GAIA's conversion Fails with the 100× MaxGas symptom

Noticed on develop, not fixed here

  1. BASE outbound gas limit. buildOutboundTx subtracts ExtraL1GasFee from MaxGas. On BASE that can go negative, and big.Int.Uint64() then returns a huge gas limit instead of aborting. Default config can't hit this today: the minimum MaxGas is 1500, against a 700 L1 fee.
  2. GAIA/NOBLE fee posting. updateGasFees compares feeDelta.LTE(resolution) against a lastFee that starts at zero. A first fee equal to gas_price_resolution is never posted, so NOBLE at its 100,000 uusdc floor never posts from a fresh scanner. EVM guards this with lastReported != 0.
  3. Keysign JSON tags differ between the two sides. chain, to_address and height disagree on omitempty. This is harmless today, and the test documents it.
  4. DOGE fee rounding. DOGE rounds up a full 500,000 sats/vB step. This is a config choice, not a units bug.

Testing

  • go test -tags=mocknet ./bifrost/thorclient/ -run TestPackage -check.f KeysignParitySuite: 3 tests, 0.16 s.
  • go test -tags=mocknet ./bifrost/pkg/chainclients/{evm,ethereum,gaia,xrp,utxo,tron}/ -run 'TestPackage|TestEVMPackage|TestETHPackage' -check.f GasUnitContract: 0.01–0.61 s each.
  • go test -tags=mocknet ./bifrost/pkg/chainclients/monero/ -run '^TestGasUnitContract$'
  • Also passing:
    • the full test suites of every touched package;
    • -race;
    • the mainnet tag for utxo and monero;
    • golangci-lint v1.63.4 with the CI config.

🤖 Generated with Claude Code

Merge request reports

Loading
Loading