<commit_before>extern crate digest;
extern crate hex;
extern crate indy_crypto;
extern crate rlp;
extern crate rmp_serde;
extern crate rust_base58;
extern crate sha2;
extern crate sha3;

use api::ErrorCode;
use base64;
use domain::ledger::{constants, request::ProtocolVersion};
use errors::common::CommonError;
use self::digest::FixedOutput;
use self::digest::Input;
use self::hex::ToHex;
use self::indy_crypto::bls::{Bls, Generator, MultiSignature, VerKey};
use self::node::{Node, TrieDB};
use self::rlp::{
    encode as rlp_encode,
    UntrustedRlp,
};
use self::rust_base58::FromBase58;
use self::sha3::Digest;
use serde_json;
use serde_json::Value as SJsonValue;
use services::pool::events::REQUESTS_FOR_STATE_PROOFS;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use super::PoolService;
use super::types::*;

mod node;

pub fn parse_generic_reply_for_proof_checking(json_msg: &SJsonValue, raw_msg: &str) -> Option<Vec<ParsedSP>> {
    let type_ = if let Some(type_) = json_msg["type"].as_str() {
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: type_: {:?}", type_);
        type_
    } else {
        debug!("TransactionHandler::parse_generic_reply_for_proof_checking: <<< No type field");
        return None;
    };

    if REQUESTS_FOR_STATE_PROOFS.contains(&type_) {
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: built-in");
        _parse_reply_for_builtin_sp(json_msg, type_)
    } else if let Some((parser, free)) = PoolService::get_sp_parser(type_) {
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: plugged: parser {:?}, free {:?}",
               parser, free);

        let msg = CString::new(raw_msg).ok()?;
        let mut parsed_c_str = ::std::ptr::null();
        let err = parser(msg.as_ptr(), &mut parsed_c_str);
        if err != ErrorCode::Success {
            debug!("TransactionHandler::parse_generic_reply_for_proof_checking: <<< plugin return err {:?}", err);
            return None;
        }
        let c_str = if parsed_c_str.is_null() { None } else { Some(unsafe { CStr::from_ptr(parsed_c_str) }) };
        let parsed_sps = c_str
            .and_then(|c_str| c_str.to_str().map_err(map_err_trace!()).ok())
            .and_then(|c_str|
                serde_json::from_str::<Vec<ParsedSP>>(c_str)
                    .map_err(|err|
                        debug!("TransactionHandler::parse_generic_reply_for_proof_checking: <<< can't parse plugin response {}", err))
                    .ok());

        let err = free(parsed_c_str);
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: plugin free res {:?}", err);

        parsed_sps
    } else {
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: <<< type not supported");
        None
    }
}

pub fn verify_parsed_sp(parsed_sps: Vec<ParsedSP>,
                        nodes: &HashMap<String, Option<VerKey>>,
                        f: usize,
                        gen: &Generator) -> bool {
    for parsed_sp in parsed_sps {
        if parsed_sp.multi_signature["value"]["state_root_hash"].as_str().ne(
            &Some(&parsed_sp.root_hash)) {
            return false;
        }

        let data_to_check_proof_signature =
            _parse_reply_for_proof_signature_checking(&parsed_sp.multi_signature);
        let (signature, participants, value) = unwrap_opt_or_return!(data_to_check_proof_signature, false);
        if !_verify_proof_signature(signature,
                                    participants.as_slice(),
                                    &value,
                                    nodes, f, gen)
            .map_err(|err| warn!("{:?}", err)).unwrap_or(false) {
            return false;
        }

        let proof_nodes = unwrap_or_return!(base64::decode(&parsed_sp.proof_nodes), false);
        let root_hash = unwrap_or_return!(parsed_sp.root_hash.from_base58(), false);
        match parsed_sp.kvs_to_verify {
            KeyValuesInSP::Simple(kvs) => {
                for (k, v) in kvs.kvs {
                    let key = unwrap_or_return!(base64::decode(&k), false);
                    if !_verify_proof(proof_nodes.as_slice(),
                                      root_hash.as_slice(),
                                      &key,
                                      v.as_ref().map(String::as_str)) {
                        return false;
                    }
                }
            }
            //TODO IS-713 support KeyValuesInSP::SubTrie
            kvs @ _ => {
                warn!("Unsupported parsed state proof format for key-values {:?} ", kvs);
                return false;
            }
        }
    }

    true
}

fn _parse_reply_for_builtin_sp(json_msg: &SJsonValue, type_: &str) -> Option<Vec<ParsedSP>> {
    trace!("TransactionHandler::parse_reply_for_builtin_sp: >>> json_msg: {:?}", json_msg);

    assert!(REQUESTS_FOR_STATE_PROOFS.contains(&type_));

    let proof = if let Some(proof) = json_msg["state_proof"]["proof_nodes"].as_str() {
        trace!("TransactionHandler::parse_reply_for_builtin_sp: proof: {:?}", proof);
        proof
    } else {
        trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< No proof");
        return None;
    };

    let root_hash = if let Some(root_hash) = json_msg["state_proof"]["root_hash"].as_str() {
        trace!("TransactionHandler::parse_reply_for_builtin_sp: root_hash: {:?}", root_hash);
        root_hash
    } else {
        trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< No root hash");
        return None;
    };

    // TODO: FIXME: It is a workaround for Node's problem. Node returns some transactions as strings and some as objects.
    // If node returns marshaled json it can contain spaces and it can cause invalid hash.
    // So we have to save the original string too.
    // See https://jira.hyperledger.org/browse/INDY-699
    let (data, parsed_data): (Option<String>, SJsonValue) = match json_msg["data"] {
        SJsonValue::Null => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: Data is null");
            (None, SJsonValue::Null)
        }
        SJsonValue::String(ref str) => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: Data is string");
            if let Ok(parsed_data) = serde_json::from_str(str) {
                (Some(str.to_owned()), parsed_data)
            } else {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< Data field is invalid json");
                return None;
            }
        }
        SJsonValue::Object(ref map) => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: Data is object");
            (Some(json_msg["data"].to_string()), SJsonValue::from(map.clone()))
        }
        _ => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< Data field is invalid type");
            return None;
        }
    };

    trace!("TransactionHandler::parse_reply_for_builtin_sp: data: {:?}, parsed_data: {:?}", data, parsed_data);

    let key_suffix: String = match type_ {
        constants::GET_ATTR => {
            if let Some(attr_name) = json_msg["raw"].as_str()
                .or(json_msg["enc"].as_str())
                .or(json_msg["hash"].as_str()) {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: GET_ATTR attr_name {:?}", attr_name);

                let mut hasher = sha2::Sha256::default();
                hasher.process(attr_name.as_bytes());
                let marker = if ProtocolVersion::is_node_1_3() { '\x01' } else { '1' };
                format!(":{}:{}", marker, hasher.fixed_result().to_hex())
            } else {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< GET_ATTR No key suffix");
                return None;
            }
        }
        constants::GET_CRED_DEF => {
            if let (Some(sign_type), Some(sch_seq_no)) = (json_msg["signature_type"].as_str(),
                                                          json_msg["ref"].as_u64()) {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: GET_CRED_DEF sign_type {:?}, sch_seq_no: {:?}", sign_type, sch_seq_no);
                let marker = if ProtocolVersion::is_node_1_3() { '\x03' } else { '3' };
                format!(":{}:{}:{}", marker, sign_type, sch_seq_no)
            } else {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< GET_CRED_DEF No key suffix");
                return None;
            }
        }
        constants::GET_NYM => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: GET_NYM");<commit_msg>Fix StateProof verification for CredDef from new Node.<commit_after>extern crate digest;
extern crate hex;
extern crate indy_crypto;
extern crate rlp;
extern crate rmp_serde;
extern crate rust_base58;
extern crate sha2;
extern crate sha3;

use api::ErrorCode;
use base64;
use domain::ledger::{constants, request::ProtocolVersion};
use errors::common::CommonError;
use self::digest::FixedOutput;
use self::digest::Input;
use self::hex::ToHex;
use self::indy_crypto::bls::{Bls, Generator, MultiSignature, VerKey};
use self::node::{Node, TrieDB};
use self::rlp::{
    encode as rlp_encode,
    UntrustedRlp,
};
use self::rust_base58::FromBase58;
use self::sha3::Digest;
use serde_json;
use serde_json::Value as SJsonValue;
use services::pool::events::REQUESTS_FOR_STATE_PROOFS;
use std::collections::HashMap;
use std::ffi::{CStr, CString};
use super::PoolService;
use super::types::*;

mod node;

pub fn parse_generic_reply_for_proof_checking(json_msg: &SJsonValue, raw_msg: &str) -> Option<Vec<ParsedSP>> {
    let type_ = if let Some(type_) = json_msg["type"].as_str() {
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: type_: {:?}", type_);
        type_
    } else {
        debug!("TransactionHandler::parse_generic_reply_for_proof_checking: <<< No type field");
        return None;
    };

    if REQUESTS_FOR_STATE_PROOFS.contains(&type_) {
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: built-in");
        _parse_reply_for_builtin_sp(json_msg, type_)
    } else if let Some((parser, free)) = PoolService::get_sp_parser(type_) {
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: plugged: parser {:?}, free {:?}",
               parser, free);

        let msg = CString::new(raw_msg).ok()?;
        let mut parsed_c_str = ::std::ptr::null();
        let err = parser(msg.as_ptr(), &mut parsed_c_str);
        if err != ErrorCode::Success {
            debug!("TransactionHandler::parse_generic_reply_for_proof_checking: <<< plugin return err {:?}", err);
            return None;
        }
        let c_str = if parsed_c_str.is_null() { None } else { Some(unsafe { CStr::from_ptr(parsed_c_str) }) };
        let parsed_sps = c_str
            .and_then(|c_str| c_str.to_str().map_err(map_err_trace!()).ok())
            .and_then(|c_str|
                serde_json::from_str::<Vec<ParsedSP>>(c_str)
                    .map_err(|err|
                        debug!("TransactionHandler::parse_generic_reply_for_proof_checking: <<< can't parse plugin response {}", err))
                    .ok());

        let err = free(parsed_c_str);
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: plugin free res {:?}", err);

        parsed_sps
    } else {
        trace!("TransactionHandler::parse_generic_reply_for_proof_checking: <<< type not supported");
        None
    }
}

pub fn verify_parsed_sp(parsed_sps: Vec<ParsedSP>,
                        nodes: &HashMap<String, Option<VerKey>>,
                        f: usize,
                        gen: &Generator) -> bool {
    for parsed_sp in parsed_sps {
        if parsed_sp.multi_signature["value"]["state_root_hash"].as_str().ne(
            &Some(&parsed_sp.root_hash)) {
            return false;
        }

        let data_to_check_proof_signature =
            _parse_reply_for_proof_signature_checking(&parsed_sp.multi_signature);
        let (signature, participants, value) = unwrap_opt_or_return!(data_to_check_proof_signature, false);
        if !_verify_proof_signature(signature,
                                    participants.as_slice(),
                                    &value,
                                    nodes, f, gen)
            .map_err(|err| warn!("{:?}", err)).unwrap_or(false) {
            return false;
        }

        let proof_nodes = unwrap_or_return!(base64::decode(&parsed_sp.proof_nodes), false);
        let root_hash = unwrap_or_return!(parsed_sp.root_hash.from_base58(), false);
        match parsed_sp.kvs_to_verify {
            KeyValuesInSP::Simple(kvs) => {
                for (k, v) in kvs.kvs {
                    let key = unwrap_or_return!(base64::decode(&k), false);
                    if !_verify_proof(proof_nodes.as_slice(),
                                      root_hash.as_slice(),
                                      &key,
                                      v.as_ref().map(String::as_str)) {
                        return false;
                    }
                }
            }
            //TODO IS-713 support KeyValuesInSP::SubTrie
            kvs @ _ => {
                warn!("Unsupported parsed state proof format for key-values {:?} ", kvs);
                return false;
            }
        }
    }

    true
}

fn _parse_reply_for_builtin_sp(json_msg: &SJsonValue, type_: &str) -> Option<Vec<ParsedSP>> {
    trace!("TransactionHandler::parse_reply_for_builtin_sp: >>> json_msg: {:?}", json_msg);

    assert!(REQUESTS_FOR_STATE_PROOFS.contains(&type_));

    let proof = if let Some(proof) = json_msg["state_proof"]["proof_nodes"].as_str() {
        trace!("TransactionHandler::parse_reply_for_builtin_sp: proof: {:?}", proof);
        proof
    } else {
        trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< No proof");
        return None;
    };

    let root_hash = if let Some(root_hash) = json_msg["state_proof"]["root_hash"].as_str() {
        trace!("TransactionHandler::parse_reply_for_builtin_sp: root_hash: {:?}", root_hash);
        root_hash
    } else {
        trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< No root hash");
        return None;
    };

    // TODO: FIXME: It is a workaround for Node's problem. Node returns some transactions as strings and some as objects.
    // If node returns marshaled json it can contain spaces and it can cause invalid hash.
    // So we have to save the original string too.
    // See https://jira.hyperledger.org/browse/INDY-699
    let (data, parsed_data): (Option<String>, SJsonValue) = match json_msg["data"] {
        SJsonValue::Null => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: Data is null");
            (None, SJsonValue::Null)
        }
        SJsonValue::String(ref str) => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: Data is string");
            if let Ok(parsed_data) = serde_json::from_str(str) {
                (Some(str.to_owned()), parsed_data)
            } else {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< Data field is invalid json");
                return None;
            }
        }
        SJsonValue::Object(ref map) => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: Data is object");
            (Some(json_msg["data"].to_string()), SJsonValue::from(map.clone()))
        }
        _ => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< Data field is invalid type");
            return None;
        }
    };

    trace!("TransactionHandler::parse_reply_for_builtin_sp: data: {:?}, parsed_data: {:?}", data, parsed_data);

    let key_suffix: String = match type_ {
        constants::GET_ATTR => {
            if let Some(attr_name) = json_msg["raw"].as_str()
                .or(json_msg["enc"].as_str())
                .or(json_msg["hash"].as_str()) {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: GET_ATTR attr_name {:?}", attr_name);

                let mut hasher = sha2::Sha256::default();
                hasher.process(attr_name.as_bytes());
                let marker = if ProtocolVersion::is_node_1_3() { '\x01' } else { '1' };
                format!(":{}:{}", marker, hasher.fixed_result().to_hex())
            } else {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< GET_ATTR No key suffix");
                return None;
            }
        }
        constants::GET_CRED_DEF => {
            if let (Some(sign_type), Some(sch_seq_no)) = (json_msg["signature_type"].as_str(),
                                                          json_msg["ref"].as_u64()) {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: GET_CRED_DEF sign_type {:?}, sch_seq_no: {:?}", sign_type, sch_seq_no);
                let marker = if ProtocolVersion::is_node_1_3() { '\x03' } else { '3' };
                let tag = if ProtocolVersion::is_node_1_3() { None } else { json_msg["tag"].as_str() };
                let tag = tag.map(|t| format!(":{}", t)).unwrap_or("".to_owned());
                format!(":{}:{}:{}{}", marker, sign_type, sch_seq_no, tag)
            } else {
                trace!("TransactionHandler::parse_reply_for_builtin_sp: <<< GET_CRED_DEF No key suffix");
                return None;
            }
        }
        constants::GET_NYM => {
            trace!("TransactionHandler::parse_reply_for_builtin_sp: GET_NYM");
{'fraction_non_alphanumeric': 0.13128792604775139, 'fraction_numerical': 0.0045369477683888165, 'mean_word_length': 2.507159904534606, 'pattern_counts': {'":': 5, '<': 83, '<?xml version=': 0, '>': 53, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3215856', 'n_tokens_mistral': 5375, 'n_tokens_neox': 5306, 'n_words': 981}