<commit_before>use std::net::Ipv6Addr;

use nom;
use nom::{le_u16, le_u32, le_u64, le_i32, le_i64, be_u16, be_u32, IResult};
use sha2::{Sha256, Digest};

use message::Message;
use message::{AddrMessage, SendCmpctMessage, VersionMessage};
use net_addr::NetAddr;

fn to_hex_string(bytes: &[u8]) -> String {
    let strs: Vec<String> = bytes.iter()
        .map(|b| format!("{:02X}", b))
        .collect();
    strs.join(" ")
}


#[derive(Debug)]
struct RawMessage<'a> {
    magic: u32,
    message_type: String,
    len: u32,
    checksum: &'a [u8],
    body: &'a [u8],
}

impl<'a> RawMessage<'a> {
    fn valid(&self) -> bool {
        let mut check: [u8; 4] = [0; 4];
        // create a Sha256 object
        let mut hasher = Sha256::default();
        hasher.input(&self.body);
        let intermediate = hasher.result();
        let mut hasher = Sha256::default();
        hasher.input(&intermediate);
        let output = hasher.result();
        // write the checksum
        for i in 0..4 {
            // let _ = packet.write_u8(output[i]);
            check[i] = output[i];
        }
        check == self.checksum
    }
}

#[derive(Debug)]
struct Header<'a> {
    magic: u32,
    message_type: String,
    len: u32,
    checksum: &'a [u8],
}

named!(header <Header>,
  do_parse!(
  magic: le_u32 >>
    message_type: take_str!(12) >>
    payload_len: le_u32 >>
    checksum: take!(4) >>
    ({trace!("message_type: {:?}\tpayload len: {}", message_type, payload_len); Header {
      magic: magic,
        message_type: message_type.trim_matches(0x00 as char).into(),
        len: payload_len,
        checksum: checksum,
    }})
));


named!(raw_message<RawMessage>,
  do_parse!(
    header: header >>
    body: take!(header.len) >>
    ({trace!("Body.len: {}", body.len());
      RawMessage {
        magic: header.magic,
        message_type: header.message_type, //.trim_matches(0x00 as char).into(),
        len: header.len,
        checksum: header.checksum,
        body: body
      }}
    )
));

pub fn message(i: &[u8]) -> IResult<&[u8], Message> {
    let raw_message_result = raw_message(&i);
    match raw_message_result {
        IResult::Done(i, raw_message) => {
            if !raw_message.valid() {
                // println!("type: {:?}", header(&i));
                warn!("Invalid message");
                return IResult::Error(nom::ErrorKind::Custom(0));
            }
            match &raw_message.message_type[..] {
                "version" => version(raw_message.body),
                "verack" => IResult::Done(i, Message::Verack),
                "sendheaders" => IResult::Done(i, Message::SendHeaders),
                "sendcmpct" => send_compact(raw_message.body),
                "feefilter" => feefilter(raw_message.body),
                "ping" => ping(raw_message.body),
                "pong" => pong(raw_message.body),
                "addr" => addr(raw_message.body),
                _ => {
                    trace!("Raw message: {:?}\n\n{:}", raw_message.message_type, to_hex_string(raw_message.body));
                    IResult::Done(i,
                                  Message::Unparsed(raw_message.message_type,
                                                    raw_message.body.into()))
                }
            }
        }
        IResult::Incomplete(len) => {
            debug!("type: {:?}", header(&i));
            // trace!("Incomplete::Raw: {:?}", raw_message_result);
            IResult::Incomplete(len)
        }
        IResult::Error(e) => {
            debug!("error :: type: {:?}", header(&i));
            IResult::Error(e)
        }
    }
}

named!(feefilter <Message>,
  do_parse!(
    feefilter: le_u64 >>
    (Message::FeeFilter(feefilter))
));

named!(ping <Message>,
  do_parse!(
    nonce: le_u64 >><commit_msg>reduce some log verbosity<commit_after>use std::net::Ipv6Addr;

use nom;
use nom::{le_u16, le_u32, le_u64, le_i32, le_i64, be_u16, be_u32, IResult};
use sha2::{Sha256, Digest};

use message::Message;
use message::{AddrMessage, SendCmpctMessage, VersionMessage};
use net_addr::NetAddr;

fn to_hex_string(bytes: &[u8]) -> String {
    let strs: Vec<String> = bytes.iter()
        .map(|b| format!("{:02X}", b))
        .collect();
    strs.join(" ")
}


#[derive(Debug)]
struct RawMessage<'a> {
    magic: u32,
    message_type: String,
    len: u32,
    checksum: &'a [u8],
    body: &'a [u8],
}

impl<'a> RawMessage<'a> {
    fn valid(&self) -> bool {
        let mut check: [u8; 4] = [0; 4];
        // create a Sha256 object
        let mut hasher = Sha256::default();
        hasher.input(&self.body);
        let intermediate = hasher.result();
        let mut hasher = Sha256::default();
        hasher.input(&intermediate);
        let output = hasher.result();
        // write the checksum
        for i in 0..4 {
            // let _ = packet.write_u8(output[i]);
            check[i] = output[i];
        }
        check == self.checksum
    }
}

#[derive(Debug)]
struct Header<'a> {
    magic: u32,
    message_type: String,
    len: u32,
    checksum: &'a [u8],
}

named!(header <Header>,
  do_parse!(
  magic: le_u32 >>
    message_type: take_str!(12) >>
    payload_len: le_u32 >>
    checksum: take!(4) >>
    ({trace!("message_type: {:?}\tpayload len: {}", message_type, payload_len); Header {
      magic: magic,
        message_type: message_type.trim_matches(0x00 as char).into(),
        len: payload_len,
        checksum: checksum,
    }})
));


named!(raw_message<RawMessage>,
  do_parse!(
    header: header >>
    body: take!(header.len) >>
    ({trace!("Body.len: {}", body.len());
      RawMessage {
        magic: header.magic,
        message_type: header.message_type, //.trim_matches(0x00 as char).into(),
        len: header.len,
        checksum: header.checksum,
        body: body
      }}
    )
));

pub fn message(i: &[u8]) -> IResult<&[u8], Message> {
    let raw_message_result = raw_message(&i);
    match raw_message_result {
        IResult::Done(i, raw_message) => {
            if !raw_message.valid() {
                // println!("type: {:?}", header(&i));
                warn!("Invalid message");
                return IResult::Error(nom::ErrorKind::Custom(0));
            }
            match &raw_message.message_type[..] {
                "version" => version(raw_message.body),
                "verack" => IResult::Done(i, Message::Verack),
                "sendheaders" => IResult::Done(i, Message::SendHeaders),
                "sendcmpct" => send_compact(raw_message.body),
                "feefilter" => feefilter(raw_message.body),
                "ping" => ping(raw_message.body),
                "pong" => pong(raw_message.body),
                "addr" => addr(raw_message.body),
                _ => {
                    trace!("Raw message: {:?}\n\n{:}", raw_message.message_type, to_hex_string(raw_message.body));
                    IResult::Done(i,
                                  Message::Unparsed(raw_message.message_type,
                                                    raw_message.body.into()))
                }
            }
        }
        IResult::Incomplete(len) => {
            trace!("type: {:?}", header(&i));
            // trace!("Incomplete::Raw: {:?}", raw_message_result);
            IResult::Incomplete(len)
        }
        IResult::Error(e) => {
            debug!("error :: type: {:?}", header(&i));
            IResult::Error(e)
        }
    }
}

named!(feefilter <Message>,
  do_parse!(
    feefilter: le_u64 >>
    (Message::FeeFilter(feefilter))
));

named!(ping <Message>,
  do_parse!(
    nonce: le_u64 >>
{'fraction_non_alphanumeric': 0.16666666666666666, 'fraction_numerical': 0.018138801261829655, 'mean_word_length': 2.1338550247116967, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 85, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3577394', 'n_tokens_mistral': 2579, 'n_tokens_neox': 2508, 'n_words': 461}