diff --git a/.github/actions/service-action/action.yml b/.github/actions/service-action/action.yml index b06ec412..3b669b6a 100644 --- a/.github/actions/service-action/action.yml +++ b/.github/actions/service-action/action.yml @@ -23,3 +23,6 @@ runs: - name: Create funds in Bitcoin regtest shell: bash run: bitcoin-27.0/bin/bitcoin-cli -regtest -rpcuser=admin -rpcpassword=admin -rpcport=18443 generatetoaddress 101 $(bitcoin-27.0/bin/bitcoin-cli -regtest -rpcuser=admin -rpcpassword=admin -rpcport=18443 getnewaddress) + + - name: Install Protoc + uses: arduino/setup-protoc@v3 diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index bd2a0ba4..65ab0d27 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -17,6 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: ./.github/actions/service-action - name: Save build artifacts uses: Swatinem/rust-cache@v2 @@ -32,6 +33,7 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: ./.github/actions/service-action - name: Save build artifacts uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index 6f60b7a7..6872ca0a 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -73,7 +73,7 @@ jobs: - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov - name: Generate code coverage and print that to a JSON file - run: cargo llvm-cov $CARGOFLAGS --json --output-path lcov.json + run: cargo llvm-cov $CARGOFLAGS --json --output-path lcov.json --ignore-filename-regex core/src/rpc/clementine.rs # TODO: Remove ignore and test auto generated code too - name: Check coverage run: scripts/check_json_code_coverage.py lcov.json diff --git a/Cargo.toml b/Cargo.toml index c7702acd..25f3dfc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,31 +3,41 @@ resolver = "2" members = ["core"] # TODO: Add "circuits" back later [workspace.dependencies] -bitcoin = "0.32.2" -bitcoincore-rpc = "0.19.0" hex = "0.4.3" lazy_static = { version = "1.5.0", default-features = false } -sha2 = { version = "=0.10.8", default-features = false } -# risc0-zkvm = "0.21.0" serde = { version = "1.0", default-features = false } -serde_json = "1.0.127" -# byteorder = "1.5.0" -secp256k1 = { version = "0.29.0", features = ["serde"] } -crypto-bigint = { version = "=0.5.5", features = ["rand_core"] } -thiserror = "1.0.63" +serde_json = "1.0.128" +thiserror = "1.0.64" tracing = { version = "0.1.40", default-features = false } tracing-subscriber = { version = "0.3.18", features = ["json"] } -tokio = "1.39.3" jsonrpsee = "0.22.5" -async-trait = "0.1.81" -futures = "0.3.30" -clap = "4.5.16" +async-trait = "0.1.83" +clap = "4.5.20" toml = "0.8.19" sqlx = { version = "0.7.4", default-features = false } # k256 = { version = "=0.13.3", default-features = false } # risc0-build = "0.21.0" -bitcoin-mock-rpc = { git = "https://github.com/chainwayxyz/bitcoin-mock-rpc", tag = "v0.0.11" } + +# bitcoin +bitcoin = "0.32.3" +bitcoincore-rpc = "0.19.0" musig2 = { version = "0.0.11", features = ["serde"] } +secp256k1 = { version = "0.29.1", features = ["serde"] } + +# async + gRPC +tonic = { version = "0.12.3", features = [ "tls" ] } +prost = "0.13.3" +tokio = { version = "1.40.0", features = [ "full" ] } +tokio-stream = { version = "0.1.16", features = [ "sync" ] } +futures = "0.3.31" + +# Circuits +sha2 = { version = "=0.10.8", default-features = false } +crypto-bigint = { version = "=0.5.5", features = ["rand_core"] } +# risc0-zkvm = "0.21.0" + +# TODO: Make this dev-dependency (see #181) +bitcoin-mock-rpc = { git = "https://github.com/chainwayxyz/bitcoin-mock-rpc", tag = "v0.0.11" } [profile.release] lto = true diff --git a/core/Cargo.toml b/core/Cargo.toml index f90a30a5..a00e3acf 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -3,6 +3,9 @@ name = "clementine-core" version = "0.2.0" edition = "2021" +[build-dependencies] +tonic-build = "0.12" + [dependencies] # clementine-circuits = { path = "../circuits" } bitcoin = { workspace = true, features = ["rand", "bitcoinconsensus"] } @@ -29,6 +32,10 @@ sqlx = { workspace = true, features = ["runtime-tokio", "postgres", "macros"] } bitcoin-mock-rpc = { workspace = true } musig2 = { workspace = true } +tonic = { workspace = true} +prost = { workspace = true } +tokio-stream = {workspace = true } + [features] default = [] mock_rpc = [] diff --git a/core/build.rs b/core/build.rs new file mode 100644 index 00000000..ddf2eafe --- /dev/null +++ b/core/build.rs @@ -0,0 +1,54 @@ +use std::{env, path::Path, process::Command}; + +fn trim_ascii_end(s: &str) -> &str { + let trimmed_len = s + .as_bytes() + .iter() + .rposition(|&b| !b.is_ascii_whitespace()) + .map_or(0, |pos| pos + 1); + &s[..trimmed_len] +} + +fn compile_protobuf() { + // Try to set PROTOC env var if on *nix. + if let Ok(output) = Command::new("which").args(["protoc"]).output() { + // Skip compilation, if command failed. + if !output.status.success() { + return; + } + + // Set env var. + let path = String::from_utf8_lossy(&output.stdout); + env::set_var("PROTOC", trim_ascii_end(&path)); + } + + // Skip compilation if env var is not set. + if env::var("PROTOC").is_err() { + return; + }; + + let proto_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("src/rpc/"); + let protos = &["clementine.proto"]; + + let proto_files: Vec = protos + .iter() + .map(|proto| proto_root.join(proto).to_str().unwrap().to_owned()) + .collect(); + + // Tell Cargo that if a proto file changes, rerun this build script. + for pf in &proto_files { + println!("cargo:rerun-if-changed={}", pf); + } + + // Compile server and client code from proto files + tonic_build::configure() + .build_server(true) + .build_client(true) + .out_dir("./src/rpc") + .compile_protos(&proto_files, &[proto_root.to_str().unwrap()]) + .unwrap(); +} + +fn main() { + compile_protobuf(); +} diff --git a/core/src/errors.rs b/core/src/errors.rs index 1559c78f..bee9f0a8 100644 --- a/core/src/errors.rs +++ b/core/src/errors.rs @@ -90,6 +90,9 @@ pub enum BridgeError { /// JSON RPC call failed #[error("JsonRpcError: {0}")] JsonRpcError(#[from] jsonrpsee::core::client::Error), + /// RPC interface requires a parameter + #[error("RPC function field {0} is required!")] + RPCRequiredFieldError(&'static str), /// ConfigError is returned when the configuration is invalid #[error("ConfigError: {0}")] ConfigError(String), @@ -183,3 +186,9 @@ impl From for ErrorObject<'static> { ErrorObject::owned(-30000, format!("{:?}", val), Some(1)) } } + +impl From for tonic::Status { + fn from(val: BridgeError) -> Self { + tonic::Status::from_error(Box::new(val)) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index 0c9a0316..7a4ac067 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -24,11 +24,13 @@ pub mod merkle; pub mod mock; pub mod musig2; pub mod operator; +pub mod rpc; pub mod servers; pub mod traits; pub mod user; pub mod utils; pub mod verifier; +pub mod watchtower; pub type ConnectorUTXOTree = Vec>; // pub type HashTree = Vec>; diff --git a/core/src/operator.rs b/core/src/operator.rs index 840e88b5..774ae593 100644 --- a/core/src/operator.rs +++ b/core/src/operator.rs @@ -476,15 +476,25 @@ where Ok(self.rpc.send_raw_transaction(&signed_tx)?) } - #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] - async fn withdrawal_proved_on_citrea( + /// Checks Citrea if a withdrawal is finalized. + /// + /// Calls `withdrawFillers(withdrawal_idx)` to check the returned id is our + /// operator's id. Then calculates `move_txid` and calls + /// `txIdToDepositId(move_txid)` to check if returned id is + /// `withdrawal_idx`. + pub async fn check_citrea_for_withdrawal( &self, withdrawal_idx: u32, deposit_outpoint: OutPoint, - ) -> Result, BridgeError> { - // call withdrawFillers(withdrawal_idx) check the returned id is our operator id. - // calculate the move_txid, txIdToDepositId(move_txid) check the returned id is withdrawal_idx - if let Some(citrea_client) = &self.citrea_client { + ) -> Result<(), BridgeError> { + // Don't check anything if Citrea client is not specified. + let citrea_client = match &self.citrea_client { + Some(c) => c, + None => return Ok(()), + }; + + // Check for operator id. + { // See: https://gist.github.com/okkothejawa/a9379b02a16dada07a2b85cbbd3c1e80 let params = rpc_params![ json!({ @@ -505,22 +515,23 @@ where self.idx, )); } + } - // Calculate move_txid. - let move_tx = builder::transaction::create_move_tx( + // Check for withdrawal idx. + { + let move_txid = builder::transaction::create_move_tx( deposit_outpoint, self.nofn_xonly_pk, self.config.bridge_amount_sats, self.config.network, - ); - let move_txid = move_tx.compute_txid(); - let move_txid_bytes = move_txid.to_byte_array(); + ) + .compute_txid(); // See: https://gist.github.com/okkothejawa/a9379b02a16dada07a2b85cbbd3c1e80 let params = rpc_params![json!({ "to": "0x3100000000000000000000000000000000000002", "data": format!("0x11e53a01{}", - hex::encode(move_txid_bytes)), + hex::encode(move_txid.to_byte_array())), })]; let response: String = citrea_client.request("eth_call", params).await?; @@ -536,6 +547,18 @@ where } } + Ok(()) + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + pub(crate) async fn withdrawal_proved_on_citrea( + &self, + withdrawal_idx: u32, + deposit_outpoint: OutPoint, + ) -> Result, BridgeError> { + self.check_citrea_for_withdrawal(withdrawal_idx, deposit_outpoint) + .await?; + let kickoff_utxo = self .db .get_kickoff_utxo(None, deposit_outpoint) diff --git a/core/src/rpc/aggregator.rs b/core/src/rpc/aggregator.rs new file mode 100644 index 00000000..d2be7b95 --- /dev/null +++ b/core/src/rpc/aggregator.rs @@ -0,0 +1,21 @@ +use super::clementine::{ + clementine_aggregator_server::ClementineAggregator, DepositParams, Empty, RawSignedMoveTx, +}; +use crate::aggregator::Aggregator; +use tonic::{async_trait, Request, Response, Status}; + +#[async_trait] +impl ClementineAggregator for Aggregator { + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn setup(&self, _request: Request) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn new_deposit( + &self, + _request: Request, + ) -> Result, Status> { + todo!() + } +} diff --git a/core/src/rpc/clementine.proto b/core/src/rpc/clementine.proto new file mode 100644 index 00000000..57880cd0 --- /dev/null +++ b/core/src/rpc/clementine.proto @@ -0,0 +1,242 @@ +syntax = "proto3"; +package clementine; + +message Empty {} + +message Outpoint { + bytes txid = 1; + uint32 vout = 2; +} + +message AssertEmptyPublicKey { + bytes hash = 1; +} + +message WinternitzPubkey { + uint32 d = 1; + uint32 n0 = 2; + repeated bytes digit_pubkey = 3; +} + +// A deposit request's details. +message DepositParams { + // User's deposit UTXO. + Outpoint deposit_outpoint = 1; + // User's EVM address. + bytes evm_address = 2; + // User's recovery taproot address. + string recovery_taproot_address = 3; + // User can take back funds after this amount of blocks. + uint64 user_takes_after = 4; +} +message DepositSignSession { + DepositParams deposit_params = 1; + repeated NonceGenFirstResponse nonce_gen_first_responses = 2; +} + +// Operator -------------------------------------------------------------------- + +message OperatorConfig { + uint32 operator_id = 1; + Outpoint initial_time_tx_outpoint = 2; + string xonly_pk = 3; + string wallet_reimburse_address = 4; +} +message OperatorParams { + // Operator's configuration. + OperatorConfig operator_details = 1; + // Winternitz pubkeys for each watchtowers challenge + bitvm assert tx. + // If there are 100 watchtowers and total of 1000 timetxs, it will take + // 1000 * (100*240 + 600*20) ~= 1 GB of hash for every winternitz pubkey. + repeated WinternitzPubkey winternitz_pubkeys = 2; + // Adaptor signatures for asserting a watchtower's challenge to zero. + // Total of 1000*100 preimages. + repeated AssertEmptyPublicKey assert_empty_public_key = 3; + // Timeout transaction signatures. It is not desired to have N-of-N in time + // txs. So, operator will sign the timeout txs. Takes total of 1000 schnorr + // sigs. + repeated bytes timeout_tx_sigs = 4; +} + +message OperatorBurnSig { + bytes schnorr_sig = 1; +} + +message NewWithdrawalSigParams { + uint32 withdrawal_id = 1; + // User's [`bitcoin::sighash::TapSighashType::SinglePlusAnyoneCanPay`] + // signature + bytes user_sig = 2; + Outpoint users_intent_outpoint = 3; + bytes users_intent_script_pubkey = 4; + uint64 users_intent_amount = 5; + bytes output_script_pubkey = 6; + uint64 output_amount = 7; +} +message NewWithdrawalSigResponse { + bytes txid = 1; +} + +message WithdrawalFinalizedParams { + uint32 withdrawal_id = 1; + Outpoint deposit_outpoint = 2; +} + +// An operator is responsible for paying withdrawals. It has an unique ID and +// chain of UTXOs named `time_txs`. An operator also runs a verifier. These are +// connected to the same database and both have access to watchtowers' +// winternitz pubkeys. +service ClementineOperator { + // Returns an operator's parameters. It will be called once, by the + // aggregator, to set all the public keys. + // + // # Returns + // + // Returns an [`OperatorParams`], which includes operator's configuration and + // Watchtower parameters. + rpc GetParams(Empty) returns (OperatorParams) {} + + // Signs everything that includes Operator's burn connector. + // + // # Parameters + // + // - User's deposit information + // - Nonce metadata + // + // # Returns + // + // - Operator burn Schnorr signature + rpc DepositSign(DepositSignSession) returns (stream OperatorBurnSig) {} + + // Prepares a withdrawal if it's profitable and previous time_tx's timelock + // has ended, by paying for the withdrawal and locking the current time_tx. + rpc NewWithdrawalSig(NewWithdrawalSigParams) + returns (NewWithdrawalSigResponse) {} + + // Checks if a withdrawal is finalized. + // + // Steps: + // + // 1. Calculate move_txid and check if the withdrawal idx matches it + // 2. Check if it is proved on citrea + // 3. Send operator_take_txs + // + // # Parameters + // + // - withdrawal_id: Withdrawal's ID + // - deposit_outpoint: Withdrawal's deposit UTXO + rpc WithdrawalFinalized(WithdrawalFinalizedParams) returns (Empty) {} +} + +// Verifier -------------------------------------------------------------------- + +message VerifierParams { + uint32 id = 1; + bytes public_key = 2; + uint32 num_verifiers = 3; + uint32 num_watchtowers = 4; + uint32 num_operators = 5; + uint32 num_time_txs = 6; +} + +message PartialSig { + bytes partial_sig = 1; +} + +message NonceGenFirstResponse { + // Nonce ID + uint32 id = 1; + // New public key for the deposit + bytes public_key = 2; + // They sign the new public key with their private key + bytes sig = 3; + // Number of nonces to generate + uint32 num_nonces = 4; +} +message NonceGenResponse { + oneof response { + NonceGenFirstResponse first_response = 1; + bytes pub_nonce = 2; + } +} + +message VerifierDepositSignParams { + oneof params { + DepositSignSession deposit_sign_first_param = 1; + bytes agg_nonce = 2; + } +} + +message VerifierDepositFinalizeParams { + oneof params { + DepositSignSession deposit_sign_first_param = 1; + bytes schnorr_sig = 2; + } +} + +message VerifierPublicKeys { + repeated string verifier_public_keys = 1; +} + +service ClementineVerifier { + // Returns verifiers' metadata. Needs to be called once per setup. + rpc GetParams(Empty) returns (VerifierParams) {} + + // Saves all verifiers public keys. + rpc SetVerifiers(VerifierPublicKeys) returns (Empty) {} + + // Saves an operator. + rpc SetOperator(OperatorParams) returns (Empty) {} + + // Saves a watchtower. + rpc SetWatchtower(WatchtowerParams) returns (Empty) {} + + // Generates nonces for a deposit. + // + // # Returns + // + // Nonce metadata followed by nonces. + rpc NonceGen(Empty) returns (stream NonceGenResponse) {} + + // Signs deposit with given aggNonces and it's corresponding secNonce using + // nonce_id. + rpc DepositSign(stream VerifierDepositSignParams) + returns (stream PartialSig) {} + + // Verifies every signature and signs move_tx. + rpc DepositFinalize(stream VerifierDepositFinalizeParams) + returns (PartialSig) {} +} + +// Watchtower ------------------------------------------------------------------ + +message WatchtowerParams { + uint32 watchtower_id = 1; + // Winternitz pubkeys for each operator's timetxs. + repeated WinternitzPubkey winternitz_pubkeys = 2; +} + +// Watchtowers are responsible for challenging the operator's kickoff txs. +// Each watchtower also runs a verifier server connected to the same db. Thus, +// they will have the operator's winternitz pubkeys. +service ClementineWatchtower { + // Returns every operator's winternitz public keys. + rpc GetParams(Empty) returns (WatchtowerParams) {} +} + +// Aggregator ------------------------------------------------------------------ + +message RawSignedMoveTx { + bytes raw_tx = 1; +} + +service ClementineAggregator { + rpc Setup(Empty) returns (Empty) {} + // This will call, DepositNonceGen for every verifier, + // then it will aggregate one by one and then send it to DepositSign, + // then it will aggregate the partial sigs and send it to DepositFinalize, + // this will also call the operator to get their signatures and send it to + // DepositFinalize then it will collect the partial sigs and create the move + // tx. + rpc NewDeposit(DepositParams) returns (RawSignedMoveTx) {} +} diff --git a/core/src/rpc/clementine.rs b/core/src/rpc/clementine.rs new file mode 100644 index 00000000..0226ebec --- /dev/null +++ b/core/src/rpc/clementine.rs @@ -0,0 +1,2279 @@ +// This file is @generated by prost-build. +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct Empty {} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Outpoint { + #[prost(bytes = "vec", tag = "1")] + pub txid: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "2")] + pub vout: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AssertEmptyPublicKey { + #[prost(bytes = "vec", tag = "1")] + pub hash: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WinternitzPubkey { + #[prost(uint32, tag = "1")] + pub d: u32, + #[prost(uint32, tag = "2")] + pub n0: u32, + #[prost(bytes = "vec", repeated, tag = "3")] + pub digit_pubkey: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +/// A deposit request's details. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DepositParams { + /// User's deposit UTXO. + #[prost(message, optional, tag = "1")] + pub deposit_outpoint: ::core::option::Option, + /// User's EVM address. + #[prost(bytes = "vec", tag = "2")] + pub evm_address: ::prost::alloc::vec::Vec, + /// User's recovery taproot address. + #[prost(string, tag = "3")] + pub recovery_taproot_address: ::prost::alloc::string::String, + /// User can take back funds after this amount of blocks. + #[prost(uint64, tag = "4")] + pub user_takes_after: u64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DepositSignSession { + #[prost(message, optional, tag = "1")] + pub deposit_params: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub nonce_gen_first_responses: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OperatorConfig { + #[prost(uint32, tag = "1")] + pub operator_id: u32, + #[prost(message, optional, tag = "2")] + pub initial_time_tx_outpoint: ::core::option::Option, + #[prost(string, tag = "3")] + pub xonly_pk: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub wallet_reimburse_address: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OperatorParams { + /// Operator's configuration. + #[prost(message, optional, tag = "1")] + pub operator_details: ::core::option::Option, + /// Winternitz pubkeys for each watchtowers challenge + bitvm assert tx. + /// If there are 100 watchtowers and total of 1000 timetxs, it will take + /// 1000 * (100*240 + 600*20) ~= 1 GB of hash for every winternitz pubkey. + #[prost(message, repeated, tag = "2")] + pub winternitz_pubkeys: ::prost::alloc::vec::Vec, + /// Adaptor signatures for asserting a watchtower's challenge to zero. + /// Total of 1000*100 preimages. + #[prost(message, repeated, tag = "3")] + pub assert_empty_public_key: ::prost::alloc::vec::Vec, + /// Timeout transaction signatures. It is not desired to have N-of-N in time + /// txs. So, operator will sign the timeout txs. Takes total of 1000 schnorr + /// sigs. + #[prost(bytes = "vec", repeated, tag = "4")] + pub timeout_tx_sigs: ::prost::alloc::vec::Vec<::prost::alloc::vec::Vec>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OperatorBurnSig { + #[prost(bytes = "vec", tag = "1")] + pub schnorr_sig: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NewWithdrawalSigParams { + #[prost(uint32, tag = "1")] + pub withdrawal_id: u32, + /// User's \[`bitcoin::sighash::TapSighashType::SinglePlusAnyoneCanPay`\] + /// signature + #[prost(bytes = "vec", tag = "2")] + pub user_sig: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "3")] + pub users_intent_outpoint: ::core::option::Option, + #[prost(bytes = "vec", tag = "4")] + pub users_intent_script_pubkey: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "5")] + pub users_intent_amount: u64, + #[prost(bytes = "vec", tag = "6")] + pub output_script_pubkey: ::prost::alloc::vec::Vec, + #[prost(uint64, tag = "7")] + pub output_amount: u64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NewWithdrawalSigResponse { + #[prost(bytes = "vec", tag = "1")] + pub txid: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WithdrawalFinalizedParams { + #[prost(uint32, tag = "1")] + pub withdrawal_id: u32, + #[prost(message, optional, tag = "2")] + pub deposit_outpoint: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VerifierParams { + #[prost(uint32, tag = "1")] + pub id: u32, + #[prost(bytes = "vec", tag = "2")] + pub public_key: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "3")] + pub num_verifiers: u32, + #[prost(uint32, tag = "4")] + pub num_watchtowers: u32, + #[prost(uint32, tag = "5")] + pub num_operators: u32, + #[prost(uint32, tag = "6")] + pub num_time_txs: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PartialSig { + #[prost(bytes = "vec", tag = "1")] + pub partial_sig: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NonceGenFirstResponse { + /// Nonce ID + #[prost(uint32, tag = "1")] + pub id: u32, + /// New public key for the deposit + #[prost(bytes = "vec", tag = "2")] + pub public_key: ::prost::alloc::vec::Vec, + /// They sign the new public key with their private key + #[prost(bytes = "vec", tag = "3")] + pub sig: ::prost::alloc::vec::Vec, + /// Number of nonces to generate + #[prost(uint32, tag = "4")] + pub num_nonces: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NonceGenResponse { + #[prost(oneof = "nonce_gen_response::Response", tags = "1, 2")] + pub response: ::core::option::Option, +} +/// Nested message and enum types in `NonceGenResponse`. +pub mod nonce_gen_response { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Response { + #[prost(message, tag = "1")] + FirstResponse(super::NonceGenFirstResponse), + #[prost(bytes, tag = "2")] + PubNonce(::prost::alloc::vec::Vec), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VerifierDepositSignParams { + #[prost(oneof = "verifier_deposit_sign_params::Params", tags = "1, 2")] + pub params: ::core::option::Option, +} +/// Nested message and enum types in `VerifierDepositSignParams`. +pub mod verifier_deposit_sign_params { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Params { + #[prost(message, tag = "1")] + DepositSignFirstParam(super::DepositSignSession), + #[prost(bytes, tag = "2")] + AggNonce(::prost::alloc::vec::Vec), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VerifierDepositFinalizeParams { + #[prost(oneof = "verifier_deposit_finalize_params::Params", tags = "1, 2")] + pub params: ::core::option::Option, +} +/// Nested message and enum types in `VerifierDepositFinalizeParams`. +pub mod verifier_deposit_finalize_params { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Params { + #[prost(message, tag = "1")] + DepositSignFirstParam(super::DepositSignSession), + #[prost(bytes, tag = "2")] + SchnorrSig(::prost::alloc::vec::Vec), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VerifierPublicKeys { + #[prost(string, repeated, tag = "1")] + pub verifier_public_keys: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WatchtowerParams { + #[prost(uint32, tag = "1")] + pub watchtower_id: u32, + /// Winternitz pubkeys for each operator's timetxs. + #[prost(message, repeated, tag = "2")] + pub winternitz_pubkeys: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RawSignedMoveTx { + #[prost(bytes = "vec", tag = "1")] + pub raw_tx: ::prost::alloc::vec::Vec, +} +/// Generated client implementations. +pub mod clementine_operator_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// An operator is responsible for paying withdrawals. It has an unique ID and + /// chain of UTXOs named `time_txs`. An operator also runs a verifier. These are + /// connected to the same database and both have access to watchtowers' + /// winternitz pubkeys. + #[derive(Debug, Clone)] + pub struct ClementineOperatorClient { + inner: tonic::client::Grpc, + } + impl ClementineOperatorClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl ClementineOperatorClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ClementineOperatorClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + ClementineOperatorClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Returns an operator's parameters. It will be called once, by the + /// aggregator, to set all the public keys. + /// + /// # Returns + /// + /// Returns an [`OperatorParams`], which includes operator's configuration and + /// Watchtower parameters. + pub async fn get_params( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineOperator/GetParams", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("clementine.ClementineOperator", "GetParams")); + self.inner.unary(req, path, codec).await + } + /// Signs everything that includes Operator's burn connector. + /// + /// # Parameters + /// + /// - User's deposit information + /// - Nonce metadata + /// + /// # Returns + /// + /// - Operator burn Schnorr signature + pub async fn deposit_sign( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineOperator/DepositSign", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("clementine.ClementineOperator", "DepositSign")); + self.inner.server_streaming(req, path, codec).await + } + /// Prepares a withdrawal if it's profitable and previous time_tx's timelock + /// has ended, by paying for the withdrawal and locking the current time_tx. + pub async fn new_withdrawal_sig( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineOperator/NewWithdrawalSig", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("clementine.ClementineOperator", "NewWithdrawalSig"), + ); + self.inner.unary(req, path, codec).await + } + /// Checks if a withdrawal is finalized. + /// + /// Steps: + /// + /// 1. Calculate move_txid and check if the withdrawal idx matches it + /// 2. Check if it is proved on citrea + /// 3. Send operator_take_txs + /// + /// # Parameters + /// + /// - withdrawal_id: Withdrawal's ID + /// - deposit_outpoint: Withdrawal's deposit UTXO + pub async fn withdrawal_finalized( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineOperator/WithdrawalFinalized", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "clementine.ClementineOperator", + "WithdrawalFinalized", + ), + ); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated client implementations. +pub mod clementine_verifier_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + #[derive(Debug, Clone)] + pub struct ClementineVerifierClient { + inner: tonic::client::Grpc, + } + impl ClementineVerifierClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl ClementineVerifierClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ClementineVerifierClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + ClementineVerifierClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Returns verifiers' metadata. Needs to be called once per setup. + pub async fn get_params( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineVerifier/GetParams", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("clementine.ClementineVerifier", "GetParams")); + self.inner.unary(req, path, codec).await + } + /// Saves all verifiers public keys. + pub async fn set_verifiers( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineVerifier/SetVerifiers", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("clementine.ClementineVerifier", "SetVerifiers"), + ); + self.inner.unary(req, path, codec).await + } + /// Saves an operator. + pub async fn set_operator( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineVerifier/SetOperator", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("clementine.ClementineVerifier", "SetOperator")); + self.inner.unary(req, path, codec).await + } + /// Saves a watchtower. + pub async fn set_watchtower( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineVerifier/SetWatchtower", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("clementine.ClementineVerifier", "SetWatchtower"), + ); + self.inner.unary(req, path, codec).await + } + /// Generates nonces for a deposit. + /// + /// # Returns + /// + /// Nonce metadata followed by nonces. + pub async fn nonce_gen( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineVerifier/NonceGen", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("clementine.ClementineVerifier", "NonceGen")); + self.inner.server_streaming(req, path, codec).await + } + /// Signs deposit with given aggNonces and it's corresponding secNonce using + /// nonce_id. + pub async fn deposit_sign( + &mut self, + request: impl tonic::IntoStreamingRequest< + Message = super::VerifierDepositSignParams, + >, + ) -> std::result::Result< + tonic::Response>, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineVerifier/DepositSign", + ); + let mut req = request.into_streaming_request(); + req.extensions_mut() + .insert(GrpcMethod::new("clementine.ClementineVerifier", "DepositSign")); + self.inner.streaming(req, path, codec).await + } + /// Verifies every signature and signs move_tx. + pub async fn deposit_finalize( + &mut self, + request: impl tonic::IntoStreamingRequest< + Message = super::VerifierDepositFinalizeParams, + >, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineVerifier/DepositFinalize", + ); + let mut req = request.into_streaming_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("clementine.ClementineVerifier", "DepositFinalize"), + ); + self.inner.client_streaming(req, path, codec).await + } + } +} +/// Generated client implementations. +pub mod clementine_watchtower_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + /// Watchtowers are responsible for challenging the operator's kickoff txs. + /// Each watchtower also runs a verifier server connected to the same db. Thus, + /// they will have the operator's winternitz pubkeys. + #[derive(Debug, Clone)] + pub struct ClementineWatchtowerClient { + inner: tonic::client::Grpc, + } + impl ClementineWatchtowerClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl ClementineWatchtowerClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ClementineWatchtowerClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + ClementineWatchtowerClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + /// Returns every operator's winternitz public keys. + pub async fn get_params( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineWatchtower/GetParams", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("clementine.ClementineWatchtower", "GetParams")); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated client implementations. +pub mod clementine_aggregator_client { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + use tonic::codegen::http::Uri; + #[derive(Debug, Clone)] + pub struct ClementineAggregatorClient { + inner: tonic::client::Grpc, + } + impl ClementineAggregatorClient { + /// Attempt to create a new client by connecting to a given endpoint. + pub async fn connect(dst: D) -> Result + where + D: TryInto, + D::Error: Into, + { + let conn = tonic::transport::Endpoint::new(dst)?.connect().await?; + Ok(Self::new(conn)) + } + } + impl ClementineAggregatorClient + where + T: tonic::client::GrpcService, + T::Error: Into, + T::ResponseBody: Body + std::marker::Send + 'static, + ::Error: Into + std::marker::Send, + { + pub fn new(inner: T) -> Self { + let inner = tonic::client::Grpc::new(inner); + Self { inner } + } + pub fn with_origin(inner: T, origin: Uri) -> Self { + let inner = tonic::client::Grpc::with_origin(inner, origin); + Self { inner } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> ClementineAggregatorClient> + where + F: tonic::service::Interceptor, + T::ResponseBody: Default, + T: tonic::codegen::Service< + http::Request, + Response = http::Response< + >::ResponseBody, + >, + >, + , + >>::Error: Into + std::marker::Send + std::marker::Sync, + { + ClementineAggregatorClient::new(InterceptedService::new(inner, interceptor)) + } + /// Compress requests with the given encoding. + /// + /// This requires the server to support it otherwise it might respond with an + /// error. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.send_compressed(encoding); + self + } + /// Enable decompressing responses. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.inner = self.inner.accept_compressed(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_decoding_message_size(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.inner = self.inner.max_encoding_message_size(limit); + self + } + pub async fn setup( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result, tonic::Status> { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineAggregator/Setup", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert(GrpcMethod::new("clementine.ClementineAggregator", "Setup")); + self.inner.unary(req, path, codec).await + } + /// This will call, DepositNonceGen for every verifier, + /// then it will aggregate one by one and then send it to DepositSign, + /// then it will aggregate the partial sigs and send it to DepositFinalize, + /// this will also call the operator to get their signatures and send it to + /// DepositFinalize then it will collect the partial sigs and create the move + /// tx. + pub async fn new_deposit( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/clementine.ClementineAggregator/NewDeposit", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new("clementine.ClementineAggregator", "NewDeposit"), + ); + self.inner.unary(req, path, codec).await + } + } +} +/// Generated server implementations. +pub mod clementine_operator_server { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with ClementineOperatorServer. + #[async_trait] + pub trait ClementineOperator: std::marker::Send + std::marker::Sync + 'static { + /// Returns an operator's parameters. It will be called once, by the + /// aggregator, to set all the public keys. + /// + /// # Returns + /// + /// Returns an [`OperatorParams`], which includes operator's configuration and + /// Watchtower parameters. + async fn get_params( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Server streaming response type for the DepositSign method. + type DepositSignStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + std::marker::Send + + 'static; + /// Signs everything that includes Operator's burn connector. + /// + /// # Parameters + /// + /// - User's deposit information + /// - Nonce metadata + /// + /// # Returns + /// + /// - Operator burn Schnorr signature + async fn deposit_sign( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Prepares a withdrawal if it's profitable and previous time_tx's timelock + /// has ended, by paying for the withdrawal and locking the current time_tx. + async fn new_withdrawal_sig( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Checks if a withdrawal is finalized. + /// + /// Steps: + /// + /// 1. Calculate move_txid and check if the withdrawal idx matches it + /// 2. Check if it is proved on citrea + /// 3. Send operator_take_txs + /// + /// # Parameters + /// + /// - withdrawal_id: Withdrawal's ID + /// - deposit_outpoint: Withdrawal's deposit UTXO + async fn withdrawal_finalized( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + } + /// An operator is responsible for paying withdrawals. It has an unique ID and + /// chain of UTXOs named `time_txs`. An operator also runs a verifier. These are + /// connected to the same database and both have access to watchtowers' + /// winternitz pubkeys. + #[derive(Debug)] + pub struct ClementineOperatorServer { + inner: Arc, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + impl ClementineOperatorServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for ClementineOperatorServer + where + T: ClementineOperator, + B: Body + std::marker::Send + 'static, + B::Error: Into + std::marker::Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/clementine.ClementineOperator/GetParams" => { + #[allow(non_camel_case_types)] + struct GetParamsSvc(pub Arc); + impl tonic::server::UnaryService + for GetParamsSvc { + type Response = super::OperatorParams; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_params(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = GetParamsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineOperator/DepositSign" => { + #[allow(non_camel_case_types)] + struct DepositSignSvc(pub Arc); + impl< + T: ClementineOperator, + > tonic::server::ServerStreamingService + for DepositSignSvc { + type Response = super::OperatorBurnSig; + type ResponseStream = T::DepositSignStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::deposit_sign(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = DepositSignSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineOperator/NewWithdrawalSig" => { + #[allow(non_camel_case_types)] + struct NewWithdrawalSigSvc(pub Arc); + impl< + T: ClementineOperator, + > tonic::server::UnaryService + for NewWithdrawalSigSvc { + type Response = super::NewWithdrawalSigResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::new_withdrawal_sig( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = NewWithdrawalSigSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineOperator/WithdrawalFinalized" => { + #[allow(non_camel_case_types)] + struct WithdrawalFinalizedSvc(pub Arc); + impl< + T: ClementineOperator, + > tonic::server::UnaryService + for WithdrawalFinalizedSvc { + type Response = super::Empty; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::withdrawal_finalized( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = WithdrawalFinalizedSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + let mut response = http::Response::new(empty_body()); + let headers = response.headers_mut(); + headers + .insert( + tonic::Status::GRPC_STATUS, + (tonic::Code::Unimplemented as i32).into(), + ); + headers + .insert( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ); + Ok(response) + }) + } + } + } + } + impl Clone for ClementineOperatorServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + /// Generated gRPC service name + pub const SERVICE_NAME: &str = "clementine.ClementineOperator"; + impl tonic::server::NamedService for ClementineOperatorServer { + const NAME: &'static str = SERVICE_NAME; + } +} +/// Generated server implementations. +pub mod clementine_verifier_server { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with ClementineVerifierServer. + #[async_trait] + pub trait ClementineVerifier: std::marker::Send + std::marker::Sync + 'static { + /// Returns verifiers' metadata. Needs to be called once per setup. + async fn get_params( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Saves all verifiers public keys. + async fn set_verifiers( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Saves an operator. + async fn set_operator( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Saves a watchtower. + async fn set_watchtower( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Server streaming response type for the NonceGen method. + type NonceGenStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + std::marker::Send + + 'static; + /// Generates nonces for a deposit. + /// + /// # Returns + /// + /// Nonce metadata followed by nonces. + async fn nonce_gen( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// Server streaming response type for the DepositSign method. + type DepositSignStream: tonic::codegen::tokio_stream::Stream< + Item = std::result::Result, + > + + std::marker::Send + + 'static; + /// Signs deposit with given aggNonces and it's corresponding secNonce using + /// nonce_id. + async fn deposit_sign( + &self, + request: tonic::Request>, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + /// Verifies every signature and signs move_tx. + async fn deposit_finalize( + &self, + request: tonic::Request< + tonic::Streaming, + >, + ) -> std::result::Result, tonic::Status>; + } + #[derive(Debug)] + pub struct ClementineVerifierServer { + inner: Arc, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + impl ClementineVerifierServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> for ClementineVerifierServer + where + T: ClementineVerifier, + B: Body + std::marker::Send + 'static, + B::Error: Into + std::marker::Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/clementine.ClementineVerifier/GetParams" => { + #[allow(non_camel_case_types)] + struct GetParamsSvc(pub Arc); + impl tonic::server::UnaryService + for GetParamsSvc { + type Response = super::VerifierParams; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_params(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = GetParamsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineVerifier/SetVerifiers" => { + #[allow(non_camel_case_types)] + struct SetVerifiersSvc(pub Arc); + impl< + T: ClementineVerifier, + > tonic::server::UnaryService + for SetVerifiersSvc { + type Response = super::Empty; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::set_verifiers(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SetVerifiersSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineVerifier/SetOperator" => { + #[allow(non_camel_case_types)] + struct SetOperatorSvc(pub Arc); + impl< + T: ClementineVerifier, + > tonic::server::UnaryService + for SetOperatorSvc { + type Response = super::Empty; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::set_operator(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SetOperatorSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineVerifier/SetWatchtower" => { + #[allow(non_camel_case_types)] + struct SetWatchtowerSvc(pub Arc); + impl< + T: ClementineVerifier, + > tonic::server::UnaryService + for SetWatchtowerSvc { + type Response = super::Empty; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::set_watchtower(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SetWatchtowerSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineVerifier/NonceGen" => { + #[allow(non_camel_case_types)] + struct NonceGenSvc(pub Arc); + impl< + T: ClementineVerifier, + > tonic::server::ServerStreamingService + for NonceGenSvc { + type Response = super::NonceGenResponse; + type ResponseStream = T::NonceGenStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::nonce_gen(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = NonceGenSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.server_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineVerifier/DepositSign" => { + #[allow(non_camel_case_types)] + struct DepositSignSvc(pub Arc); + impl< + T: ClementineVerifier, + > tonic::server::StreamingService + for DepositSignSvc { + type Response = super::PartialSig; + type ResponseStream = T::DepositSignStream; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + tonic::Streaming, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::deposit_sign(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = DepositSignSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineVerifier/DepositFinalize" => { + #[allow(non_camel_case_types)] + struct DepositFinalizeSvc(pub Arc); + impl< + T: ClementineVerifier, + > tonic::server::ClientStreamingService< + super::VerifierDepositFinalizeParams, + > for DepositFinalizeSvc { + type Response = super::PartialSig; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + tonic::Streaming, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::deposit_finalize(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = DepositFinalizeSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.client_streaming(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + let mut response = http::Response::new(empty_body()); + let headers = response.headers_mut(); + headers + .insert( + tonic::Status::GRPC_STATUS, + (tonic::Code::Unimplemented as i32).into(), + ); + headers + .insert( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ); + Ok(response) + }) + } + } + } + } + impl Clone for ClementineVerifierServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + /// Generated gRPC service name + pub const SERVICE_NAME: &str = "clementine.ClementineVerifier"; + impl tonic::server::NamedService for ClementineVerifierServer { + const NAME: &'static str = SERVICE_NAME; + } +} +/// Generated server implementations. +pub mod clementine_watchtower_server { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with ClementineWatchtowerServer. + #[async_trait] + pub trait ClementineWatchtower: std::marker::Send + std::marker::Sync + 'static { + /// Returns every operator's winternitz public keys. + async fn get_params( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; + } + /// Watchtowers are responsible for challenging the operator's kickoff txs. + /// Each watchtower also runs a verifier server connected to the same db. Thus, + /// they will have the operator's winternitz pubkeys. + #[derive(Debug)] + pub struct ClementineWatchtowerServer { + inner: Arc, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + impl ClementineWatchtowerServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> + for ClementineWatchtowerServer + where + T: ClementineWatchtower, + B: Body + std::marker::Send + 'static, + B::Error: Into + std::marker::Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/clementine.ClementineWatchtower/GetParams" => { + #[allow(non_camel_case_types)] + struct GetParamsSvc(pub Arc); + impl< + T: ClementineWatchtower, + > tonic::server::UnaryService for GetParamsSvc { + type Response = super::WatchtowerParams; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_params(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = GetParamsSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + let mut response = http::Response::new(empty_body()); + let headers = response.headers_mut(); + headers + .insert( + tonic::Status::GRPC_STATUS, + (tonic::Code::Unimplemented as i32).into(), + ); + headers + .insert( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ); + Ok(response) + }) + } + } + } + } + impl Clone for ClementineWatchtowerServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + /// Generated gRPC service name + pub const SERVICE_NAME: &str = "clementine.ClementineWatchtower"; + impl tonic::server::NamedService for ClementineWatchtowerServer { + const NAME: &'static str = SERVICE_NAME; + } +} +/// Generated server implementations. +pub mod clementine_aggregator_server { + #![allow( + unused_variables, + dead_code, + missing_docs, + clippy::wildcard_imports, + clippy::let_unit_value, + )] + use tonic::codegen::*; + /// Generated trait containing gRPC methods that should be implemented for use with ClementineAggregatorServer. + #[async_trait] + pub trait ClementineAggregator: std::marker::Send + std::marker::Sync + 'static { + async fn setup( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + /// This will call, DepositNonceGen for every verifier, + /// then it will aggregate one by one and then send it to DepositSign, + /// then it will aggregate the partial sigs and send it to DepositFinalize, + /// this will also call the operator to get their signatures and send it to + /// DepositFinalize then it will collect the partial sigs and create the move + /// tx. + async fn new_deposit( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; + } + #[derive(Debug)] + pub struct ClementineAggregatorServer { + inner: Arc, + accept_compression_encodings: EnabledCompressionEncodings, + send_compression_encodings: EnabledCompressionEncodings, + max_decoding_message_size: Option, + max_encoding_message_size: Option, + } + impl ClementineAggregatorServer { + pub fn new(inner: T) -> Self { + Self::from_arc(Arc::new(inner)) + } + pub fn from_arc(inner: Arc) -> Self { + Self { + inner, + accept_compression_encodings: Default::default(), + send_compression_encodings: Default::default(), + max_decoding_message_size: None, + max_encoding_message_size: None, + } + } + pub fn with_interceptor( + inner: T, + interceptor: F, + ) -> InterceptedService + where + F: tonic::service::Interceptor, + { + InterceptedService::new(Self::new(inner), interceptor) + } + /// Enable decompressing requests with the given encoding. + #[must_use] + pub fn accept_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.accept_compression_encodings.enable(encoding); + self + } + /// Compress responses with the given encoding, if the client supports it. + #[must_use] + pub fn send_compressed(mut self, encoding: CompressionEncoding) -> Self { + self.send_compression_encodings.enable(encoding); + self + } + /// Limits the maximum size of a decoded message. + /// + /// Default: `4MB` + #[must_use] + pub fn max_decoding_message_size(mut self, limit: usize) -> Self { + self.max_decoding_message_size = Some(limit); + self + } + /// Limits the maximum size of an encoded message. + /// + /// Default: `usize::MAX` + #[must_use] + pub fn max_encoding_message_size(mut self, limit: usize) -> Self { + self.max_encoding_message_size = Some(limit); + self + } + } + impl tonic::codegen::Service> + for ClementineAggregatorServer + where + T: ClementineAggregator, + B: Body + std::marker::Send + 'static, + B::Error: Into + std::marker::Send + 'static, + { + type Response = http::Response; + type Error = std::convert::Infallible; + type Future = BoxFuture; + fn poll_ready( + &mut self, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(Ok(())) + } + fn call(&mut self, req: http::Request) -> Self::Future { + match req.uri().path() { + "/clementine.ClementineAggregator/Setup" => { + #[allow(non_camel_case_types)] + struct SetupSvc(pub Arc); + impl< + T: ClementineAggregator, + > tonic::server::UnaryService for SetupSvc { + type Response = super::Empty; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::setup(&inner, request).await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = SetupSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + "/clementine.ClementineAggregator/NewDeposit" => { + #[allow(non_camel_case_types)] + struct NewDepositSvc(pub Arc); + impl< + T: ClementineAggregator, + > tonic::server::UnaryService + for NewDepositSvc { + type Response = super::RawSignedMoveTx; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::new_deposit(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = NewDepositSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } + _ => { + Box::pin(async move { + let mut response = http::Response::new(empty_body()); + let headers = response.headers_mut(); + headers + .insert( + tonic::Status::GRPC_STATUS, + (tonic::Code::Unimplemented as i32).into(), + ); + headers + .insert( + http::header::CONTENT_TYPE, + tonic::metadata::GRPC_CONTENT_TYPE, + ); + Ok(response) + }) + } + } + } + } + impl Clone for ClementineAggregatorServer { + fn clone(&self) -> Self { + let inner = self.inner.clone(); + Self { + inner, + accept_compression_encodings: self.accept_compression_encodings, + send_compression_encodings: self.send_compression_encodings, + max_decoding_message_size: self.max_decoding_message_size, + max_encoding_message_size: self.max_encoding_message_size, + } + } + } + /// Generated gRPC service name + pub const SERVICE_NAME: &str = "clementine.ClementineAggregator"; + impl tonic::server::NamedService for ClementineAggregatorServer { + const NAME: &'static str = SERVICE_NAME; + } +} diff --git a/core/src/rpc/mod.rs b/core/src/rpc/mod.rs new file mode 100644 index 00000000..28de4cfc --- /dev/null +++ b/core/src/rpc/mod.rs @@ -0,0 +1,8 @@ +#[allow(clippy::all)] +#[rustfmt::skip] +pub mod clementine; +pub mod aggregator; +pub mod operator; +pub mod verifier; +pub mod watchtower; +mod wrapper; diff --git a/core/src/rpc/operator.rs b/core/src/rpc/operator.rs new file mode 100644 index 00000000..66183fc0 --- /dev/null +++ b/core/src/rpc/operator.rs @@ -0,0 +1,62 @@ +use super::clementine::{ + clementine_operator_server::ClementineOperator, DepositSignSession, Empty, + NewWithdrawalSigParams, NewWithdrawalSigResponse, OperatorBurnSig, OperatorParams, + WithdrawalFinalizedParams, +}; +use crate::{errors::BridgeError, operator::Operator}; +use bitcoin::OutPoint; +use bitcoin_mock_rpc::RpcApiWrapper; +use tokio_stream::wrappers::ReceiverStream; +use tonic::{async_trait, Request, Response, Status}; + +#[async_trait] +impl ClementineOperator for Operator +where + T: RpcApiWrapper, +{ + type DepositSignStream = ReceiverStream>; + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn get_params( + &self, + _request: Request, + ) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn deposit_sign( + &self, + _request: Request, + ) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn new_withdrawal_sig( + &self, + _: Request, + ) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn withdrawal_finalized( + &self, + request: Request, + ) -> Result, Status> { + // Decode inputs. + let withdrawal_idx: u32 = request.get_ref().withdrawal_id; + let deposit_outpoint: OutPoint = request + .get_ref() + .deposit_outpoint + .clone() + .ok_or(BridgeError::RPCRequiredFieldError("deposit_outpoint"))? + .try_into()?; + + self.withdrawal_proved_on_citrea(withdrawal_idx, deposit_outpoint) + .await?; + + Ok(Response::new(Empty {})) + } +} diff --git a/core/src/rpc/verifier.rs b/core/src/rpc/verifier.rs new file mode 100644 index 00000000..04579ef6 --- /dev/null +++ b/core/src/rpc/verifier.rs @@ -0,0 +1,71 @@ +use super::clementine::{ + clementine_verifier_server::ClementineVerifier, Empty, NonceGenResponse, OperatorParams, + PartialSig, VerifierDepositFinalizeParams, VerifierDepositSignParams, VerifierParams, + VerifierPublicKeys, WatchtowerParams, +}; +use crate::verifier::Verifier; +use bitcoin_mock_rpc::RpcApiWrapper; +use tokio_stream::wrappers::ReceiverStream; +use tonic::{async_trait, Request, Response, Status, Streaming}; + +#[async_trait] +impl ClementineVerifier for Verifier +where + T: RpcApiWrapper, +{ + type NonceGenStream = ReceiverStream>; + type DepositSignStream = ReceiverStream>; + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn get_params(&self, _: Request) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn set_verifiers( + &self, + _request: Request, + ) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn set_operator( + &self, + _request: Request, + ) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn set_watchtower( + &self, + _request: Request, + ) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn nonce_gen( + &self, + _request: Request, + ) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn deposit_sign( + &self, + _request: Request>, + ) -> Result, Status> { + todo!() + } + + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn deposit_finalize( + &self, + _request: Request>, + ) -> Result, Status> { + todo!() + } +} diff --git a/core/src/rpc/watchtower.rs b/core/src/rpc/watchtower.rs new file mode 100644 index 00000000..54400b47 --- /dev/null +++ b/core/src/rpc/watchtower.rs @@ -0,0 +1,20 @@ +use super::clementine::{ + clementine_watchtower_server::ClementineWatchtower, Empty, WatchtowerParams, +}; +use crate::watchtower::Watchtower; +use bitcoin_mock_rpc::RpcApiWrapper; +use tonic::{async_trait, Request, Response, Status}; + +#[async_trait] +impl ClementineWatchtower for Watchtower +where + T: RpcApiWrapper, +{ + #[tracing::instrument(skip(self), err(level = tracing::Level::ERROR), ret(level = tracing::Level::TRACE))] + async fn get_params( + &self, + _request: Request, + ) -> Result, Status> { + todo!() + } +} diff --git a/core/src/rpc/wrapper.rs b/core/src/rpc/wrapper.rs new file mode 100644 index 00000000..a760f963 --- /dev/null +++ b/core/src/rpc/wrapper.rs @@ -0,0 +1,73 @@ +//! # Wrapper For Converting Proto Structures + +use super::clementine::Outpoint; +use crate::errors::BridgeError; +use bitcoin::{hashes::Hash, OutPoint, Txid}; + +impl TryFrom for OutPoint { + type Error = BridgeError; + + fn try_from(value: Outpoint) -> Result { + let hash = match Hash::from_slice(&value.txid) { + Ok(h) => h, + Err(e) => return Err(BridgeError::FromSliceError(e)), + }; + + Ok(OutPoint { + txid: Txid::from_raw_hash(hash), + vout: value.vout, + }) + } +} +impl From for Outpoint { + fn from(value: OutPoint) -> Self { + Outpoint { + txid: value.txid.to_byte_array().to_vec(), + vout: value.vout, + } + } +} + +#[cfg(test)] +mod tests { + use crate::rpc::clementine::Outpoint; + use bitcoin::{hashes::Hash, OutPoint, Txid}; + + #[test] + fn from_bitcoin_outpoint_to_proto_outpoint() { + let og_outpoint = OutPoint { + txid: Txid::from_raw_hash(Hash::from_slice(&[0x1F; 32]).unwrap()), + vout: 0x45, + }; + + let proto_outpoint: Outpoint = og_outpoint.into(); + let bitcoin_outpoint: OutPoint = proto_outpoint.try_into().unwrap(); + assert_eq!(og_outpoint, bitcoin_outpoint); + + let proto_outpoint = Outpoint { + txid: vec![0x1F; 32], + vout: 0x45, + }; + let bitcoin_outpoint: OutPoint = proto_outpoint.try_into().unwrap(); + assert_eq!(og_outpoint, bitcoin_outpoint); + } + + #[test] + fn from_proto_outpoint_to_bitcoin_outpoint() { + let og_outpoint = Outpoint { + txid: vec![0x1F; 32], + vout: 0x45, + }; + + let bitcoin_outpoint: OutPoint = og_outpoint.clone().try_into().unwrap(); + let proto_outpoint: Outpoint = bitcoin_outpoint.into(); + assert_eq!(og_outpoint, proto_outpoint); + + let bitcoin_outpoint = OutPoint { + txid: Txid::from_raw_hash(Hash::from_slice(&[0x1F; 32]).unwrap()), + vout: 0x45, + }; + let proto_outpoint: Outpoint = bitcoin_outpoint.into(); + assert_eq!(og_outpoint, proto_outpoint); + } +} diff --git a/core/src/watchtower.rs b/core/src/watchtower.rs new file mode 100644 index 00000000..b1501ef9 --- /dev/null +++ b/core/src/watchtower.rs @@ -0,0 +1,11 @@ +use crate::{database::Database, extended_rpc::ExtendedRpc}; +use bitcoin_mock_rpc::RpcApiWrapper; + +#[derive(Debug, Clone)] +pub struct Watchtower +where + R: RpcApiWrapper, +{ + _rpc: ExtendedRpc, + _db: Database, +}