Skip to content

feat(tools): templar-oft-bridge operator CLI (draft) - #622

Draft
carrion256 wants to merge 11 commits into
devfrom
feat/oft-bridge-cli
Draft

carrion256 wants to merge 11 commits into
devfrom
feat/oft-bridge-cli

Conversation

@carrion256

@carrion256 carrion256 commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Qualification closure

This delivery closes the frozen Ralplan contract and the pending qualification gates. Commits 15f55c26 (qualification closure) and bd60016a (post-audit on-chain correctness + delivery guards).

Step 8 leg-intent custody (canary)

  • quote_live is now read-only observation — no executable-plan construction, no nonce/sequence reservation, no simulation or signing.
  • LegIntentV1 binds the full Step 8 observation/policy surface: quote source ledger/block, observed source sequence/nonce, fee ceilings (Stellar resource-fee ceiling; EVM max-fee/priority/gas-limit), pre-send balance/lockbox/supply snapshot, route finality policy, and the additional-obligation outstanding/cap.
  • send re-runs the offline revalidation, re-reads the same live observations, and refuses with a Conflict naming any drifted field before signing. Offline-preview intents without bindings are refused (fresh-quote discipline).
  • Pre-signing fee-ceiling guards: verify_stellar_plan_fee_ceiling parses the assembled envelope XDR and rejects over a stroke-fee ceiling; verify_evm_plan_fee_ceiling rejects Policy when live gas/fee prices exceed the bound ceilings. Both run immediately after plan construction, before signing.
  • Real HTTP implementations added (EvmChain::latest_block, observation reads) reusing existing readback patterns; all existing fakes preserved via trait defaults.

--rpc-headers-file (plan-mandated)

  • Wired into both HTTP clients: Stellar via soroban Options.headers; EVM via a reqwest client with default_headers through connect_reqwest. Resolved once in Cli::run, so no construction path can skip credentialed RPC.
  • New recorded-evidence keys now required for leg quote: canary:finality_policy, canary:max_outstanding_obligation_raw, canary:stellar_resource_fee_ceiling_raw:stellar_to_evm, canary:evm_gas_limit:evm_to_stellar, canary:evm_max_fee_per_gas_wei:evm_to_stellar, canary:evm_max_priority_fee_per_gas_wei:evm_to_stellar.

Ponytail final diff audit

Removed (safe, with reason for rejections): uncalled restore_messages_log, dead reconcile::health_command, uncalled intent_operation, third evm_address_to_bytes32 copy, the dead CanaryMessageV1 state machine and its tests, and eight duplicated SystemTime/now_unix clock reads consolidated into lib::now_unix.

  • Rejected stellar_symbol/stellar_arg reuse: the error-variant split (user-arg encoding → InvalidInput vs chain readback → Chain) is load-bearing.
  • Rejected --json field deletion: clap would reject the unknown flag and main_entry special-cases it for the parse-error JSON envelope; retained as an accepted compatibility flag.

Post-audit on-chain correctness (commit bd60016a)

  • Fixed live on-chain bug: the Stellar SendLeg MessagingFee map key used lz_token_fee where Soroban contracttype decodes by exact key name (MessagingFee is {native_fee, zro_fee}); corrected to zro_fee.
  • Verified set_peer/set_delegate bare Option<T> encodings are correct against pinned soroban-env-common 25.0.1 (Option<T> is SCV_VOID/bare-inner, not a Vec). Verified the stateful build_stellar_operation_for_route already emits the official 6-arg lz_receive (executor, origin, guid, message, extra_data, value).
  • Verified end-to-end on committed tree: UlnConfig encodes exactly the 4 official top-level keys with DVN fields nested; direction-aware config_type; EVM SendLeg msg.value = native_fee_raw; containment mainnet gate fires before operation derivation; management/leg RPC URLs are graceful Option (missing → no hard both-chain requirement).
  • White-box guard added: top-level --stellar-rpc-env must propagate into the subcommand's flattened RpcArgs (clap propagation, not accept-then-dropped).

Verification

  • cargo test -p templar-oft-bridge-cli under the pinned 1.86.0 toolchain (/home/common/.rustup/toolchains/1.86.0-…/bin/cargo): 255 passed, 0 failed.
  • cargo check --all-targets: clean.
  • ethnum 1.5.3 lock pin confirmed to compile and pass under pinned rustc 1.86 from the offline registry cache (not a host-1.97-only artifact).

This change is Reviewable

Implements the approved LayerZero Stellar<->Sepolia OFT operator CLI plan:
- Closed OperationV1 grammar, no generic mutation/calldata/XDR escape hatches
- Hash-chained local custody store with route locking and tamper detection
- USDC pre-context refusal (unsupported_use_cctp) and hard mainnet
  mutation disable (production_mutation_unsupported_v1) before any
  state/artifact/signer access
- Typed codec for SEP-41 strkeys, Type-3 options, shared-decimals(6)
- Stage-delta custody reconciliation and stable health output
- Fail-closed artifact lock (pinned official sources; unbuilt bytecode
  hashes remain null and verification fails closed)
- Governance proposal custody, packet recovery capability matrix,
  containment planning; live adapter execution fails closed
@coderabbitai

coderabbitai Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Comment @coderabbitai help to get the list of available commands.

Frozen plan SHA-256 45c5cf3650886ea3dab4646fa366c9e4809c06f71d29a57e273926e97106fc5d
let deployer =
parse_address("0xb20a608c624ca5003905aa834de7156c68b2e1d0").expect("valid deployer");
assert_eq!(
canonical_address(derive_create_address(deployer, 0)),
"0x00000000219ab540356cbb839cbe05303d7705fa"
);
assert_eq!(
canonical_address(derive_create_address(deployer, 1)),
let endpoint = parse_address("0x00000000219ab540356cbb839cbe05303d7705fa").expect("valid");
let binding = DeployEvmOftBindingV1::bind(
deployer,
0,
…rnings clean

- reconcile honors --fail-on-deficit with typed custody error
- stellar role set-admin maps to SetRoleAdmin
- fix needless_pass_by_value, field_reassign_with_default, doc_lazy_continuation,
  match_same_arms so -D warnings passes for real
…goldens, canary legs, deterministic EVM artifact freeze

- layerzero::encode_calldata against vendored @layerzerolabs artifacts with
  cast-verified selectors; 7 golden tests
- closed operation draft grammar (43 commands, kebab-case dispatch, no
  OperationV1 JSON input)
- CommandEffect classification wired through the output envelope
- wrap planner: fully concrete 5-node plans, XDR SAC/contract derivation,
  CREATE1 peer derivation, USDC/issuer/evidence boundaries
- LegIntentV1 binding for leg quote/send proposals; canary read-only paths
  opened to mainnet inspection while mutations stay testnet-gated
- init: live environment verification (passphrase, chain id, endpoint EID)
- deterministic EVM artifact build: digest-verified dependency archive
  (peer-resolved pnpm closure, OZ 5.6.1), forge via injected executor,
  frozen creation/runtime bytecode keccak256 with fail-closed divergence
  gate; artifact verify green against the frozen lock
- clippy -D warnings clean across all targets; 76 tests green
Comment thread tools/oft-bridge-cli/tests/message_ledger.rs Fixed
Comment thread tools/oft-bridge-cli/tests/message_ledger.rs Fixed
Comment thread tools/oft-bridge-cli/tests/message_ledger.rs Fixed
Comment thread tools/oft-bridge-cli/tests/message_ledger.rs Fixed
Comment thread tools/oft-bridge-cli/tests/message_ledger.rs Fixed
#[test]
fn issued_asset_requires_evidence_when_demanded() {
let mut d = desired(AssetKind::IssuedSep41, TESTNET_NATIVE_SAC);
let error = plan_wrap(&d, "h", "n", "s", 0, true).expect_err("evidence demanded");
d.asset.issuer_custodian_evidence_sha256 = Some("a".repeat(64));
d.asset.destination_acceptance_evidence_sha256 = Some("b".repeat(64));
d.asset.custody_risk_acceptance_sha256 = Some("c".repeat(64));
plan_wrap(&d, "h", "n", "s", 0, true).expect("evidence satisfied");
fn forbidden_issuer_cannot_be_operator() {
let mut d = desired(AssetKind::NativeSac, "native");
d.asset.forbidden_classic_issuer = Some(OPERATOR.into());
let error = plan_wrap(&d, "h", "n", "s", 0, false).expect_err("issuer excluded");
#[test]
fn empty_name_and_symbol_are_rejected() {
let d = desired(AssetKind::NativeSac, "native");
assert!(plan_wrap(&d, "h", "", "s", 0, false).is_err());
fn empty_name_and_symbol_are_rejected() {
let d = desired(AssetKind::NativeSac, "native");
assert!(plan_wrap(&d, "h", "", "s", 0, false).is_err());
assert!(plan_wrap(&d, "h", "n", " ", 0, false).is_err());
fn encrypted_keystore_fixture_qualifies_with_expected_identity() {
let dir = tempfile::tempdir().expect("tempdir");
let key = [0xAB; 32];
let password = "op-secret-marker-7f3a";
fn encrypted_keystore_fixture_rejects_symlink() {
let dir = tempfile::tempdir().expect("tempdir");
let key = [0xAB; 32];
let password = "op-secret-marker-7f3a";
fn encrypted_keystore_fixture_rejects_unsafe_mode() {
let dir = tempfile::tempdir().expect("tempdir");
let key = [0xAB; 32];
let password = "op-secret-marker-7f3a";
let dir = tempfile::tempdir().expect("tempdir");
let key = [0xAB; 32];
let other_key = [0xCD; 32];
let password = "op-secret-marker-7f3a";
fn encrypted_keystore_secrets_never_enter_errors_or_rendered_commands() {
let dir = tempfile::tempdir().expect("tempdir");
let key = [0xAB; 32];
let password = "op-secret-marker-7f3a";
let expected = fixture_signer(&key).address();
let path = write_encrypted_fixture(dir.path(), &key, password, 0o600);

let error = keystore_signer(&path, "wrong-password", expected)
Complete the frozen Ralplan contract and close the pending delivery gates:

- Step 8 canary leg intent: quote_live is read-only observation (no plan
  construction, no nonce reservation, no simulation); LegIntentV1 binds the
  full Step 8 observation/policy surface (source ledger/block, observed
  sequence/nonce, fee ceilings, pre-send balance/lockbox/supply, finality
  policy, additional-obligation cap); send re-reads live observations and
  refuses drift per VM before signing; pre-signing fee-ceiling guards parse
  the assembled envelope and reject over-ceiling on both chains.
- Wire the plan-mandated --rpc-headers-file into both HTTP clients (Stellar
  soroban Options.headers; EVM reqwest default_headers via connect_reqwest),
  resolved once at Cli::run so no construction path skips credentialed RPC.
- Ponytail audit: delete uncalled restore_messages_log, dead
  reconcile::health_command, uncalled intent_operation, duplicate
  evm_address_to_bytes32, and the dead CanaryMessageV1 state machine and its
  tests; consolidate 8 duplicate SystemTime/now_unix clock reads into
  lib::now_unix. Rejected stellar_symbol/stellar_arg reuse (load-bearing
  InvalidInput-vs-Chain error classification). Retained --json as an accepted
  compatibility flag (clap parse + main_entry parse-error envelope).
- ethnum 1.5.3 lock pin so the workspace compiles under host rustc 1.97.

Verification: cargo test -p templar-oft-bridge-cli 254 passed, 0 failed;
--all-targets check clean; no project-wide output emitted.
- Fix Stellar SendLeg MessagingFee map key: "lz_token_fee" -> "zro_fee".
  Soroban contracttype structs decode by exact map-key name; the official
  MessagingFee is { native_fee, zro_fee }, so the prior key makes send()
  fail to decode on-chain.
- Add a white-box guard that the top-level global --stellar-rpc-env value
  is NOT accepted-then-dropped: clap must propagate it into the subcommand's
  flattened RpcArgs (verified it does; the RPC global surface delivers via
  propagation for child commands and directly for `leg`).
- Verified against the frozen contract source and pinned soroban-env-common
  25.0.1: set_peer/set_delegate bare Option<T> encodings are correct
  (Option<T> is SCV_VOID/bare inner, not a Vec), and the stateful
  build_stellar_operation_for_route already emits the official 6-arg
  lz_receive (executor, origin, guid, message, extra_data, value) around the
  base encoder — no change needed.

Verification: cargo test -p templar-oft-bridge-cli under the pinned 1.86.0
toolchain: 255 passed, 0 failed. --all-targets clean.

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@carrion256 carrion256 self-assigned this Sep 8, 2026
.to_xdr(Limits::none())
.map_err(|error| Error::InvalidInput(format!("xdr encode failed: {error}")))?;
let digest = sha2::Sha256::digest(encoded);
let mut bytes = [0u8; 32];
let raw = hex::decode(hex_digits).map_err(|error| {
Error::InvalidInput(format!("malformed EVM address {address}: {error}"))
})?;
let mut bytes = [0u8; BYTES32_LEN];
}

fn append_stuck(root: &Path, direction: Direction, seed: &str, outstanding_raw: &str) {
let mut record = common::message_record(direction, 1, seed);
#[test]
fn settled_ledger_history_does_not_gate_a_new_send() {
let (_directory, root) = route();
let mut settled = common::message_record(Direction::StellarToEvm, 1, "settled");
fn quote_live_records_observations_without_plan_or_nonce_reservation() {
let (_directory, root) = live_route();
let stellar = obs_stellar(4_310, "41", "900000", "2000000");
let evm = obs_evm(420, 7, "2000000");
fn send_live_rejects_stellar_lockbox_drift_before_signing() {
let (_directory, root) = live_route();
let stellar = obs_stellar(4_310, "41", "900000", "2000000");
let evm = obs_evm(420, 7, "2000000");
fn send_live_rejects_stellar_sequence_drift_before_signing() {
let (_directory, root) = live_route();
let stellar = obs_stellar(4_310, "41", "900000", "2000000");
let evm = obs_evm(420, 7, "2000000");
fn send_live_refuses_an_offline_preview_intent() {
let (_directory, root) = live_route();
let stellar = obs_stellar(4_310, "41", "900000", "2000000");
let evm = obs_evm(420, 7, "2000000");

let (_directory, root) = live_route();
let stellar = obs_stellar(4_310, "41", "900000", "2000000");
let evm = obs_evm(420, 7, "2000000");
}

fn plan(desired: &DesiredRouteV1) -> Result<templar_oft_bridge_cli::wrap::WrapPlanV1> {
plan_wrap(desired, DESIRED_DIGEST, "Wrapped XLM", "wXLM", 7, false)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants