From e58e6cadf5b4cf8b58343b4665e3993f1f843015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 11 Aug 2025 16:40:18 +0300 Subject: [PATCH 1/9] feat: Add initial CI/CD. --- .github/workflow/build_and_test.yml | 85 +++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 .github/workflow/build_and_test.yml diff --git a/.github/workflow/build_and_test.yml b/.github/workflow/build_and_test.yml new file mode 100644 index 0000000..eaa1cdc --- /dev/null +++ b/.github/workflow/build_and_test.yml @@ -0,0 +1,85 @@ +name: Build And Test + +on: + push: + branches: + - main + - cli-rust-rewrite # TODO: remove when merged to main + - "releases/*" + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ (github.ref != 'refs/heads/main') }} + +env: + CARGO_TERM_COLOR: always + + CARGOFLAGS: --workspace --all-targets + CARGOFLAGS_ALL_FEATURES: --workspace --all-targets --all-features + +jobs: + debug_tests_all_features: + name: Debug | All features | Test + runs-on: ubicloud-standard-16 + + env: + INFO_LOG_FILE: ${{ github.workspace }}/test-logs/debug/debug-all-features-test.log + + steps: + - name: Collect Workflow Telemetry + uses: catchpoint/workflow-telemetry-action@v2 + with: + comment_on_pr: false + + - uses: actions/checkout@v4 + + - name: Create test log directories + run: mkdir -p test-logs/debug + + - name: Run tests + run: | + set -o pipefail + cargo test $CARGOFLAGS_ALL_FEATURES + + - name: Upload deposit state artifact + if: failure() + uses: actions/upload-artifact@v4 + with: + name: deposit-state-debug + path: core/src/test/data/deposit_state_debug.bincode + if-no-files-found: ignore + retention-days: 1 + + release_tests_all_features: + name: Release | All features | Test + runs-on: ubicloud-standard-16 + + env: + INFO_LOG_FILE: ${{ github.workspace }}/test-logs/debug/debug-all-features-test.log + + steps: + - name: Collect Workflow Telemetry + uses: catchpoint/workflow-telemetry-action@v2 + with: + comment_on_pr: false + + - uses: actions/checkout@v4 + + - name: Create test log directories + run: mkdir -p test-logs/debug + + - name: Run tests + run: | + set -o pipefail + cargo test --release $CARGOFLAGS_ALL_FEATURES + + - name: Upload deposit state artifact + if: failure() + uses: actions/upload-artifact@v4 + with: + name: deposit-state-debug + path: core/src/test/data/deposit_state_debug.bincode + if-no-files-found: ignore + retention-days: 1 From 43323fd7688a8ce401f6d63545767033623d48a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 11 Aug 2025 16:41:59 +0300 Subject: [PATCH 2/9] feat: Add code checks CI/CD --- .../build_and_test.yml | 0 .github/workflows/code_checks.yml | 92 +++++++++++++++++++ 2 files changed, 92 insertions(+) rename .github/{workflow => workflows}/build_and_test.yml (100%) create mode 100644 .github/workflows/code_checks.yml diff --git a/.github/workflow/build_and_test.yml b/.github/workflows/build_and_test.yml similarity index 100% rename from .github/workflow/build_and_test.yml rename to .github/workflows/build_and_test.yml diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml new file mode 100644 index 0000000..1cfe0c7 --- /dev/null +++ b/.github/workflows/code_checks.yml @@ -0,0 +1,92 @@ +name: Code Checks + +on: + push: + branches: + - main + - "releases/*" + - cli-rust-rewrite # TODO: remove when merged to main + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ (github.ref != 'refs/heads/main') }} + +env: + CARGO_TERM_COLOR: always + +jobs: + formatting: + name: Check formatting + runs-on: ubicloud-standard-2 + + steps: + - uses: actions/checkout@v4 + - name: Run Cargo fmt + run: cargo fmt --check + + linting: + name: Check linting + runs-on: ubicloud-standard-2 + + steps: + - uses: actions/checkout@v4 + - name: Run Cargo clippy + run: cargo clippy --no-deps --all-targets --all-features -- -Dwarnings + + udeps: + name: Check unused dependencies + runs-on: ubicloud-standard-2 + + steps: + - uses: actions/checkout@v4 + + - name: Toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2025-03-09 + override: true + + - name: Run cargo-udeps + env: + RUSTFLAGS: -A warnings + uses: aig787/cargo-udeps-action@v1 + with: + version: "latest" + args: "--workspace --all-features --all-targets" + + codespell: + name: Check spelling + runs-on: ubicloud-standard-2 + if: github.event.pull_request.draft == false + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install codespell + run: pip install codespell + + - name: Run codespell + run: | + codespell --skip="*.lock,./target" -I="codespell_ignore.txt" + + check_for_todos: + name: Check for TODOs + runs-on: ubicloud-standard-2 + if: github.event.pull_request.draft == false + steps: + - uses: actions/checkout@v4 + + - name: Check for TODOs + run: | + if git grep -i "TODO" -- ':!docs' ':!*.md' ':!*.txt' ':!*.rst' ':!*.lock' ':!target' ':!scripts' ':!tests' ':!examples' ':!**/code_checks.yml'; then + echo "Found TODOs in code directories. Please address them before merging."; + exit 1; + else + echo "No TODOs found in code directories."; + fi From 59b062d77657d2a37bd976baba6215d528ca7e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Mon, 11 Aug 2025 16:48:30 +0300 Subject: [PATCH 3/9] fix: Tests. --- src/config.rs | 25 ++++--------------------- src/lib.rs | 2 +- src/storage.rs | 2 +- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/src/config.rs b/src/config.rs index 04fdf76..a10dd1f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,7 +20,7 @@ pub const USER_TAKES_AFTER: u64 = 200; pub fn get_backend_endpoint(network: Network) -> &'static str { match network { Network::Bitcoin => "https://api.citrea.xyz/", - Network::Testnet4 => "https://api.testnet.citrea.xyz/", + Network::Testnet4 | Network::Testnet => "https://api.testnet.citrea.xyz/", Network::Signet => "https://api.devnet.citrea.xyz/", _ => { panic!("No backend endpoint configured for network {:?}", network); @@ -98,33 +98,16 @@ mod tests { get_backend_endpoint(Network::Signet), "https://api.devnet.citrea.xyz/" ); - // For other networks, falls back to testnet assert_eq!( get_backend_endpoint(Network::Testnet), "https://api.testnet.citrea.xyz/" ); - assert_eq!( - get_backend_endpoint(Network::Regtest), - "https://api.testnet.citrea.xyz/" - ); - } - - #[test] - #[should_panic] - fn test_get_verifier_pks_bitcoin() { - get_verifier_pks(Network::Bitcoin); - } - - #[test] - #[should_panic] - fn test_get_verifier_pks_testnet4() { - get_verifier_pks(Network::Testnet4); } #[test] #[should_panic] - fn test_get_verifier_pks_signet() { - get_verifier_pks(Network::Signet); + fn test_get_backend_endpoint_regtest() { + get_backend_endpoint(Network::Regtest); } #[test] @@ -139,7 +122,7 @@ mod tests { assert_eq!(USER_TAKES_AFTER, 200); assert_eq!( UNSPENDABLE_XONLY_PUBKEY.to_string(), - "50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0" + "93c7378d96518a75448821c4f7c8f4bae7ce60f804d03d1f0628dd5dd0f5de51" ); } } diff --git a/src/lib.rs b/src/lib.rs index 681310b..131b050 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,8 +40,8 @@ pub mod musig2; pub mod parameters; pub mod script; pub mod storage; -pub mod withdrawal; pub mod types; +pub mod withdrawal; /// EVM Address type - 20 bytes #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)] diff --git a/src/storage.rs b/src/storage.rs index d5631fd..0571c46 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -35,7 +35,7 @@ pub fn store_key( "stored_at": chrono::Utc::now().to_rfc3339() }); fs::write(&key_file, serde_json::to_string_pretty(&key_data)?)?; - + // Set file permissions to 700 (rwx------) #[cfg(unix)] { From c540a00fb9dd1ea29714e0dc6a69045d5d5b9983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Tue, 12 Aug 2025 11:28:39 +0300 Subject: [PATCH 4/9] chore: Remove some of the todos and fix codespell. --- README.md | 7 ++++++- codespell_ignore.txt | 0 src/bin/cli.rs | 12 ++++++------ 3 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 codespell_ignore.txt diff --git a/README.md b/README.md index 1e402b1..3b42556 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ A wallet-agnostic command-line tool for interacting with Citrea, supporting secure Bitcoin deposits and withdrawals without requiring wallet connection. ## Features + - Deposit and withdrawal flows for Citrea - Airgapped key generation and signing - Bitcoin address and transaction utilities @@ -28,14 +29,18 @@ cargo install --path . Run `clementine --help` to see available commands. ## Security Warning + Some commands (notably key generation and signing) must be run in an airgapped environment. The CLI will prompt for confirmation before proceeding with sensitive operations. ## Documentation + See the [CLI documentation](./docs/cli.md) for detailed command usage. ## Contributing + - If you have suggestions for naming or structure, please open an issue or PR. - For musig2 and advanced cryptography, see TODOs and stubs in the codebase. ## License -MIT \ No newline at end of file + +MIT diff --git a/codespell_ignore.txt b/codespell_ignore.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/bin/cli.rs b/src/bin/cli.rs index 1184cfe..861ebe6 100644 --- a/src/bin/cli.rs +++ b/src/bin/cli.rs @@ -206,7 +206,7 @@ async fn main() { } } DepositCommands::DepositStatus { deposit_address } => { - println!("TODO: deposit.deposit_status: {}", deposit_address); + unimplemented!("deposit.deposit_status: {}", deposit_address); } DepositCommands::GetDepositParams { move_to_vault_txid, @@ -309,7 +309,7 @@ async fn main() { } } WithdrawalCommands::Status { withdrawal_index } => { - println!("TODO: withdrawal.status: {}", withdrawal_index); + unimplemented!("withdrawal.status: {}", withdrawal_index); } WithdrawalCommands::GenerateOperatorWithdrawalSignatures { withdrawal_address, @@ -318,8 +318,8 @@ async fn main() { withdrawal_utxo_vout, withdrawal_amount, } => { - println!( - "TODO: withdrawal.generate_operator_withdrawal_signatures: {} {} {} {} {}", + unimplemented!( + "withdrawal.generate_operator_withdrawal_signatures: {} {} {} {} {}", withdrawal_address, signer_address, withdrawal_utxo_txid, @@ -335,8 +335,8 @@ async fn main() { withdrawal_index, signature, } => { - println!( - "TODO: withdrawal.send_withdrawal_signatures_to_operators: {} {} {} {} {} {}", + unimplemented!( + "withdrawal.send_withdrawal_signatures_to_operators: {} {} {} {} {} {}", withdrawal_address, signer_address, withdrawal_utxo_txid, From 1e9180db22de22171721937618495dbabb5409f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Tue, 12 Aug 2025 11:32:39 +0300 Subject: [PATCH 5/9] chore: Remove more todos. --- .github/workflows/build_and_test.yml | 2 +- .github/workflows/code_checks.yml | 2 +- src/deposit.rs | 2 -- src/storage.rs | 2 +- src/withdrawal.rs | 4 ---- 5 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index eaa1cdc..5fbd650 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -4,7 +4,7 @@ on: push: branches: - main - - cli-rust-rewrite # TODO: remove when merged to main + - cli-rust-rewrite - "releases/*" pull_request: types: [opened, synchronize, reopened, ready_for_review] diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index 1cfe0c7..b119148 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -5,7 +5,7 @@ on: branches: - main - "releases/*" - - cli-rust-rewrite # TODO: remove when merged to main + - cli-rust-rewrite pull_request: types: [opened, synchronize, reopened, ready_for_review] diff --git a/src/deposit.rs b/src/deposit.rs index 7db41e6..542a80e 100644 --- a/src/deposit.rs +++ b/src/deposit.rs @@ -224,8 +224,6 @@ pub fn verify_recovery_tx( Ok((txid, address, amount)) } -// TODO: Implement deposit.deposit_status - #[cfg(test)] mod tests { use super::*; diff --git a/src/storage.rs b/src/storage.rs index 0571c46..67547dd 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -25,7 +25,7 @@ pub fn store_key( let storage_dir = get_storage_dir()?; fs::create_dir_all(&storage_dir)?; - // Store the keypair in plaintext (TODO: implement encryption) + // Store the keypair in plaintext (see #12) let key_file = storage_dir.join(format!("key_{}.json", address)); let key_data = serde_json::json!({ "network": network.to_string(), diff --git a/src/withdrawal.rs b/src/withdrawal.rs index 4ecae63..2793a61 100644 --- a/src/withdrawal.rs +++ b/src/withdrawal.rs @@ -376,7 +376,3 @@ pub async fn send_safe_withdrawal( Ok(()) } - -// TODO: Implement withdrawal.status -// TODO: Implement withdrawal.generate_operator_withdrawal_signatures -// TODO: Implement withdrawal.send_withdrawal_signatures_to_operators From 0b70a38212917d03e685282e5e0d69f172b09a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Tue, 12 Aug 2025 11:50:13 +0300 Subject: [PATCH 6/9] fix: Clippy. --- src/withdrawal.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/withdrawal.rs b/src/withdrawal.rs index 2793a61..2b86af1 100644 --- a/src/withdrawal.rs +++ b/src/withdrawal.rs @@ -185,11 +185,9 @@ pub async fn get_tx_details( bitcoin_rpc_password: Option<&str>, network: Network, ) -> Result<(Transaction, Block, u32), Box> { - if bitcoin_rpc_url.is_some() && bitcoin_rpc_user.is_some() && bitcoin_rpc_password.is_some() { - let bitcoin_rpc_url = bitcoin_rpc_url.unwrap(); - let bitcoin_rpc_user = bitcoin_rpc_user.unwrap(); - let bitcoin_rpc_password = bitcoin_rpc_password.unwrap(); - + if let (Some(bitcoin_rpc_url), Some(bitcoin_rpc_user), Some(bitcoin_rpc_password)) = + (bitcoin_rpc_url, bitcoin_rpc_user, bitcoin_rpc_password) + { let tx_details = get_tx_details_from_rpc( bitcoin_rpc_url, bitcoin_rpc_user, @@ -199,6 +197,7 @@ pub async fn get_tx_details( .await?; return Ok(tx_details); } + get_tx_details_from_mempool(prepare_txid, network).await } From 3fbccad7b45a6b66e8f44b10aedc8bb65dbffdb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Tue, 12 Aug 2025 11:53:30 +0300 Subject: [PATCH 7/9] chore: Convert todo to issue. --- src/script.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script.rs b/src/script.rs index dd8b351..f5fe618 100644 --- a/src/script.rs +++ b/src/script.rs @@ -26,7 +26,7 @@ pub fn deposit_script(evm_address: EVMAddress, nofn_xonly_pk: XOnlyPublicKey) -> .push_opcode(OP_IF) .push_slice(citrea) .push_slice(evm_address.0) - .push_slice(PushBytesBuf::try_from(hex::decode("000000003b9aca00").unwrap()).unwrap()) // TODO: Remove this. + .push_slice(PushBytesBuf::try_from(hex::decode("000000003b9aca00").unwrap()).unwrap()) // #22 .push_opcode(OP_ENDIF) .into_script() } From db5147faf7f0b0f27b8528a92a5eafa669fbbaf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Tue, 12 Aug 2025 14:48:25 +0300 Subject: [PATCH 8/9] chore: vers --- .github/workflows/code_checks.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index b119148..0b11948 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -32,6 +32,8 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Run Cargo version + run: cargo clippy --version && cargo --version && rustc --version - name: Run Cargo clippy run: cargo clippy --no-deps --all-targets --all-features -- -Dwarnings From 33c9dbcc06dc7bd1e21bf331b8b852a79e46bc25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Tue, 12 Aug 2025 14:55:30 +0300 Subject: [PATCH 9/9] fix: Specify toolchain. --- .github/workflows/code_checks.yml | 4 ++-- rust-toolchain.toml | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 rust-toolchain.toml diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index 0b11948..f1c8029 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -32,8 +32,8 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Run Cargo version - run: cargo clippy --version && cargo --version && rustc --version + - name: Install Clippy + run: rustup component add --toolchain 1.89-x86_64-unknown-linux-gnu clippy - name: Run Cargo clippy run: cargo clippy --no-deps --all-targets --all-features -- -Dwarnings diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..299adba --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.89" +components = ["rustfmt", "rust-src"] +profile = "minimal"