1use hashes::sha256d;
10use io::{Read, Write};
11
12use crate::consensus::{encode, Decodable, Encodable, ReadExt};
13use crate::internal_macros::impl_consensus_encoding;
14use crate::p2p;
15use crate::p2p::address::Address;
16use crate::p2p::ServiceFlags;
17use crate::prelude::*;
18
19#[derive(PartialEq, Eq, Clone, Debug)]
23pub struct VersionMessage {
24 pub version: u32,
26 pub services: ServiceFlags,
28 pub timestamp: i64,
30 pub receiver: Address,
32 pub sender: Address,
34 pub nonce: u64,
43 pub user_agent: String,
45 pub start_height: i32,
47 pub relay: bool,
51}
52
53impl VersionMessage {
54 pub fn new(
56 services: ServiceFlags,
57 timestamp: i64,
58 receiver: Address,
59 sender: Address,
60 nonce: u64,
61 user_agent: String,
62 start_height: i32,
63 ) -> VersionMessage {
64 VersionMessage {
65 version: p2p::PROTOCOL_VERSION,
66 services,
67 timestamp,
68 receiver,
69 sender,
70 nonce,
71 user_agent,
72 start_height,
73 relay: false,
74 }
75 }
76}
77
78impl_consensus_encoding!(
79 VersionMessage,
80 version,
81 services,
82 timestamp,
83 receiver,
84 sender,
85 nonce,
86 user_agent,
87 start_height,
88 relay
89);
90
91#[derive(PartialEq, Eq, Clone, Copy, Debug)]
93pub enum RejectReason {
94 Malformed = 0x01,
96 Invalid = 0x10,
98 Obsolete = 0x11,
100 Duplicate = 0x12,
102 NonStandard = 0x40,
104 Dust = 0x41,
106 Fee = 0x42,
108 Checkpoint = 0x43,
110}
111
112impl Encodable for RejectReason {
113 fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
114 w.write_all(&[*self as u8])?;
115 Ok(1)
116 }
117}
118
119impl Decodable for RejectReason {
120 fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
121 Ok(match r.read_u8()? {
122 0x01 => RejectReason::Malformed,
123 0x10 => RejectReason::Invalid,
124 0x11 => RejectReason::Obsolete,
125 0x12 => RejectReason::Duplicate,
126 0x40 => RejectReason::NonStandard,
127 0x41 => RejectReason::Dust,
128 0x42 => RejectReason::Fee,
129 0x43 => RejectReason::Checkpoint,
130 _ => return Err(encode::Error::ParseFailed("unknown reject code")),
131 })
132 }
133}
134
135#[derive(PartialEq, Eq, Clone, Debug)]
137pub struct Reject {
138 pub message: Cow<'static, str>,
140 pub ccode: RejectReason,
142 pub reason: Cow<'static, str>,
144 pub hash: sha256d::Hash,
146}
147
148impl_consensus_encoding!(Reject, message, ccode, reason, hash);
149
150#[cfg(test)]
151mod tests {
152 use hashes::sha256d;
153 use hex::test_hex_unwrap as hex;
154
155 use super::{Reject, RejectReason, VersionMessage};
156 use crate::consensus::encode::{deserialize, serialize};
157 use crate::p2p::ServiceFlags;
158
159 #[test]
160 fn version_message_test() {
161 let from_sat = hex!("721101000100000000000000e6e0845300000000010000000000000000000000000000000000ffff0000000000000100000000000000fd87d87eeb4364f22cf54dca59412db7208d47d920cffce83ee8102f5361746f7368693a302e392e39392f2c9f040001");
163
164 let decode: Result<VersionMessage, _> = deserialize(&from_sat);
165 assert!(decode.is_ok());
166 let real_decode = decode.unwrap();
167 assert_eq!(real_decode.version, 70002);
168 assert_eq!(real_decode.services, ServiceFlags::NETWORK);
169 assert_eq!(real_decode.timestamp, 1401217254);
170 assert_eq!(real_decode.nonce, 16735069437859780935);
172 assert_eq!(real_decode.user_agent, "/Satoshi:0.9.99/".to_string());
173 assert_eq!(real_decode.start_height, 302892);
174 assert!(real_decode.relay);
175
176 assert_eq!(serialize(&real_decode), from_sat);
177 }
178
179 #[test]
180 fn reject_message_test() {
181 let reject_tx_conflict = hex!("027478121474786e2d6d656d706f6f6c2d636f6e666c69637405df54d3860b3c41806a3546ab48279300affacf4b88591b229141dcf2f47004");
182 let reject_tx_nonfinal = hex!("02747840096e6f6e2d66696e616c259bbe6c83db8bbdfca7ca303b19413dc245d9f2371b344ede5f8b1339a5460b");
183
184 let decode_result_conflict: Result<Reject, _> = deserialize(&reject_tx_conflict);
185 let decode_result_nonfinal: Result<Reject, _> = deserialize(&reject_tx_nonfinal);
186
187 assert!(decode_result_conflict.is_ok());
188 assert!(decode_result_nonfinal.is_ok());
189
190 let conflict = decode_result_conflict.unwrap();
191 assert_eq!("tx", conflict.message);
192 assert_eq!(RejectReason::Duplicate, conflict.ccode);
193 assert_eq!("txn-mempool-conflict", conflict.reason);
194 assert_eq!(
195 "0470f4f2dc4191221b59884bcffaaf00932748ab46356a80413c0b86d354df05"
196 .parse::<sha256d::Hash>()
197 .unwrap(),
198 conflict.hash
199 );
200
201 let nonfinal = decode_result_nonfinal.unwrap();
202 assert_eq!("tx", nonfinal.message);
203 assert_eq!(RejectReason::NonStandard, nonfinal.ccode);
204 assert_eq!("non-final", nonfinal.reason);
205 assert_eq!(
206 "0b46a539138b5fde4e341b37f2d945c23d41193b30caa7fcbd8bdb836cbe9b25"
207 .parse::<sha256d::Hash>()
208 .unwrap(),
209 nonfinal.hash
210 );
211
212 assert_eq!(serialize(&conflict), reject_tx_conflict);
213 assert_eq!(serialize(&nonfinal), reject_tx_nonfinal);
214 }
215}