2022-06-29 04:05:31 +00:00
|
|
|
// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
|
|
|
|
// SPDX-License-Identifier: CC0-1.0
|
2014-07-18 13:56:17 +00:00
|
|
|
|
2021-11-05 21:58:18 +00:00
|
|
|
//! Bitcoin network-related network messages.
|
2014-07-18 13:56:17 +00:00
|
|
|
//!
|
|
|
|
//! This module defines network messages which describe peers and their
|
2021-11-05 21:58:18 +00:00
|
|
|
//! capabilities.
|
2014-07-18 13:56:17 +00:00
|
|
|
//!
|
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::prelude::*;
|
2021-06-09 10:34:44 +00:00
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::io;
|
2019-12-03 22:39:04 +00:00
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::network::address::Address;
|
|
|
|
use crate::network::constants::{self, ServiceFlags};
|
|
|
|
use crate::consensus::{Encodable, Decodable, ReadExt};
|
|
|
|
use crate::consensus::encode;
|
|
|
|
use crate::hashes::sha256d;
|
2022-06-07 03:25:27 +00:00
|
|
|
use crate::internal_macros::impl_consensus_encoding;
|
2014-07-18 13:56:17 +00:00
|
|
|
|
|
|
|
/// Some simple messages
|
|
|
|
|
|
|
|
/// The `version` message
|
2015-03-26 15:35:31 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
2014-07-18 13:56:17 +00:00
|
|
|
pub struct VersionMessage {
|
2015-04-07 01:51:11 +00:00
|
|
|
/// The P2P network protocol version
|
|
|
|
pub version: u32,
|
|
|
|
/// A bitmask describing the services supported by this node
|
2019-11-22 11:00:48 +00:00
|
|
|
pub services: ServiceFlags,
|
2015-04-07 01:51:11 +00:00
|
|
|
/// The time at which the `version` message was sent
|
|
|
|
pub timestamp: i64,
|
|
|
|
/// The network address of the peer receiving the message
|
|
|
|
pub receiver: Address,
|
|
|
|
/// The network address of the peer sending the message
|
|
|
|
pub sender: Address,
|
|
|
|
/// A random nonce used to detect loops in the network
|
|
|
|
pub nonce: u64,
|
|
|
|
/// A string describing the peer's software
|
|
|
|
pub user_agent: String,
|
2019-01-23 18:30:32 +00:00
|
|
|
/// The height of the maximum-work blockchain that the peer is aware of
|
2015-04-07 01:51:11 +00:00
|
|
|
pub start_height: i32,
|
|
|
|
/// Whether the receiving peer should relay messages to the sender; used
|
|
|
|
/// if the sender is bandwidth-limited and would like to support bloom
|
2019-11-19 22:58:49 +00:00
|
|
|
/// filtering. Defaults to false.
|
2015-04-07 01:51:11 +00:00
|
|
|
pub relay: bool
|
2014-07-18 13:56:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl VersionMessage {
|
2019-12-11 07:25:20 +00:00
|
|
|
/// Constructs a new `version` message with `relay` set to false
|
2018-09-03 05:04:05 +00:00
|
|
|
pub fn new(
|
2019-11-22 11:00:48 +00:00
|
|
|
services: ServiceFlags,
|
2018-09-03 05:04:05 +00:00
|
|
|
timestamp: i64,
|
|
|
|
receiver: Address,
|
|
|
|
sender: Address,
|
|
|
|
nonce: u64,
|
|
|
|
user_agent: String,
|
|
|
|
start_height: i32,
|
|
|
|
) -> VersionMessage {
|
|
|
|
VersionMessage {
|
2015-04-07 01:51:11 +00:00
|
|
|
version: constants::PROTOCOL_VERSION,
|
2021-11-03 09:20:34 +00:00
|
|
|
services,
|
|
|
|
timestamp,
|
|
|
|
receiver,
|
|
|
|
sender,
|
|
|
|
nonce,
|
|
|
|
user_agent,
|
|
|
|
start_height,
|
2018-09-03 05:04:05 +00:00
|
|
|
relay: false,
|
|
|
|
}
|
2014-07-18 13:56:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-01 16:01:39 +00:00
|
|
|
impl_consensus_encoding!(VersionMessage, version, services, timestamp,
|
2015-04-07 01:51:11 +00:00
|
|
|
receiver, sender, nonce,
|
|
|
|
user_agent, start_height, relay);
|
2014-07-18 13:56:17 +00:00
|
|
|
|
2019-08-27 11:01:21 +00:00
|
|
|
/// message rejection reason as a code
|
2020-12-21 11:59:25 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
2019-08-27 11:01:21 +00:00
|
|
|
pub enum RejectReason {
|
|
|
|
/// malformed message
|
2019-12-03 22:42:09 +00:00
|
|
|
Malformed = 0x01,
|
2019-08-27 11:01:21 +00:00
|
|
|
/// invalid message
|
2019-12-03 22:42:09 +00:00
|
|
|
Invalid = 0x10,
|
2019-08-27 11:01:21 +00:00
|
|
|
/// obsolete message
|
2019-12-03 22:42:09 +00:00
|
|
|
Obsolete = 0x11,
|
2019-08-27 11:01:21 +00:00
|
|
|
/// duplicate message
|
2019-12-03 22:42:09 +00:00
|
|
|
Duplicate = 0x12,
|
2019-08-27 11:01:21 +00:00
|
|
|
/// nonstandard transaction
|
2019-12-03 22:42:09 +00:00
|
|
|
NonStandard = 0x40,
|
2019-09-21 11:31:03 +00:00
|
|
|
/// an output is below dust limit
|
2019-12-03 22:42:09 +00:00
|
|
|
Dust = 0x41,
|
2019-08-27 11:01:21 +00:00
|
|
|
/// insufficient fee
|
2019-12-03 22:42:09 +00:00
|
|
|
Fee = 0x42,
|
2019-08-27 11:01:21 +00:00
|
|
|
/// checkpoint
|
2019-12-03 22:42:09 +00:00
|
|
|
Checkpoint = 0x43
|
2019-08-27 11:01:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Encodable for RejectReason {
|
2022-06-29 01:22:12 +00:00
|
|
|
fn consensus_encode<W: io::Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
w.write_all(&[*self as u8])?;
|
2019-08-27 11:01:21 +00:00
|
|
|
Ok(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Decodable for RejectReason {
|
2022-06-29 01:22:12 +00:00
|
|
|
fn consensus_decode<R: io::Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
Ok(match r.read_u8()? {
|
2019-12-03 22:42:09 +00:00
|
|
|
0x01 => RejectReason::Malformed,
|
|
|
|
0x10 => RejectReason::Invalid,
|
|
|
|
0x11 => RejectReason::Obsolete,
|
|
|
|
0x12 => RejectReason::Duplicate,
|
|
|
|
0x40 => RejectReason::NonStandard,
|
|
|
|
0x41 => RejectReason::Dust,
|
|
|
|
0x42 => RejectReason::Fee,
|
|
|
|
0x43 => RejectReason::Checkpoint,
|
2019-09-10 17:44:39 +00:00
|
|
|
_ => return Err(encode::Error::ParseFailed("unknown reject code"))
|
2019-08-27 11:01:21 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reject message might be sent by peers rejecting one of our messages
|
|
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
|
|
pub struct Reject {
|
|
|
|
/// message type rejected
|
2022-04-04 09:09:53 +00:00
|
|
|
pub message: Cow<'static, str>,
|
2019-08-27 11:01:21 +00:00
|
|
|
/// reason of rejection as code
|
|
|
|
pub ccode: RejectReason,
|
|
|
|
/// reason of rejectection
|
2019-12-03 22:39:04 +00:00
|
|
|
pub reason: Cow<'static, str>,
|
2019-08-27 11:01:21 +00:00
|
|
|
/// reference to rejected item
|
|
|
|
pub hash: sha256d::Hash
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_consensus_encoding!(Reject, message, ccode, reason, hash);
|
|
|
|
|
2014-08-01 16:01:39 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2015-04-07 01:51:11 +00:00
|
|
|
use super::VersionMessage;
|
2022-04-04 09:03:08 +00:00
|
|
|
use super::Reject;
|
|
|
|
use super::RejectReason;
|
2015-04-07 01:51:11 +00:00
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::hashes::hex::FromHex;
|
|
|
|
use crate::hashes::sha256d::Hash;
|
|
|
|
use crate::network::constants::ServiceFlags;
|
2015-04-07 01:51:11 +00:00
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::consensus::encode::{deserialize, serialize};
|
2015-04-07 01:51:11 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn version_message_test() {
|
|
|
|
// This message is from my satoshi node, morning of May 27 2014
|
2020-01-08 17:02:30 +00:00
|
|
|
let from_sat = Vec::from_hex("721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001").unwrap();
|
2015-04-07 01:51:11 +00:00
|
|
|
|
2015-04-08 22:23:45 +00:00
|
|
|
let decode: Result<VersionMessage, _> = deserialize(&from_sat);
|
2015-04-07 01:51:11 +00:00
|
|
|
assert!(decode.is_ok());
|
|
|
|
let real_decode = decode.unwrap();
|
|
|
|
assert_eq!(real_decode.version, 70002);
|
2019-12-11 07:32:47 +00:00
|
|
|
assert_eq!(real_decode.services, ServiceFlags::NETWORK);
|
2015-04-07 01:51:11 +00:00
|
|
|
assert_eq!(real_decode.timestamp, 1401217254);
|
|
|
|
// address decodes should be covered by Address tests
|
|
|
|
assert_eq!(real_decode.nonce, 16735069437859780935);
|
2015-04-11 01:55:59 +00:00
|
|
|
assert_eq!(real_decode.user_agent, "/Satoshi:0.9.99/".to_string());
|
2015-04-07 01:51:11 +00:00
|
|
|
assert_eq!(real_decode.start_height, 302892);
|
2022-06-07 04:31:03 +00:00
|
|
|
assert!(real_decode.relay);
|
2015-04-07 01:51:11 +00:00
|
|
|
|
Move relevant names into consensus::encode
- Move network::encodable::* to consensus::encode::*
- Rename Consensus{En,De}codable to {En,De}codable (now under
consensus::encode)
- Move network::serialize::Error to consensus::encode::Error
- Remove Raw{En,De}coder, implement {En,De}coder for T: {Write,Read}
instead
- Move network::serialize::Simple{En,De}coder to
consensus::encode::{En,De}coder
- Rename util::Error::Serialize to util::Error::Encode
- Modify comments to refer to new names
- Modify files to refer to new names
- Expose {En,De}cod{able,er}, {de,}serialize, Params
- Do not return Result for serialize{,_hex} as serializing to a Vec
should never fail
2018-09-20 10:15:45 +00:00
|
|
|
assert_eq!(serialize(&real_decode), from_sat);
|
2015-04-07 01:51:11 +00:00
|
|
|
}
|
2022-04-04 09:03:08 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn reject_message_test() {
|
|
|
|
let reject_tx_conflict = Vec::from_hex("027478121474786e2d6d656d706f6f6c2d636f6e666c69637405df54d3860b3c41806a3546ab48279300affacf4b88591b229141dcf2f47004").unwrap();
|
|
|
|
let reject_tx_nonfinal = Vec::from_hex("02747840096e6f6e2d66696e616c259bbe6c83db8bbdfca7ca303b19413dc245d9f2371b344ede5f8b1339a5460b").unwrap();
|
|
|
|
|
|
|
|
let decode_result_conflict: Result<Reject, _> = deserialize(&reject_tx_conflict);
|
|
|
|
let decode_result_nonfinal: Result<Reject, _> = deserialize(&reject_tx_nonfinal);
|
|
|
|
|
|
|
|
assert!(decode_result_conflict.is_ok());
|
|
|
|
assert!(decode_result_nonfinal.is_ok());
|
|
|
|
|
|
|
|
let conflict = decode_result_conflict.unwrap();
|
|
|
|
assert_eq!("tx", conflict.message);
|
|
|
|
assert_eq!(RejectReason::Duplicate, conflict.ccode);
|
|
|
|
assert_eq!("txn-mempool-conflict", conflict.reason);
|
|
|
|
assert_eq!(
|
|
|
|
Hash::from_hex("0470f4f2dc4191221b59884bcffaaf00932748ab46356a80413c0b86d354df05").unwrap(),
|
|
|
|
conflict.hash
|
|
|
|
);
|
|
|
|
|
|
|
|
let nonfinal = decode_result_nonfinal.unwrap();
|
|
|
|
assert_eq!("tx", nonfinal.message);
|
|
|
|
assert_eq!(RejectReason::NonStandard, nonfinal.ccode);
|
|
|
|
assert_eq!("non-final", nonfinal.reason);
|
|
|
|
assert_eq!(
|
|
|
|
Hash::from_hex("0b46a539138b5fde4e341b37f2d945c23d41193b30caa7fcbd8bdb836cbe9b25").unwrap(),
|
|
|
|
nonfinal.hash
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(serialize(&conflict), reject_tx_conflict);
|
|
|
|
assert_eq!(serialize(&nonfinal), reject_tx_nonfinal);
|
|
|
|
}
|
2014-07-18 13:56:17 +00:00
|
|
|
}
|