From 7caf15dc507b49b25f7a66cb7972de4b1fb485fd Mon Sep 17 00:00:00 2001 From: lunar-mining Date: Mon, 13 Sep 2021 09:52:10 +0200 Subject: [PATCH] deleted old client and adapter dirs, made singular and updated Cargo.toml --- Cargo.toml | 20 - src/client/client_old.rs | 415 ------------------ .../{adapters => adapter}/client_adapter.rs | 0 src/rpc/{adapters => adapter}/mod.rs | 0 src/rpc/adapters_old/cashier_adapter.rs | 78 ---- src/rpc/adapters_old/mod.rs | 4 - src/rpc/adapters_old/user_adapter.rs | 245 ----------- 7 files changed, 762 deletions(-) delete mode 100644 src/client/client_old.rs rename src/rpc/{adapters => adapter}/client_adapter.rs (100%) rename src/rpc/{adapters => adapter}/mod.rs (100%) delete mode 100644 src/rpc/adapters_old/cashier_adapter.rs delete mode 100644 src/rpc/adapters_old/mod.rs delete mode 100644 src/rpc/adapters_old/user_adapter.rs diff --git a/Cargo.toml b/Cargo.toml index 602f0f4c1..2cfedcc1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,26 +101,6 @@ features = ["bundled", "sqlcipher"] default = ["bitcoin", "secp256k1", "electrum-client"] sol = ["solana-sdk", "solana-client", "tokio-tungstenite", "tokio", "ed25519-dalek" ] -[[bin]] -name = "lisp" -path = "lisp/lisp.rs" - -[[bin]] -name = "mimc" -path = "src/old/mimc.rs" - -[[bin]] -name = "mint-classic" -path = "src/bin/mint-classic.rs" - -[[bin]] -name = "spend-classic" -path = "src/bin/spend-classic.rs" - -[[bin]] -name = "tx" -path = "src/bin/tx.rs" - [[bin]] name = "gatewayd" path = "src/bin/gatewayd.rs" diff --git a/src/client/client_old.rs b/src/client/client_old.rs deleted file mode 100644 index 50951a9da..000000000 --- a/src/client/client_old.rs +++ /dev/null @@ -1,415 +0,0 @@ -use crate::blockchain::{rocks::columns, Rocks, RocksColumn, Slab}; -use crate::cli::TransferParams; -use crate::crypto::{ - load_params, - merkle::{CommitmentTree, IncrementalWitness}, - merkle_node::MerkleNode, - note::{EncryptedNote, Note}, - nullifier::Nullifier, - save_params, setup_mint_prover, setup_spend_prover, -}; -use crate::rpc::adapters::user_adapter::UserAdapter; -use crate::rpc::jsonserver; -use crate::serial::Encodable; -use crate::serial::{deserialize, Decodable}; -use crate::service::{CashierClient, GatewayClient, GatewaySlabsSubscriber}; -use crate::state::{state_transition, ProgramState, StateUpdate}; -use crate::wallet::WalletPtr; -use crate::{tx, Result}; - -use super::{ClientFailed, ClientResult}; - -use async_executor::Executor; -use bellman::groth16; -use bls12_381::Bls12; -use log::*; -use rusqlite::Connection; - -use async_std::sync::{Arc, Mutex}; -use futures::FutureExt; -use std::net::SocketAddr; -use std::path::PathBuf; - -pub struct Client { - state: State, - secret: jubjub::Fr, - mint_params: bellman::groth16::Parameters, - spend_params: bellman::groth16::Parameters, - gateway: GatewayClient, -} - -impl Client { - pub fn new( - secret: jubjub::Fr, - rocks: Arc, - gateway_addrs: (SocketAddr, SocketAddr), - params_paths: (PathBuf, PathBuf), - wallet_path: PathBuf, - ) -> Result { - let slabstore = RocksColumn::::new(rocks.clone()); - let merkle_roots = RocksColumn::::new(rocks.clone()); - let nullifiers = RocksColumn::::new(rocks); - - let mint_params_path = params_paths.0.to_str().unwrap_or("mint.params"); - let spend_params_path = params_paths.1.to_str().unwrap_or("spend.params"); - - // Auto create trusted ceremony parameters if they don't exist - if !params_paths.0.exists() { - let params = setup_mint_prover(); - save_params(mint_params_path, ¶ms)?; - } - if !params_paths.1.exists() { - let params = setup_spend_prover(); - save_params(spend_params_path, ¶ms)?; - } - - // Load trusted setup parameters - let (mint_params, mint_pvk) = load_params(mint_params_path)?; - let (spend_params, spend_pvk) = load_params(spend_params_path)?; - - let state = State { - tree: CommitmentTree::empty(), - merkle_roots, - nullifiers, - mint_pvk, - spend_pvk, - wallet_path, - }; - - // create gateway client - debug!(target: "CLIENT", "Creating GatewayClient"); - let gateway = GatewayClient::new(gateway_addrs.0, gateway_addrs.1, slabstore)?; - - Ok(Self { - state, - secret, - mint_params, - spend_params, - gateway, - }) - } - - pub async fn start(&mut self) -> Result<()> { - self.gateway.start().await?; - Ok(()) - } - - pub async fn connect_to_cashier( - &mut self, - executor: Arc>, - wallet: WalletPtr, - cashier_addr: SocketAddr, - rpc_url: SocketAddr, - ) -> Result<()> { - // create cashier client - debug!(target: "CLIENT", "Creating cashier client"); - let mut cashier_client = CashierClient::new(cashier_addr)?; - - // start subscribing - debug!(target: "CLIENT", "Start subscriber"); - let gateway_slabs_sub: GatewaySlabsSubscriber = - self.gateway.start_subscriber(executor.clone()).await?; - - // channels to request transfer from adapter - let (transfer_req_send, transfer_req_recv) = async_channel::unbounded::(); - let (transfer_rep_send, transfer_rep_recv) = async_channel::unbounded::>(); - - // channels to request deposit from adapter, send DRK key and receive BTC key - let (deposit_req_send, deposit_req_recv) = - async_channel::unbounded::(); - let (deposit_rep_send, deposit_rep_recv) = - async_channel::unbounded::>(); - - // channel to request withdraw from adapter, send BTC key and receive DRK key - let (withdraw_req_send, withdraw_req_recv) = async_channel::unbounded::(); - let (withdraw_rep_send, withdraw_rep_recv) = - async_channel::unbounded::>(); - - // start cashier_client - cashier_client.start().await?; - - let adapter = Arc::new(UserAdapter::new( - wallet.clone(), - (transfer_req_send.clone(), transfer_rep_recv.clone()), - (deposit_req_send.clone(), deposit_rep_recv.clone()), - (withdraw_req_send.clone(), withdraw_rep_recv.clone()), - )?); - - // start the rpc server - debug!(target: "CLIENT", "Start RPC server"); - let io = Arc::new(adapter.handle_input()?); - let _ = jsonserver::start(executor.clone(), rpc_url, io).await?; - - self.futures_broker( - &mut cashier_client, - wallet, - gateway_slabs_sub.clone(), - deposit_req_recv.clone(), - deposit_rep_send.clone(), - withdraw_req_recv.clone(), - withdraw_rep_send.clone(), - transfer_req_recv.clone(), - transfer_rep_send.clone(), - ) - .await?; - - Ok(()) - } - - pub async fn futures_broker( - &mut self, - cashier_client: &mut CashierClient, - wallet: WalletPtr, - gateway_slabs_sub: async_channel::Receiver, - deposit_req: async_channel::Receiver, - deposit_rep: async_channel::Sender>, - withdraw_req: async_channel::Receiver, - withdraw_rep: async_channel::Sender>, - transfer_req: async_channel::Receiver, - transfer_rep: async_channel::Sender>, - ) -> Result<()> { - loop { - futures::select! { - slab = gateway_slabs_sub.recv().fuse() => { - let slab = slab?; - let tx = tx::Transaction::decode(&slab.get_payload()[..])?; - let update = state_transition(&self.state, tx)?; - self.state.apply(update, wallet.clone()).await?; - } - deposit_addr = deposit_req.recv().fuse() => { - let btc_public = cashier_client.get_address(deposit_addr?).await.map_err(|err| {ClientFailed::from(err)}); - - if let Err(err) = btc_public { - deposit_rep.send(Err(err)).await?; - } else { - if let Some(btc_addr) = btc_public? { - deposit_rep.send(Ok(btc_addr)).await?; - }else { - deposit_rep.send(Err(ClientFailed::UnableToGetDepositAddress)).await?; - } - } - } - withdraw_addr = withdraw_req.recv().fuse() => { - let drk_public = cashier_client.withdraw(withdraw_addr?).await.map_err(|err| {ClientFailed::from(err)}); - - if let Err(err) = drk_public { - withdraw_rep.send(Err(err)).await?; - } else { - if let Some(drk_addr) = drk_public? { - withdraw_rep.send(Ok(drk_addr)).await?; - }else { - withdraw_rep.send(Err(ClientFailed::UnableToGetWithdrawAddress)).await?; - } - } - } - transfer_params = transfer_req.recv().fuse() => { - - let result = self.transfer( - transfer_params?, - wallet.clone() - ).await; - - if let Err(err) = result { - transfer_rep.send(Err(err)).await?; - } else { - transfer_rep.send(Ok(())).await?; - } - - } - - } - } - } - - pub async fn transfer( - &mut self, - transfer_params: TransferParams, - wallet: WalletPtr, - ) -> ClientResult<()> { - let pub_key = transfer_params.pub_key; - - let address = bs58::decode(pub_key.clone()) - .into_vec() - .map_err(|_| ClientFailed::UnvalidAddress(pub_key.clone()))?; - - let address: jubjub::SubgroupPoint = - deserialize(&address).map_err(|_| ClientFailed::UnvalidAddress(pub_key))?; - - let amount = transfer_params.amount; - - if amount <= 0.0 { - return Err(ClientFailed::UnvalidAmount(amount as u64)); - } - - // check if there are coins - let own_coins = wallet.get_own_coins()?; - - if own_coins.is_empty() { - return Err(ClientFailed::NotEnoughValue(0)); - } - - let witness = &own_coins[0].3; - let merkle_path = witness.path().unwrap(); - - // Construct a new tx spending the coin - let builder = tx::TransactionBuilder { - clear_inputs: vec![], - inputs: vec![tx::TransactionBuilderInputInfo { - merkle_path, - secret: self.secret.clone(), - note: own_coins[0].1.clone(), - }], - // We can add more outputs to this list. - // The only constraint is that sum(value in) == sum(value out) - outputs: vec![tx::TransactionBuilderOutputInfo { - value: amount as u64, - asset_id: 1, - public: address, - }], - }; - // Build the tx - let mut tx_data = vec![]; - { - let tx = builder.build(&self.mint_params, &self.spend_params); - tx.encode(&mut tx_data).expect("encode tx"); - } - - // build slab from the transaction - let slab = Slab::new(tx_data); - - self.gateway.put_slab(slab).await?; - - Ok(()) - } - - pub async fn connect_to_subscriber( - client: Arc>, - executor: Arc>, - wallet: WalletPtr, - ) -> Result<()> { - // start subscribing - debug!(target: "CLIENT", "Start subscriber"); - let gateway_slabs_sub: GatewaySlabsSubscriber = client - .lock() - .await - .gateway - .start_subscriber(executor.clone()) - .await?; - - loop { - let slab = gateway_slabs_sub.recv().await?; - let tx = tx::Transaction::decode(&slab.get_payload()[..])?; - let mut client = client.lock().await; - let update = state_transition(&client.state, tx)?; - client.state.apply(update, wallet.clone()).await?; - } - } -} - -pub struct State { - // The entire merkle tree state - pub tree: CommitmentTree, - // List of all previous and the current merkle roots - // This is the hashed value of all the children. - pub merkle_roots: RocksColumn, - // Nullifiers prevent double spending - pub nullifiers: RocksColumn, - // Mint verifying key used by ZK - pub mint_pvk: groth16::PreparedVerifyingKey, - // Spend verifying key used by ZK - pub spend_pvk: groth16::PreparedVerifyingKey, - // TODO: remove this - wallet_path: PathBuf, -} - -impl ProgramState for State { - fn is_valid_cashier_public_key(&self, _public: &jubjub::SubgroupPoint) -> bool { - // TODO: use walletdb instead of connecting with sqlite directly - let conn = - Connection::open(self.wallet_path.clone()).expect("Connect to database"); - let mut stmt = conn - .prepare("SELECT key_public FROM cashier WHERE key_public IN (SELECT key_public)") - .expect("Generate statement"); - stmt.exists([1i32]).expect("Read database") - // do actual validity check - } - - fn is_valid_merkle(&self, merkle_root: &MerkleNode) -> bool { - self.merkle_roots - .key_exist(*merkle_root) - .expect("Check if the merkle_root valid") - } - - fn nullifier_exists(&self, nullifier: &Nullifier) -> bool { - self.nullifiers - .key_exist(nullifier.repr) - .expect("Check if nullifier exists") - } - - // load from disk - fn mint_pvk(&self) -> &groth16::PreparedVerifyingKey { - &self.mint_pvk - } - - fn spend_pvk(&self) -> &groth16::PreparedVerifyingKey { - &self.spend_pvk - } -} - -impl State { - pub async fn apply(&mut self, update: StateUpdate, wallet: WalletPtr) -> Result<()> { - // Extend our list of nullifiers with the ones from the update - for nullifier in update.nullifiers { - self.nullifiers.put(nullifier, vec![] as Vec)?; - } - - // Update merkle tree and witnesses - for (coin, enc_note) in update.coins.into_iter().zip(update.enc_notes.into_iter()) { - // Add the new coins to the merkle tree - let node = MerkleNode::from_coin(&coin); - self.tree.append(node).expect("Append to merkle tree"); - - // Keep track of all merkle roots that have existed - self.merkle_roots.put(self.tree.root(), vec![] as Vec)?; - - // Also update all the coin witnesses - for witness in wallet.witnesses.lock().await.iter_mut() { - witness.append(node).expect("Append to witness"); - } - - if let Some((note, secret)) = self.try_decrypt_note(wallet.clone(), enc_note).await { - // We need to keep track of the witness for this coin. - // This allows us to prove inclusion of the coin in the merkle tree with ZK. - // Just as we update the merkle tree with every new coin, so we do the same with - // the witness. - - // Derive the current witness from the current tree. - // This is done right after we add our coin to the tree (but before any other - // coins are added) - - // Make a new witness for this coin - let witness = IncrementalWitness::from_tree(&self.tree); - - wallet.put_own_coins(coin.clone(), note.clone(), witness.clone(), secret)?; - } - } - Ok(()) - } - - async fn try_decrypt_note( - &self, - wallet: WalletPtr, - ciphertext: EncryptedNote, - ) -> Option<(Note, jubjub::Fr)> { - let secret = wallet.get_private().ok()?; - match ciphertext.decrypt(&secret) { - Ok(note) => { - // ... and return the decrypted note for this coin. - return Some((note, secret.clone())); - } - Err(_) => {} - } - // We weren't able to decrypt the note with our key. - None - } -} diff --git a/src/rpc/adapters/client_adapter.rs b/src/rpc/adapter/client_adapter.rs similarity index 100% rename from src/rpc/adapters/client_adapter.rs rename to src/rpc/adapter/client_adapter.rs diff --git a/src/rpc/adapters/mod.rs b/src/rpc/adapter/mod.rs similarity index 100% rename from src/rpc/adapters/mod.rs rename to src/rpc/adapter/mod.rs diff --git a/src/rpc/adapters_old/cashier_adapter.rs b/src/rpc/adapters_old/cashier_adapter.rs deleted file mode 100644 index 5637590e6..000000000 --- a/src/rpc/adapters_old/cashier_adapter.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::wallet::CashierDb; -use crate::Result; -use async_std::sync::Arc; - -pub struct CashierAdapter { - pub wallet: Arc, -} - -impl CashierAdapter { - pub fn new(wallet: Arc) -> Result { - Ok(Self { wallet }) - } - - pub fn handle_input(self: Arc) -> Result { - let mut io = jsonrpc_core::IoHandler::new(); - io.add_sync_method("cashier_hello", |_| { - Ok(jsonrpc_core::Value::String("hello world!".into())) - }); - Ok(io) - // let self1 = self.clone(); - // io.add_method("get_key", move |_| { - // let self2 = self1.clone(); - // async move { - // let pub_key = self2.get_key()?; - // Ok(jsonrpc_core::Value::String(pub_key)) - // } - // }); - - // let self1 = self.clone(); - // io.add_method("get_cash_public", move |_| { - // let self2 = self1.clone(); - // async move { - // let cash_key = self2.get_cash_public()?; - // Ok(jsonrpc_core::Value::String(cash_key)) - // } - // }); - - // let self1 = self.clone(); - // io.add_method("get_info", move |_| { - // let self2 = self1.clone(); - // async move { - // self2.get_info(); - // Ok(jsonrpc_core::Value::Null) - // } - // }); - - // let self1 = self.clone(); - // io.add_method("stop", move |_| { - // let self2 = self1.clone(); - // async move { - // self2.stop(); - // Ok(jsonrpc_core::Value::Null) - // } - // }); - // let self1 = self.clone(); - - // io.add_method("create_wallet", move |_| { - // let self2 = self1.clone(); - // async move { - // self2.init_db()?; - // Ok(jsonrpc_core::Value::String( - // "wallet creation successful".into(), - // )) - // } - // }); - - // let self1 = self.clone(); - // io.add_method("key_gen", move |_| { - // let self2 = self1.clone(); - // async move { - // self2.key_gen()?; - // Ok(jsonrpc_core::Value::String( - // "key generation successful".into(), - // )) - // } - // }); - } -} diff --git a/src/rpc/adapters_old/mod.rs b/src/rpc/adapters_old/mod.rs deleted file mode 100644 index d0e38632b..000000000 --- a/src/rpc/adapters_old/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod cashier_adapter; -pub mod user_adapter; - -pub use user_adapter::{UserAdapter, UserAdapterPtr}; diff --git a/src/rpc/adapters_old/user_adapter.rs b/src/rpc/adapters_old/user_adapter.rs deleted file mode 100644 index 41011052f..000000000 --- a/src/rpc/adapters_old/user_adapter.rs +++ /dev/null @@ -1,245 +0,0 @@ -use crate::cli::{TransferParams, WithdrawParams}; -use crate::client::ClientResult; -use crate::serial::serialize; -use crate::service::btc::PubAddress; -use crate::wallet::WalletDb; -use crate::{Error, Result}; - -use log::*; - -use async_std::sync::Arc; - -pub type UserAdapterPtr = Arc; - -pub type TransferChannel = ( - async_channel::Sender, - async_channel::Receiver>, -); - -pub type DepositChannel = ( - async_channel::Sender, - async_channel::Receiver>, -); - -pub type WithdrawChannel = ( - async_channel::Sender, - async_channel::Receiver>, -); - -pub struct UserAdapter { - pub wallet: Arc, - transfer_channel: TransferChannel, - deposit_channel: DepositChannel, - withdraw_channel: WithdrawChannel, -} - -impl UserAdapter { - pub fn new( - wallet: Arc, - transfer_channel: TransferChannel, - deposit_channel: DepositChannel, - withdraw_channel: WithdrawChannel, - ) -> Result { - debug!(target: "RPC USER ADAPTER", "new() [CREATING NEW WALLET]"); - Ok(Self { - wallet, - transfer_channel, - deposit_channel, - withdraw_channel, - }) - } - - pub fn handle_input(self: Arc) -> Result { - let mut io = jsonrpc_core::IoHandler::new(); - - io.add_sync_method("say_hello", |_| { - Ok(jsonrpc_core::Value::String("hello world!".into())) - }); - - let self1 = self.clone(); - io.add_method("get_key", move |_| { - let self2 = self1.clone(); - async move { - let pub_key = self2.get_key()?; - Ok(jsonrpc_core::Value::String(pub_key)) - } - }); - - let self1 = self.clone(); - io.add_method("get_cash_public", move |_| { - let self2 = self1.clone(); - async move { - let cash_key = self2.get_cash_public()?; - Ok(jsonrpc_core::Value::String(cash_key)) - } - }); - - let self1 = self.clone(); - io.add_method("get_info", move |_| { - let self2 = self1.clone(); - async move { - self2.get_info(); - Ok(jsonrpc_core::Value::Null) - } - }); - - let self1 = self.clone(); - io.add_method("stop", move |_| { - let self2 = self1.clone(); - async move { - self2.stop(); - Ok(jsonrpc_core::Value::Null) - } - }); - let self1 = self.clone(); - - io.add_method("create_wallet", move |_| { - let self2 = self1.clone(); - async move { - self2.init_db()?; - Ok(jsonrpc_core::Value::String( - "wallet creation successful".into(), - )) - } - }); - - let self1 = self.clone(); - io.add_method("key_gen", move |_| { - let self2 = self1.clone(); - async move { - self2.key_gen()?; - Ok(jsonrpc_core::Value::String( - "key generation successful".into(), - )) - } - }); - - let self1 = self.clone(); - io.add_method("deposit", move |_| { - let self2 = self1.clone(); - async move { - let btckey = self2.deposit().await?; - Ok(jsonrpc_core::Value::String(format!("{}", btckey))) - } - }); - - let self1 = self.clone(); - io.add_method("transfer", move |params: jsonrpc_core::Params| { - let self2 = self1.clone(); - async move { - let parsed: TransferParams = params.parse().unwrap(); - let amount = parsed.amount.clone(); - let address = parsed.pub_key.clone(); - self2.transfer(parsed).await?; - Ok(jsonrpc_core::Value::String(format!( - "transfered {} DRK to {}", - amount, address - ))) - } - }); - - let self1 = self.clone(); - io.add_method("withdraw", move |params: jsonrpc_core::Params| { - let self2 = self1.clone(); - async move { - let parsed: WithdrawParams = params.parse().unwrap(); - let amount = parsed.amount; - let address = self2.withdraw(parsed).await?; - Ok(jsonrpc_core::Value::String(format!( - "sending {} dbtc to provided address for withdrawing: {} ", - amount, address - ))) - } - }); - - Ok(io) - } - - pub fn init_db(&self) -> Result<()> { - debug!(target: "RPC USER ADAPTER", "init_db() [START]"); - self.wallet.init_db()?; - Ok(()) - } - - pub fn key_gen(&self) -> Result<()> { - debug!(target: "RPC USER ADAPTER", "key_gen() [START]"); - let (public, private) = self.wallet.key_gen(); - debug!(target: "RPC USER ADAPTER", "Created keypair..."); - debug!(target: "RPC USER ADAPTER", "Attempting to write to database..."); - self.wallet.put_keypair(public, private)?; - Ok(()) - } - - pub fn get_key(&self) -> Result { - debug!(target: "RPC USER ADAPTER", "get_key() [START]"); - let key_public = self.wallet.get_public()?; - let bs58_address = bs58::encode(serialize(&key_public)).into_string(); - Ok(bs58_address) - } - - pub fn get_cash_public(&self) -> Result { - debug!(target: "RPC USER ADAPTER", "get_cash_public() [START]"); - let cashier_public = self.wallet.get_cashier_public()?; - let bs58_address = bs58::encode(serialize(&cashier_public)).into_string(); - Ok(bs58_address) - } - - pub async fn deposit(&self) -> Result { - debug!(target: "RPC USER ADAPTER", "deposit: START"); - let (public, private) = self.wallet.key_gen(); - self.wallet.put_keypair(public, private)?; - let dkey = self.wallet.get_public()?; - self.deposit_channel.0.send(dkey).await?; - self.deposit_channel - .1 - .recv() - .await? - .map_err(|err| Error::from(err)) - } - - async fn transfer(&self, transfer_params: TransferParams) -> Result<()> { - self.transfer_channel.0.send(transfer_params).await?; - - self.transfer_channel - .1 - .recv() - .await? - .map_err(|err| Error::from(err)) - } - - async fn withdraw(&self, withdraw_params: WithdrawParams) -> Result { - debug!(target: "RPC USER ADAPTER", "withdraw: START"); - self.withdraw_channel - .0 - .send(withdraw_params.pub_key) - .await?; - - // receive dbtc address - let key = self - .withdraw_channel - .1 - .recv() - .await? - .map_err(|err| Error::from(err))?; - - // transfer the dbtc - let key = bs58::encode(serialize(&key)).into_string(); - let mut transfer_params = TransferParams::new(); - transfer_params.pub_key = key.clone(); - transfer_params.amount = withdraw_params.amount; - self.transfer_channel.0.send(transfer_params).await?; - self.transfer_channel - .1 - .recv() - .await? - .map_err(|err| Error::from(err))?; - - Ok(key) - } - - pub fn get_info(&self) {} - - pub fn say_hello(&self) {} - - pub fn stop(&self) {} -}