feat(tools): templar-oft-bridge operator CLI (draft) - #622
Draft
carrion256 wants to merge 11 commits into
Draft
carrion256 wants to merge 11 commits into
carrion256 wants to merge 11 commits into
Conversation
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
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
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
carrion256
force-pushed
the
feat/oft-bridge-cli
branch
from
September 3, 2026 15:18
66acb82 to
5ec8586
Compare
| #[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()); |
carrion256
force-pushed
the
feat/oft-bridge-cli
branch
from
September 3, 2026 16:27
686b11a to
5783de7
Compare
| 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.
There was a problem hiding this comment.
CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
| .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) |
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.
Qualification closure
This delivery closes the frozen Ralplan contract and the pending qualification gates. Commits
15f55c26(qualification closure) andbd60016a(post-audit on-chain correctness + delivery guards).Step 8 leg-intent custody (canary)
quote_liveis now read-only observation — no executable-plan construction, no nonce/sequence reservation, no simulation or signing.LegIntentV1binds 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.sendre-runs the offline revalidation, re-reads the same live observations, and refuses with aConflictnaming any drifted field before signing. Offline-preview intents without bindings are refused (fresh-quote discipline).verify_stellar_plan_fee_ceilingparses the assembled envelope XDR and rejects over a stroke-fee ceiling;verify_evm_plan_fee_ceilingrejects Policy when live gas/fee prices exceed the bound ceilings. Both run immediately after plan construction, before signing.EvmChain::latest_block, observation reads) reusing existing readback patterns; all existing fakes preserved via trait defaults.--rpc-headers-file(plan-mandated)Options.headers; EVM via a reqwest client withdefault_headersthroughconnect_reqwest. Resolved once inCli::run, so no construction path can skip credentialed RPC.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, deadreconcile::health_command, uncalledintent_operation, thirdevm_address_to_bytes32copy, the deadCanaryMessageV1state machine and its tests, and eight duplicatedSystemTime/now_unixclock reads consolidated intolib::now_unix.stellar_symbol/stellar_argreuse: the error-variant split (user-arg encoding →InvalidInputvs chain readback →Chain) is load-bearing.--jsonfield deletion: clap would reject the unknown flag andmain_entryspecial-cases it for the parse-error JSON envelope; retained as an accepted compatibility flag.Post-audit on-chain correctness (commit
bd60016a)lz_token_feewhere Soroban contracttype decodes by exact key name (MessagingFeeis{native_fee, zro_fee}); corrected tozro_fee.set_peer/set_delegatebareOption<T>encodings are correct against pinnedsoroban-env-common25.0.1 (Option<T>isSCV_VOID/bare-inner, not a Vec). Verified the statefulbuild_stellar_operation_for_routealready emits the official 6-arglz_receive(executor, origin, guid, message, extra_data, value).config_type; EVM SendLegmsg.value=native_fee_raw; containment mainnet gate fires before operation derivation; management/leg RPC URLs are gracefulOption(missing → no hard both-chain requirement).--stellar-rpc-envmust propagate into the subcommand's flattenedRpcArgs(clap propagation, not accept-then-dropped).Verification
cargo test -p templar-oft-bridge-cliunder the pinned 1.86.0 toolchain (/home/common/.rustup/toolchains/1.86.0-…/bin/cargo): 255 passed, 0 failed.cargo check --all-targets: clean.This change is