diff --git a/.github/workflows/check_and_lint.yaml b/.github/workflows/check_and_lint.yaml new file mode 100644 index 0000000..7794a8b --- /dev/null +++ b/.github/workflows/check_and_lint.yaml @@ -0,0 +1,50 @@ +on: [push] + +name: Check, Lint, Build + +env: + CARGO_TERM_COLOR: always + +jobs: + check-lint-build-stable: + name: Check, Lint, Build (stable) + runs-on: ubuntu-latest + timeout-minutes: 20 + # env: + # RUSTFLAGS: -D warnings + steps: + - uses: actions/checkout@v2 + - name: Install latest stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + components: rustfmt, clippy + override: true + target: x86_64-pc-windows-gnu + + - name: Rust Cache + uses: Swatinem/rust-cache@v2.5.1 + + - name: Rustfmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + - name: Cargo check + uses: actions-rs/cargo@v1 + with: + command: check + + - name: Clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + args: --all-targets --all-features + + - name: Build + uses: actions-rs/cargo@v1 + with: + command: build + args: --release \ No newline at end of file diff --git a/.github/workflows/comment_coverage.yaml b/.github/workflows/comment_coverage.yaml new file mode 100644 index 0000000..79c7446 --- /dev/null +++ b/.github/workflows/comment_coverage.yaml @@ -0,0 +1,116 @@ +# This workflow should enforce monotonically increasing comment coverage + +on: [pull_request] + +name: Comment Coverage + +#env: +# CARGO_TERM_COLOR: always + +jobs: + check-lint-build-stable: + name: Comment Coverage (stable) + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Install Protoc + uses: arduino/setup-protoc@v2 + + - name: Install latest stable toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + components: rustfmt, clippy + override: true + target: x86_64-pc-windows-gnu + + - name: Rust Cache + uses: Swatinem/rust-cache@v2.5.1 + + - name: Checkout PR branch + uses: actions/checkout@v2 + + - name: Missing docs warnings (PR) + id: missing_docs_warnings_pr + run: | + # use a random EOF, as per GitHub security recommendations + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + WARNINGS=$(\ + cargo -q clippy --message-format=short -- \ + -Aclippy::all \ + -Wclippy::missing_errors_doc \ + -Wclippy::missing_panics_doc \ + -Wclippy::missing_safety_doc \ + -Wclippy::missing_docs_in_private_items \ + -Wmissing_docs \ + 2>&1) + echo "$WARNINGS" + AWKSTR='/warning: `.+` \(lib\) generated [0-9]+ warnings?/ { print $3 ": " $7 }' + WARNINGS=$(echo "$WARNINGS" | awk -F"[\` ]" "$AWKSTR" | sort) + echo "PR_WARNINGS<<$EOF" >> "$GITHUB_OUTPUT" + echo "$WARNINGS" >> "$GITHUB_OUTPUT" + echo "$EOF" >> "$GITHUB_OUTPUT" + + - name: Checkout target branch + uses: actions/checkout@v2 + with: + ref: ${{ github.base_ref }} + + - name: Missing docs warnings (Target) + id: missing_docs_warnings_target + run: | + # use a random EOF, as per GitHub security recommendations + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + WARNINGS=$(\ + cargo -q clippy --message-format=short -- \ + -Aclippy::all \ + -Wclippy::missing_errors_doc \ + -Wclippy::missing_panics_doc \ + -Wclippy::missing_safety_doc \ + -Wclippy::missing_docs_in_private_items \ + -Wmissing_docs \ + 2>&1) + echo "$WARNINGS" + AWKSTR='/warning: `.+` \(lib\) generated [0-9]+ warnings?/ { print $3 ": " $7 }' + WARNINGS=$(echo "$WARNINGS" | awk -F"[\` ]" "$AWKSTR" | sort) + echo "TARGET_WARNINGS<<$EOF" >> "$GITHUB_OUTPUT" + echo "$WARNINGS" >> "$GITHUB_OUTPUT" + echo "$EOF" >> "$GITHUB_OUTPUT" + + - name: Compare comment coverage + run: | + PR_WARNINGS="${{steps.missing_docs_warnings_pr.outputs.PR_WARNINGS}}" + TARGET_WARNINGS="${{ steps.missing_docs_warnings_target.outputs.TARGET_WARNINGS }}" + readarray -t missing_docs_warnings_pr_arr <<< "$PR_WARNINGS" + readarray -t missing_docs_warnings_target_arr <<< "$TARGET_WARNINGS" + for pr_warnings_line in "${missing_docs_warnings_pr_arr[@]}" + do + # Extract the libname and number of warnings from the line + IFS=': ' read -r libname nwarnings_pr <<< "$pr_warnings_line" + # Look for the libname in the target warnings + target_warning_line="" + for target_warnings_line in "${missing_docs_warnings_target_arr[@]}" + do + if [[ $target_warnings_line == "$libname:"* ]]; then + target_warning_line=$target_warnings_line + break + fi + done + + if [ -z "$target_warning_line" ] + then + echo "New warnings found for \`${libname}\`" + exit 1 + fi + + # Find the number of warnings for the target branch + IFS=': ' read -r _ nwarnings_target <<< "$target_warning_line" + + # Compare the values + if [ "$nwarnings_target" -gt "$nwarnings_pr" ] + then + echo "Too many warnings for \`${libname}\` (${nwarnings_pr}): must be less than $nwarnings_target" + exit 1 + fi + done \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml index 3a26366..5878aee 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,7 @@ edition = "2021" +max_width = 80 + +# unstable features +# unstable_features = true +# reorder_impl_items = true +# group_imports = "StdExternalCrate" \ No newline at end of file diff --git a/src/archive.rs b/src/archive.rs index 297305b..7e2ad79 100644 --- a/src/archive.rs +++ b/src/archive.rs @@ -31,13 +31,21 @@ impl< }) } - pub fn get_header(&self, txn: &RoTxn, height: u32) -> Result, Error> { + pub fn get_header( + &self, + txn: &RoTxn, + height: u32, + ) -> Result, Error> { let height = height.to_be_bytes(); let header = self.headers.get(txn, &height)?; Ok(header) } - pub fn get_body(&self, txn: &RoTxn, height: u32) -> Result>, Error> { + pub fn get_body( + &self, + txn: &RoTxn, + height: u32, + ) -> Result>, Error> { let height = height.to_be_bytes(); let header = self.bodies.get(txn, &height)?; Ok(header) @@ -77,7 +85,11 @@ impl< Ok(()) } - pub fn append_header(&self, txn: &mut RwTxn, header: &Header) -> Result<(), Error> { + pub fn append_header( + &self, + txn: &mut RwTxn, + header: &Header, + ) -> Result<(), Error> { let height = self.get_height(txn)?; let best_hash = self.get_best_hash(txn)?; if header.prev_side_hash != best_hash { diff --git a/src/authorization.rs b/src/authorization.rs index 79ab6ed..ce051fc 100644 --- a/src/authorization.rs +++ b/src/authorization.rs @@ -1,6 +1,10 @@ use crate::types::blake3; -use crate::types::{Address, AuthorizedTransaction, Body, GetAddress, Transaction, Verify}; -pub use ed25519_dalek::{Keypair, PublicKey, Signature, SignatureError, Signer, Verifier}; +use crate::types::{ + Address, AuthorizedTransaction, Body, GetAddress, Transaction, Verify, +}; +pub use ed25519_dalek::{ + Keypair, PublicKey, Signature, SignatureError, Signer, Verifier, +}; use rayon::prelude::*; use serde::{Deserialize, Serialize}; @@ -16,9 +20,13 @@ impl GetAddress for Authorization { } } -impl Deserialize<'de> + Send + Sync> Verify for Authorization { +impl Deserialize<'de> + Send + Sync> Verify + for Authorization +{ type Error = Error; - fn verify_transaction(transaction: &AuthorizedTransaction) -> Result<(), Self::Error> + fn verify_transaction( + transaction: &AuthorizedTransaction, + ) -> Result<(), Self::Error> where Self: Sized, { @@ -56,16 +64,17 @@ pub fn verify_authorized_transaction( let messages: Vec<_> = std::iter::repeat(serialized_transaction.as_slice()) .take(transaction.authorizations.len()) .collect(); - let (public_keys, signatures): (Vec, Vec) = transaction - .authorizations - .iter() - .map( - |Authorization { - public_key, - signature, - }| (public_key, signature), - ) - .unzip(); + let (public_keys, signatures): (Vec, Vec) = + transaction + .authorizations + .iter() + .map( + |Authorization { + public_key, + signature, + }| (public_key, signature), + ) + .unzip(); ed25519_dalek::verify_batch(&messages, &signatures, &public_keys)?; Ok(()) } @@ -82,7 +91,8 @@ pub fn verify_authorizations( .par_iter() .map(bincode::serialize) .collect::>()?; - let serialized_transactions = serialized_transactions.iter().map(Vec::as_slice); + let serialized_transactions = + serialized_transactions.iter().map(Vec::as_slice); let messages = input_numbers.zip(serialized_transactions).flat_map( |(input_number, serialized_transaction)| { std::iter::repeat(serialized_transaction).take(input_number) @@ -101,7 +111,9 @@ pub fn verify_authorizations( signatures: Vec::with_capacity(package_size), public_keys: Vec::with_capacity(package_size), }; - for (authorization, message) in &pairs[i * package_size..(i + 1) * package_size] { + for (authorization, message) in + &pairs[i * package_size..(i + 1) * package_size] + { package.messages.push(*message); package.signatures.push(authorization.signature); package.public_keys.push(authorization.public_key); @@ -128,7 +140,9 @@ pub fn verify_authorizations( messages, signatures, public_keys, - }| ed25519_dalek::verify_batch(messages, signatures, public_keys), + }| { + ed25519_dalek::verify_batch(messages, signatures, public_keys) + }, ) .collect::>()?; Ok(()) @@ -146,7 +160,8 @@ pub fn authorize( addresses_keypairs: &[(Address, &Keypair)], transaction: Transaction, ) -> Result, Error> { - let mut authorizations: Vec = Vec::with_capacity(addresses_keypairs.len()); + let mut authorizations: Vec = + Vec::with_capacity(addresses_keypairs.len()); let message = bincode::serialize(&transaction)?; for (address, keypair) in addresses_keypairs { let hash_public_key = get_address(&keypair.public); diff --git a/src/drivechain/client.rs b/src/drivechain/client.rs index 0f17c67..43a98b3 100644 --- a/src/drivechain/client.rs +++ b/src/drivechain/client.rs @@ -74,13 +74,19 @@ pub trait Main { nsidechain: u8, ) -> Result, jsonrpsee::core::Error>; #[method(name = "listspentwithdrawals")] - async fn listspentwithdrawals(&self) -> Result, jsonrpsee::core::Error>; + async fn listspentwithdrawals( + &self, + ) -> Result, jsonrpsee::core::Error>; #[method(name = "listfailedwithdrawals")] - async fn listfailedwithdrawals(&self) -> Result, jsonrpsee::core::Error>; + async fn listfailedwithdrawals( + &self, + ) -> Result, jsonrpsee::core::Error>; #[method(name = "getblockcount")] async fn getblockcount(&self) -> Result; #[method(name = "getbestblockhash")] - async fn getbestblockhash(&self) -> Result; + async fn getbestblockhash( + &self, + ) -> Result; #[method(name = "getblock")] async fn getblock( &self, @@ -121,14 +127,20 @@ pub trait Main { ) -> Result; #[method(name = "generate")] - async fn generate(&self, num: u32) -> Result; + async fn generate( + &self, + num: u32, + ) -> Result; #[method(name = "getnewaddress")] async fn getnewaddress( &self, account: &str, address_type: &str, - ) -> Result, jsonrpsee::core::Error>; + ) -> Result< + bitcoin::Address, + jsonrpsee::core::Error, + >; #[method(name = "createsidechaindeposit")] async fn createsidechaindeposit( diff --git a/src/drivechain/mod.rs b/src/drivechain/mod.rs index f7972d1..d3dc7ed 100644 --- a/src/drivechain/mod.rs +++ b/src/drivechain/mod.rs @@ -24,7 +24,11 @@ impl Drivechain { .nextblockhash .ok_or(Error::NoNextBlock { prev_main_hash })?; self.client - .verifybmm(&block_hash, &header.hash().into(), self.sidechain_number) + .verifybmm( + &block_hash, + &header.hash().into(), + self.sidechain_number, + ) .await?; Ok(()) } @@ -38,7 +42,8 @@ impl Drivechain { end: bitcoin::BlockHash, start: Option, ) -> Result, Error> { - let (deposits, deposit_block_hash) = self.get_deposit_outputs(end, start).await?; + let (deposits, deposit_block_hash) = + self.get_deposit_outputs(end, start).await?; let bundle_statuses = self.get_withdrawal_bundle_statuses().await?; let two_way_peg_data = TwoWayPegData { deposits, @@ -65,18 +70,24 @@ impl Drivechain { &self, end: bitcoin::BlockHash, start: Option, - ) -> Result<(HashMap>, Option), Error> { + ) -> Result<(HashMap>, Option), Error> + { let deposits = self .client - .listsidechaindepositsbyblock(self.sidechain_number, Some(end), start) + .listsidechaindepositsbyblock( + self.sidechain_number, + Some(end), + start, + ) .await?; let mut last_block_hash = None; let mut last_total = 0; let mut outputs = HashMap::new(); for deposit in &deposits { let transaction = hex::decode(&deposit.txhex)?; - let transaction = - bitcoin::Transaction::consensus_decode(&mut std::io::Cursor::new(transaction))?; + let transaction = bitcoin::Transaction::consensus_decode( + &mut std::io::Cursor::new(transaction), + )?; if let Some(start) = start { if deposit.hashblock == start { last_total = transaction.output[deposit.nburnindex].value; @@ -125,7 +136,10 @@ impl Drivechain { Ok(statuses) } - pub fn new(sidechain_number: u8, main_addr: SocketAddr) -> Result { + pub fn new( + sidechain_number: u8, + main_addr: SocketAddr, + ) -> Result { let mut headers = HeaderMap::new(); let auth = format!("{}:{}", "user", "password"); let header_value = format!( diff --git a/src/mempool.rs b/src/mempool.rs index 180618b..8f09826 100644 --- a/src/mempool.rs +++ b/src/mempool.rs @@ -5,7 +5,10 @@ use serde::{Deserialize, Serialize}; #[derive(Clone)] pub struct MemPool { - pub transactions: Database, SerdeBincode>>, + pub transactions: Database< + OwnedType<[u8; 32]>, + SerdeBincode>, + >, pub spent_utxos: Database, Unit>, } @@ -40,8 +43,11 @@ impl< } self.spent_utxos.put(txn, input, &())?; } - self.transactions - .put(txn, &transaction.transaction.txid().into(), &transaction)?; + self.transactions.put( + txn, + &transaction.transaction.txid().into(), + &transaction, + )?; Ok(()) } diff --git a/src/miner.rs b/src/miner.rs index b80519c..b45e8a4 100644 --- a/src/miner.rs +++ b/src/miner.rs @@ -15,7 +15,10 @@ pub struct Miner { } impl Miner { - pub fn new(sidechain_number: u8, main_addr: SocketAddr) -> Result { + pub fn new( + sidechain_number: u8, + main_addr: SocketAddr, + ) -> Result { let drivechain = Drivechain::new(sidechain_number, main_addr)?; Ok(Self { drivechain, @@ -55,14 +58,18 @@ impl Miner { ) .await .map_err(crate::drivechain::Error::from)?; - bitcoin::Txid::from_str(value["txid"]["txid"].as_str().ok_or(Error::InvalidJson)?) - .map_err(crate::drivechain::Error::from)?; + bitcoin::Txid::from_str( + value["txid"]["txid"].as_str().ok_or(Error::InvalidJson)?, + ) + .map_err(crate::drivechain::Error::from)?; assert_eq!(header.merkle_root, body.compute_merkle_root()); self.block = Some((header, body)); Ok(()) } - pub async fn confirm_bmm(&mut self) -> Result)>, Error> { + pub async fn confirm_bmm( + &mut self, + ) -> Result)>, Error> { if let Some((header, body)) = self.block.clone() { self.drivechain.verify_bmm(&header).await?; self.block = None; diff --git a/src/net.rs b/src/net.rs index 189671d..d759301 100644 --- a/src/net.rs +++ b/src/net.rs @@ -117,7 +117,10 @@ impl Net { Ok(peer) } - pub async fn disconnect(&self, stable_id: usize) -> Result, Error> { + pub async fn disconnect( + &self, + stable_id: usize, + ) -> Result, Error> { let peer = self.peers.write().await.remove(&stable_id); Ok(peer) } @@ -139,7 +142,9 @@ pub fn make_client_endpoint(bind_addr: SocketAddr) -> Result { /// - a stream of incoming QUIC connections /// - server certificate serialized into DER format #[allow(unused)] -pub fn make_server_endpoint(bind_addr: SocketAddr) -> Result<(Endpoint, Vec), Error> { +pub fn make_server_endpoint( + bind_addr: SocketAddr, +) -> Result<(Endpoint, Vec), Error> { let (server_config, server_cert) = configure_server()?; let endpoint = Endpoint::server(server_config, bind_addr)?; Ok((endpoint, server_cert)) @@ -153,7 +158,8 @@ fn configure_server() -> Result<(ServerConfig, Vec), Error> { let priv_key = rustls::PrivateKey(priv_key); let cert_chain = vec![rustls::Certificate(cert_der.clone())]; - let mut server_config = ServerConfig::with_single_cert(cert_chain, priv_key)?; + let mut server_config = + ServerConfig::with_single_cert(cert_chain, priv_key)?; let transport_config = Arc::get_mut(&mut server_config.transport).unwrap(); transport_config.max_concurrent_uni_streams(1_u8.into()); diff --git a/src/node.rs b/src/node.rs index adf5a72..6800391 100644 --- a/src/node.rs +++ b/src/node.rs @@ -64,8 +64,10 @@ impl< let state = crate::state::State::new(&env)?; let archive = crate::archive::Archive::new(&env)?; let mempool = crate::mempool::MemPool::new(&env)?; - let drivechain = - crate::drivechain::Drivechain::new(>::THIS_SIDECHAIN, main_addr)?; + let drivechain = crate::drivechain::Drivechain::new( + >::THIS_SIDECHAIN, + main_addr, + )?; let net = crate::net::Net::new(bind_addr)?; let custom_state = State::new(&env)?; Ok(Self { @@ -96,7 +98,8 @@ impl< txn: &RoTxn, transaction: &AuthorizedTransaction, ) -> Result>::Error>> { - let filled_transaction = self.state.fill_transaction(txn, &transaction.transaction)?; + let filled_transaction = + self.state.fill_transaction(txn, &transaction.transaction)?; for (authorization, spent_utxo) in transaction .authorizations .iter() @@ -158,7 +161,8 @@ impl< pub fn get_utxos_by_addresses( &self, addresses: &HashSet
, - ) -> Result>, Error<>::Error>> { + ) -> Result>, Error<>::Error>> + { let txn = self.env.read_txn()?; let utxos = self.state.get_utxos_by_addresses(&txn, addresses)?; Ok(utxos) @@ -167,14 +171,18 @@ impl< pub fn get_transactions( &self, number: usize, - ) -> Result<(Vec>, u64), Error<>::Error>> { + ) -> Result< + (Vec>, u64), + Error<>::Error>, + > { let mut txn = self.env.write_txn()?; let transactions = self.mempool.take(&txn, number)?; let mut fee: u64 = 0; let mut returned_transactions = vec![]; let mut spent_utxos = HashSet::new(); for transaction in &transactions { - let inputs: HashSet<_> = transaction.transaction.inputs.iter().copied().collect(); + let inputs: HashSet<_> = + transaction.transaction.inputs.iter().copied().collect(); if !spent_utxos.is_disjoint(&inputs) { println!("UTXO double spent"); self.mempool @@ -210,7 +218,8 @@ impl< pub fn get_pending_withdrawal_bundle( &self, - ) -> Result>, Error<>::Error>> { + ) -> Result>, Error<>::Error>> + { let txn = self.env.read_txn()?; Ok(self.state.get_pending_withdrawal_bundle(&txn)?) } @@ -227,18 +236,32 @@ impl< let bundle = { let two_way_peg_data = self .drivechain - .get_two_way_peg_data(header.prev_main_hash, last_deposit_block_hash) + .get_two_way_peg_data( + header.prev_main_hash, + last_deposit_block_hash, + ) .await?; let mut txn = self.env.write_txn()?; self.state.validate_body(&txn, &body)?; let height = self.archive.get_height(&txn)?; - self.custom_state - .validate_body(&txn, height, &self.state, &body)?; + self.custom_state.validate_body( + &txn, + height, + &self.state, + &body, + )?; self.state.connect_body(&mut txn, &body)?; - self.custom_state - .connect_body(&mut txn, height, &self.state, &body)?; - self.state - .connect_two_way_peg_data(&mut txn, &two_way_peg_data, height)?; + self.custom_state.connect_body( + &mut txn, + height, + &self.state, + &body, + )?; + self.state.connect_two_way_peg_data( + &mut txn, + &two_way_peg_data, + height, + )?; let bundle = self.state.get_pending_withdrawal_bundle(&txn)?; self.archive.append_header(&mut txn, &header)?; self.archive.put_body(&mut txn, &header, &body)?; @@ -257,7 +280,10 @@ impl< Ok(()) } - pub async fn connect(&self, addr: SocketAddr) -> Result<(), Error<>::Error>> { + pub async fn connect( + &self, + addr: SocketAddr, + ) -> Result<(), Error<>::Error>> { let peer = self.net.connect(addr).await?; let peer0 = peer.clone(); let node0 = self.clone(); @@ -334,7 +360,9 @@ impl< ) }; let response = match (header, body) { - (Some(header), Some(body)) => Response::Block { header, body }, + (Some(header), Some(body)) => { + Response::Block { header, body } + } (_, _) => Response::NoBlock, }; let response = bincode::serialize(&response)?; @@ -360,12 +388,17 @@ impl< Ok(_) => { { let mut txn = self.env.write_txn()?; - println!("adding transaction to mempool: {:?}", &transaction); + println!( + "adding transaction to mempool: {:?}", + &transaction + ); self.mempool.put(&mut txn, &transaction)?; txn.commit()?; } for peer0 in self.net.peers.read().await.values() { - if peer0.connection.stable_id() == peer.connection.stable_id() { + if peer0.connection.stable_id() + == peer.connection.stable_id() + { continue; } peer0 @@ -395,13 +428,17 @@ impl< let incoming_conn = node.net.server.accept().await.unwrap(); let connection = incoming_conn.await.unwrap(); for peer in node.net.peers.read().await.values() { - if peer.connection.remote_address() == connection.remote_address() { + if peer.connection.remote_address() + == connection.remote_address() + { println!( "already connected to {} refusing duplicate connection", connection.remote_address() ); - connection - .close(crate::net::quinn::VarInt::from_u32(1), b"already connected"); + connection.close( + crate::net::quinn::VarInt::from_u32(1), + b"already connected", + ); } } if connection.close_reason().is_some() { @@ -478,13 +515,17 @@ impl< }; if state.block_height > height { let response = peer - .request(&Request::GetBlock { height: height + 1 }) + .request(&Request::GetBlock { + height: height + 1, + }) .await .unwrap(); match response { Response::Block { header, body } => { println!("got new header {:?}", &header); - node.submit_block(&header, &body).await.unwrap(); + node.submit_block(&header, &body) + .await + .unwrap(); } Response::NoBlock => {} Response::TransactionAccepted => {} diff --git a/src/state.rs b/src/state.rs index 61b0209..4621109 100644 --- a/src/state.rs +++ b/src/state.rs @@ -11,15 +11,24 @@ use std::marker::PhantomData; #[derive(Clone)] pub struct State { pub utxos: Database, SerdeBincode>>, - pub pending_withdrawal_bundle: Database, SerdeBincode>>, - pub last_withdrawal_bundle_failure_height: Database, OwnedType>, - pub last_deposit_block: Database, SerdeBincode>, + pub pending_withdrawal_bundle: + Database, SerdeBincode>>, + pub last_withdrawal_bundle_failure_height: + Database, OwnedType>, + pub last_deposit_block: + Database, SerdeBincode>, pub _body: PhantomData, } impl< A: crate::types::Verify + GetAddress, - C: GetValue + Debug + Clone + Eq + Serialize + for<'de> Deserialize<'de> + 'static, + C: GetValue + + Debug + + Clone + + Eq + + Serialize + + for<'de> Deserialize<'de> + + 'static, > State { pub const NUM_DBS: u32 = 5; @@ -28,10 +37,12 @@ impl< pub fn new(env: &heed::Env) -> Result { let utxos = env.create_database(Some("utxos"))?; - let pending_withdrawal_bundle = env.create_database(Some("pending_withdrawal_bundle"))?; + let pending_withdrawal_bundle = + env.create_database(Some("pending_withdrawal_bundle"))?; let last_withdrawal_bundle_failure_height = env.create_database(Some("last_withdrawal_bundle_failure_height"))?; - let last_deposit_block = env.create_database(Some("last_deposit_block"))?; + let last_deposit_block = + env.create_database(Some("last_deposit_block"))?; Ok(Self { utxos, pending_withdrawal_bundle, @@ -41,7 +52,10 @@ impl< }) } - pub fn get_utxos(&self, txn: &RoTxn) -> Result>, Error> { + pub fn get_utxos( + &self, + txn: &RoTxn, + ) -> Result>, Error> { let mut utxos = HashMap::new(); for item in self.utxos.iter(txn)? { let (outpoint, output) = item?; @@ -95,9 +109,9 @@ impl< // Weight of a single output. const OUTPUT_WEIGHT: u64 = 128; // Turns out to be 3121. - const MAX_BUNDLE_OUTPUTS: usize = ((bitcoin::policy::MAX_STANDARD_TX_WEIGHT as u64 - - BUNDLE_0_WEIGHT) - / OUTPUT_WEIGHT) as usize; + const MAX_BUNDLE_OUTPUTS: usize = + ((bitcoin::policy::MAX_STANDARD_TX_WEIGHT as u64 - BUNDLE_0_WEIGHT) + / OUTPUT_WEIGHT) as usize; // Aggregate all outputs by destination. // destination -> (value, mainchain fee, spent_utxos) @@ -211,7 +225,9 @@ impl< ] .concat(), }; - if transaction.weight().to_wu() > bitcoin::policy::MAX_STANDARD_TX_WEIGHT as u64 { + if transaction.weight().to_wu() + > bitcoin::policy::MAX_STANDARD_TX_WEIGHT as u64 + { Err(Error::BundleTooHeavy { weight: transaction.weight().to_wu(), max_weight: bitcoin::policy::MAX_STANDARD_TX_WEIGHT as u64, @@ -248,7 +264,11 @@ impl< Ok(value_in - value_out) } - pub fn validate_body(&self, txn: &RoTxn, body: &Body) -> Result { + pub fn validate_body( + &self, + txn: &RoTxn, + body: &Body, + ) -> Result { let mut coinbase_value: u64 = 0; for output in &body.coinbase { coinbase_value += output.get_value(); @@ -267,7 +287,8 @@ impl< } spent_utxos.insert(*input); } - total_fees += self.validate_filled_transaction(filled_transaction)?; + total_fees += + self.validate_filled_transaction(filled_transaction)?; } if coinbase_value > total_fees { return Err(Error::NotEnoughFees); @@ -275,7 +296,9 @@ impl< let spent_utxos = filled_transactions .iter() .flat_map(|t| t.spent_utxos.iter()); - for (authorization, spent_utxo) in body.authorizations.iter().zip(spent_utxos) { + for (authorization, spent_utxo) in + body.authorizations.iter().zip(spent_utxos) + { if authorization.get_address() != spent_utxo.address { return Err(Error::WrongPubKeyForAddress); } @@ -316,7 +339,9 @@ impl< > Self::WITHDRAWAL_BUNDLE_FAILURE_GAP && self.pending_withdrawal_bundle.get(txn, &0)?.is_none() { - if let Some(bundle) = self.collect_withdrawal_bundle(txn, block_height + 1)? { + if let Some(bundle) = + self.collect_withdrawal_bundle(txn, block_height + 1)? + { for outpoint in bundle.spent_utxos.keys() { self.utxos.delete(txn, outpoint)?; } @@ -349,7 +374,11 @@ impl< Ok(()) } - pub fn connect_body(&self, txn: &mut RwTxn, body: &Body) -> Result<(), Error> { + pub fn connect_body( + &self, + txn: &mut RwTxn, + body: &Body, + ) -> Result<(), Error> { let merkle_root = body.compute_merkle_root(); for (vout, output) in body.coinbase.iter().enumerate() { let outpoint = OutPoint::Coinbase { diff --git a/src/types/address.rs b/src/types/address.rs index 6978346..8429d8a 100644 --- a/src/types/address.rs +++ b/src/types/address.rs @@ -1,4 +1,6 @@ -#[derive(Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)] +#[derive( + Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, +)] pub struct Address(pub [u8; 20]); impl Address { diff --git a/src/types/hashes.rs b/src/types/hashes.rs index 17f3dd6..9d4a2e8 100644 --- a/src/types/hashes.rs +++ b/src/types/hashes.rs @@ -3,7 +3,16 @@ use bitcoin::hashes::Hash as _; const BLAKE3_LENGTH: usize = 32; pub type Hash = [u8; BLAKE3_LENGTH]; -#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)] +#[derive( + Default, + Clone, + Copy, + Eq, + PartialEq, + Hash, + serde::Serialize, + serde::Deserialize, +)] pub struct BlockHash(pub Hash); impl From for BlockHash { @@ -43,7 +52,16 @@ impl std::fmt::Debug for BlockHash { } } -#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)] +#[derive( + Default, + Clone, + Copy, + Eq, + PartialEq, + Hash, + serde::Serialize, + serde::Deserialize, +)] pub struct MerkleRoot(Hash); impl From for MerkleRoot { @@ -70,7 +88,16 @@ impl std::fmt::Debug for MerkleRoot { } } -#[derive(Default, Clone, Copy, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)] +#[derive( + Default, + Clone, + Copy, + Eq, + PartialEq, + Hash, + serde::Serialize, + serde::Deserialize, +)] pub struct Txid(pub Hash); impl Txid { @@ -110,7 +137,7 @@ impl std::fmt::Debug for Txid { } pub fn hash(data: &T) -> Hash { - let data_serialized = - bincode::serialize(data).expect("failed to serialize a type to compute a hash"); + let data_serialized = bincode::serialize(data) + .expect("failed to serialize a type to compute a hash"); blake3::hash(&data_serialized).into() } diff --git a/src/types/mod.rs b/src/types/mod.rs index 5a24430..1c57f26 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -5,12 +5,11 @@ mod address; mod hashes; mod types; -pub use types::*; pub use bitcoin; +pub use blake3; pub use bs58; pub use serde; -pub use blake3; - +pub use types::*; /* // Replace () with a type (usually an enum) for output data specific for your sidechain. diff --git a/src/types/types.rs b/src/types/types.rs index ffeb6e7..3c6f481 100644 --- a/src/types/types.rs +++ b/src/types/types.rs @@ -17,8 +17,12 @@ impl std::fmt::Display for OutPoint { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Regular { txid, vout } => write!(f, "regular {txid} {vout}"), - Self::Coinbase { merkle_root, vout } => write!(f, "coinbase {merkle_root} {vout}"), - Self::Deposit(bitcoin::OutPoint { txid, vout }) => write!(f, "deposit {txid} {vout}"), + Self::Coinbase { merkle_root, vout } => { + write!(f, "coinbase {merkle_root} {vout}") + } + Self::Deposit(bitcoin::OutPoint { txid, vout }) => { + write!(f, "deposit {txid} {vout}") + } } } } @@ -144,7 +148,8 @@ impl Body { .map(|t| t.transaction.inputs.len()) .sum(), ); - let mut transactions = Vec::with_capacity(authorized_transactions.len()); + let mut transactions = + Vec::with_capacity(authorized_transactions.len()); for at in authorized_transactions.into_iter() { authorizations.extend(at.authorizations); transactions.push(at.transaction); @@ -209,7 +214,9 @@ impl GetValue for () { pub trait Verify { type Error; - fn verify_transaction(transaction: &AuthorizedTransaction) -> Result<(), Self::Error> + fn verify_transaction( + transaction: &AuthorizedTransaction, + ) -> Result<(), Self::Error> where Self: Sized; fn verify_body(body: &Body) -> Result<(), Self::Error> diff --git a/src/wallet.rs b/src/wallet.rs index 2caaf31..fa2192e 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,6 +1,7 @@ pub use crate::authorization::{get_address, Authorization}; use crate::types::{ - bitcoin, Address, AuthorizedTransaction, GetValue, OutPoint, Output, Transaction, + bitcoin, Address, AuthorizedTransaction, GetValue, OutPoint, Output, + Transaction, }; use byteorder::{BigEndian, ByteOrder}; use ed25519_dalek_bip32::*; @@ -20,7 +21,9 @@ pub struct Wallet { pub utxos: Database, SerdeBincode>>, } -impl Deserialize<'de> + 'static> Wallet { +impl Deserialize<'de> + 'static> + Wallet +{ pub const NUM_DBS: u32 = 5; pub fn new(path: &Path) -> Result { @@ -106,7 +109,10 @@ impl Deserialize<'de> + 'static> Wall Ok(Transaction { inputs, outputs }) } - pub fn select_coins(&self, value: u64) -> Result<(u64, HashMap>), Error> { + pub fn select_coins( + &self, + value: u64, + ) -> Result<(u64, HashMap>), Error> { let txn = self.env.read_txn()?; let mut utxos = vec![]; for item in self.utxos.iter(&txn)? { @@ -141,7 +147,10 @@ impl Deserialize<'de> + 'static> Wall Ok(()) } - pub fn put_utxos(&self, utxos: &HashMap>) -> Result<(), Error> { + pub fn put_utxos( + &self, + utxos: &HashMap>, + ) -> Result<(), Error> { let mut txn = self.env.write_txn()?; for (outpoint, output) in utxos { self.utxos.put(&mut txn, outpoint, output)?; @@ -187,13 +196,14 @@ impl Deserialize<'de> + 'static> Wall let txn = self.env.read_txn()?; let mut authorizations = vec![]; for input in &transaction.inputs { - let spent_utxo = self.utxos.get(&txn, input)?.ok_or(Error::NoUtxo)?; + let spent_utxo = + self.utxos.get(&txn, input)?.ok_or(Error::NoUtxo)?; let index = self .address_to_index .get(&txn, &spent_utxo.address)? .ok_or(Error::NoIndex { - address: spent_utxo.address, - })?; + address: spent_utxo.address, + })?; let index = BigEndian::read_u32(&index); let keypair = self.get_keypair(&txn, index)?; let signature = crate::authorization::sign(&keypair, &transaction)?; @@ -235,7 +245,11 @@ impl Deserialize<'de> + 'static> Wall Ok(last_index) } - fn get_keypair(&self, txn: &RoTxn, index: u32) -> Result { + fn get_keypair( + &self, + txn: &RoTxn, + index: u32, + ) -> Result { let seed = self.seed.get(txn, &0)?.ok_or(Error::NoSeed)?; let xpriv = ExtendedSecretKey::from_seed(&seed)?; let derivation_path = DerivationPath::new([