diff --git a/Cargo.lock b/Cargo.lock index 34ba3e3..2da0637 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,11 +44,21 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base58ck" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f" +dependencies = [ + "bitcoin-internals 0.3.0", + "bitcoin_hashes 0.14.0", +] + [[package]] name = "bech32" -version = "0.10.0-beta" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea" +checksum = "d965446196e3b7decd44aa7ee49e31d630118f90ef12f97900f262eb915c951d" [[package]] name = "bip324" @@ -72,15 +82,17 @@ dependencies = [ [[package]] name = "bitcoin" -version = "0.31.2" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c85783c2fe40083ea54a33aa2f0ba58831d90fcd190f5bdc47e74e84d2a96ae" +checksum = "7170e7750a20974246f17ece04311b4205a6155f1db564c5b224af817663c3ea" dependencies = [ + "base58ck", "bech32", - "bitcoin-internals", - "bitcoin_hashes", - "core2", - "hex-conservative 0.1.1", + "bitcoin-internals 0.3.0", + "bitcoin-io", + "bitcoin-units", + "bitcoin_hashes 0.14.0", + "hex-conservative 0.2.0", "hex_lit", "secp256k1", ] @@ -91,17 +103,47 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" +[[package]] +name = "bitcoin-internals" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2" + +[[package]] +name = "bitcoin-io" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "340e09e8399c7bd8912f495af6aa58bea0c9214773417ffaa8f6460f93aaee56" + +[[package]] +name = "bitcoin-units" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb54da0b28892f3c52203a7191534033e051b6f4b52bc15480681b57b7e036f5" +dependencies = [ + "bitcoin-internals 0.3.0", +] + [[package]] name = "bitcoin_hashes" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b" dependencies = [ - "bitcoin-internals", - "core2", + "bitcoin-internals 0.2.0", "hex-conservative 0.1.1", ] +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.0", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -126,15 +168,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "core2" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239fa3ae9b63c2dc74bd3fa852d4792b8b305ae64eeede946265b6af62f1fff3" -dependencies = [ - "memchr", -] - [[package]] name = "getrandom" version = "0.2.12" @@ -163,9 +196,6 @@ name = "hex-conservative" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2" -dependencies = [ - "core2", -] [[package]] name = "hex-conservative" @@ -198,12 +228,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - [[package]] name = "miniz_oxide" version = "0.4.4" @@ -347,19 +371,19 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "secp256k1" -version = "0.28.2" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10" +checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" dependencies = [ - "bitcoin_hashes", + "bitcoin_hashes 0.13.0", "secp256k1-sys", ] [[package]] name = "secp256k1-sys" -version = "0.9.2" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb" +checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" dependencies = [ "cc", ] diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index 2aad494..cbdcaac 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -15,7 +15,7 @@ alloc = [] [dependencies] rand = { version = "0.8.0", default-features = false } -bitcoin = { version = "0.31.2", default-features = false, features = ["no-std"] } +bitcoin = { version = "0.32.0", default-features = false } [dev-dependencies] hex = { package = "hex-conservative", version = "0.2.0" } diff --git a/protocol/src/fschacha20poly1305.rs b/protocol/src/fschacha20poly1305.rs index 8cee8ff..b68c97e 100644 --- a/protocol/src/fschacha20poly1305.rs +++ b/protocol/src/fschacha20poly1305.rs @@ -9,7 +9,7 @@ const CHACHA_BLOCKS_USED: u32 = 3; const REKEY_INTERVAL: u32 = 224; const REKEY_INITIAL_NONCE: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF]; -/// Errors encrypting and decrypting with FSChaCha20Poly1305. +/// Errors encrypting and decrypting with [`FSChaCha20Poly1305`]. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Error { Decryption(crate::chacha20poly1305::Error), diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index d5af57d..f7a1d30 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -1,6 +1,9 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -//! BIP 324 encrypted transport for exchanging Bitcoin P2P messages. Read more about the [specification](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki). +//! BIP 324 encrypted transport for exchanging Bitcoin P2P messages. Much like TLS, a connection begins by exchanging ephimeral +//! elliptic curve public keys and performing a Diffie-Hellman handshake. Thereafter, each participant derives shared session secrets, and may +//! freely exchange encrypted messages. Under the new V2 specification, messages are encoded slightly differently than V1. +//! Read more about the [specification](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki). #![no_std] #[cfg(feature = "alloc")] @@ -35,26 +38,35 @@ use fschacha20poly1305::{FSChaCha20, FSChaCha20Poly1305}; use hkdf::Hkdf; use rand::Rng; -/// Number of bytes for the decoy flag on a packet. +// Number of bytes for the decoy flag on a packet. const DECOY_BYTES: usize = 1; -/// Number of bytes for the authentication tag of a packet. +// Number of bytes for the authentication tag of a packet. const TAG_BYTES: usize = 16; -/// Number of bytes for the length encoding prefix of a packet. +// Number of bytes for the length encoding prefix of a packet. const LENGTH_BYTES: usize = 3; -/// Value for decoy flag. +// Value for decoy flag. const DECOY: u8 = 128; -/// Version content is always empty for the current version of the protocol. +// Version content is always empty for the current version of the protocol. const VERSION_CONTENT: [u8; 0] = []; +/// Errors encountered throughout the lifetime of a V2 connection. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Error { + /// The message decoded is smaller than expected MessageLengthTooSmall, + /// There is a mismatch in the encoding of a message IncompatableV1Message, + /// The message exceeded the maximum allowable length MaxGarbageLength, + /// A handshake step was not completed in the proper order HandshakeOutOfOrder, + /// A curve function could not be executed SecretMaterialsGeneration(secp256k1::Error), + /// Deriving the shared secrets was unsuccessful SecretExpansion, + /// The authentication data was not correct when decoding a message Cipher(fschacha20poly1305::Error), + /// The internal counters of the ciphers are not in sync OutOfSync, } @@ -160,6 +172,7 @@ impl ReceivedMessage { } } +/// Read packets off of a byte stream from a peer. #[derive(Clone, Debug)] pub struct PacketReader { length_decoding_cipher: FSChaCha20, @@ -245,6 +258,7 @@ impl PacketReader { } } +/// Prepare messages to be sent over a byte stream. #[derive(Clone, Debug)] pub struct PacketWriter { length_encoding_cipher: FSChaCha20, diff --git a/protocol/src/serde.rs b/protocol/src/serde.rs index 9c27012..4021a8a 100644 --- a/protocol/src/serde.rs +++ b/protocol/src/serde.rs @@ -1,20 +1,21 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 -//! A subset of commands are represented with a single byte in V2 instead of the 12-byte ASCII encoding like V1. +//! Serialize and deserialize V2 messages over the wire. //! -//! ID mappings defined in [BIP324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki#user-content-v2_Bitcoin_P2P_message_structure). +//! A subset of commands are represented with a single byte in V2 instead of the 12-byte ASCII encoding like V1. Message ID mappings are defined in [BIP324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki#user-content-v2_Bitcoin_P2P_message_structure). use core::fmt; -use std::io; use alloc::vec::Vec; use bitcoin::{ block, consensus::{encode, Decodable, Encodable}, - p2p::message::{CommandString, NetworkMessage}, + io::BufRead, VarInt, }; +pub use bitcoin::p2p::message::{CommandString, NetworkMessage}; + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Error { Serialize, @@ -40,7 +41,7 @@ impl std::error::Error for Error { } } -/// Serialize message in v2 format to buffer. +/// Serialize a [`NetworkMessage`] into a buffer. pub fn serialize(msg: NetworkMessage) -> Result, Error> { let mut buffer = Vec::new(); match &msg { @@ -159,7 +160,7 @@ pub fn serialize(msg: NetworkMessage) -> Result, Error> { Ok(buffer) } -/// Deserialize v2 message into NetworkMessage. +/// Deserialize v2 message into [`NetworkMessage`]. pub fn deserialize(buffer: &[u8]) -> Result { let short_id = buffer[0]; let mut payload_buffer = &buffer[1..]; @@ -295,7 +296,7 @@ struct HeaderDeserializationWrapper(Vec); impl Decodable for HeaderDeserializationWrapper { #[inline] - fn consensus_decode_from_finite_reader( + fn consensus_decode_from_finite_reader( r: &mut R, ) -> Result { let len = VarInt::consensus_decode(r)?.0; diff --git a/proxy/Cargo.toml b/proxy/Cargo.toml index e2daa66..4b3846b 100644 --- a/proxy/Cargo.toml +++ b/proxy/Cargo.toml @@ -9,7 +9,7 @@ readme = "README.md" rust-version = "1.56.1" [dependencies] -bitcoin = { version = "0.31.2" } +bitcoin = { version = "0.32.0" } tokio = { version = "1.37.0", features = ["full"] } bytes = "1.6.0" hex = { package = "hex-conservative", version = "0.2.0" } diff --git a/proxy/README.md b/proxy/README.md index 1bae3bb..6d72b90 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -1,3 +1,27 @@ -# Proxy +# V2 Proxy A proxy process which allows V1-only clients to communicate over a V2 protocol. The process listens on port `1324` for V1 connections and requires the V1 client to send along the remote peer's IP address in the `addr_recv` field. + +## Running the Proxy + +`cargo run --bin async` + +## Testing with Nakamoto + +[Nakamoto](https://github.com/cloudhead/nakamoto) is a BIP-157/BIP-158 Light Client that communicates over the Bitcoin P2P network. With a single change, Nakamoto may be modified to use the proxy. + +```diff +diff --git a/net/poll/src/reactor.rs b/net/poll/src/reactor.rs + +--- a/net/poll/src/reactor.rs ++++ b/net/poll/src/reactor.rs +@@ -468,7 +468,7 @@ fn dial(addr: &net::SocketAddr) -> Result { + sock.set_write_timeout(Some(WRITE_TIMEOUT))?; + sock.set_nonblocking(true)?; + +- match sock.connect(&(*addr).into()) { ++ match sock.connect(&net::SocketAddr::from(([127, 0, 0, 1], 1324)).into()) { + Ok(()) => {} + Err(e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {} + Err(e) if e.raw_os_error() == Some(libc::EALREADY) => { +``` diff --git a/proxy/src/bin/async.rs b/proxy/src/bin/v2.rs similarity index 100% rename from proxy/src/bin/async.rs rename to proxy/src/bin/v2.rs diff --git a/proxy/src/lib.rs b/proxy/src/lib.rs index 342378a..925bc42 100644 --- a/proxy/src/lib.rs +++ b/proxy/src/lib.rs @@ -3,7 +3,7 @@ //! Helper functions for bitcoin p2p proxies. //! //! The V1 and V2 p2p protocols have different header encodings, so a proxy has to do -//! a little more work than just encrypt/decrypt. The [NetworkMessage](bitcoin::p2p::message::NetworkMessage) +//! a little more work than just encrypt/decrypt. The [`NetworkMessage`] //! type is the intermediate state for messages. The V1 side can use the RawNetworkMessage wrapper, but the V2 side //! cannot since things like the checksum are not relevant (those responsibilites are pushed //! onto the transport in V2). @@ -124,7 +124,6 @@ pub async fn read_v1(input: &mut T) -> Result