// Copyright 2018 The Grin Developers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::sync::Arc; use crate::chain; use crate::core::core::hash::Hashed; use crate::core::core::merkle_proof::MerkleProof; use crate::core::core::{KernelFeatures, TxKernel}; use crate::core::{core, ser}; use crate::p2p; use crate::util; use crate::util::secp::pedersen; use serde; use serde::de::MapAccess; use serde::ser::SerializeStruct; use std::fmt; macro_rules! no_dup { ($field:ident) => { if $field.is_some() { return Err(serde::de::Error::duplicate_field("$field")); } }; } /// API Version Information #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Version { /// Current node API Version (api crate version) pub node_version: String, /// Block header version pub block_header_version: u16, } /// The state of the current fork tip #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Tip { /// Height of the tip (max height of the fork) pub height: u64, // Last block pushed to the fork pub last_block_pushed: String, // Block previous to last pub prev_block_to_last: String, // Total difficulty accumulated on that fork pub total_difficulty: u64, } impl Tip { pub fn from_tip(tip: chain::Tip) -> Tip { Tip { height: tip.height, last_block_pushed: util::to_hex(tip.last_block_h.to_vec()), prev_block_to_last: util::to_hex(tip.prev_block_h.to_vec()), total_difficulty: tip.total_difficulty.to_num(), } } } /// Status page containing different server information #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Status { // The protocol version pub protocol_version: u32, // The user user agent pub user_agent: String, // The current number of connections pub connections: u32, // The state of the current fork Tip pub tip: Tip, } impl Status { pub fn from_tip_and_peers(current_tip: chain::Tip, connections: u32) -> Status { Status { protocol_version: ser::ProtocolVersion::local().into(), user_agent: p2p::msg::USER_AGENT.to_string(), connections: connections, tip: Tip::from_tip(current_tip), } } } /// TxHashSet #[derive(Serialize, Deserialize, Debug, Clone)] pub struct TxHashSet { /// Output Root Hash pub output_root_hash: String, // Rangeproof root hash pub range_proof_root_hash: String, // Kernel set root hash pub kernel_root_hash: String, } impl TxHashSet { pub fn from_head(head: Arc) -> TxHashSet { let roots = head.get_txhashset_roots(); TxHashSet { output_root_hash: roots.output_root.to_hex(), range_proof_root_hash: roots.rproof_root.to_hex(), kernel_root_hash: roots.kernel_root.to_hex(), } } } /// Wrapper around a list of txhashset nodes, so it can be /// presented properly via json #[derive(Serialize, Deserialize, Debug, Clone)] pub struct TxHashSetNode { // The hash pub hash: String, } impl TxHashSetNode { pub fn get_last_n_output(chain: Arc, distance: u64) -> Vec { let mut return_vec = Vec::new(); let last_n = chain.get_last_n_output(distance); for x in last_n { return_vec.push(TxHashSetNode { hash: util::to_hex(x.0.to_vec()), }); } return_vec } pub fn get_last_n_rangeproof(head: Arc, distance: u64) -> Vec { let mut return_vec = Vec::new(); let last_n = head.get_last_n_rangeproof(distance); for elem in last_n { return_vec.push(TxHashSetNode { hash: util::to_hex(elem.0.to_vec()), }); } return_vec } pub fn get_last_n_kernel(head: Arc, distance: u64) -> Vec { let mut return_vec = Vec::new(); let last_n = head.get_last_n_kernel(distance); for elem in last_n { return_vec.push(TxHashSetNode { hash: util::to_hex(elem.0.to_vec()), }); } return_vec } } #[derive(Debug, Serialize, Deserialize, Clone)] pub enum OutputType { Coinbase, Transaction, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Output { /// The output commitment representing the amount pub commit: PrintableCommitment, /// Height of the block which contains the output pub height: u64, /// MMR Index of output pub mmr_index: u64, } impl Output { pub fn new(commit: &pedersen::Commitment, height: u64, mmr_index: u64) -> Output { Output { commit: PrintableCommitment { commit: commit.clone(), }, height: height, mmr_index: mmr_index, } } } #[derive(Debug, Clone)] pub struct PrintableCommitment { pub commit: pedersen::Commitment, } impl PrintableCommitment { pub fn commit(&self) -> pedersen::Commitment { self.commit.clone() } pub fn to_vec(&self) -> Vec { self.commit.0.to_vec() } } impl serde::ser::Serialize for PrintableCommitment { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { serializer.serialize_str(&util::to_hex(self.to_vec())) } } impl<'de> serde::de::Deserialize<'de> for PrintableCommitment { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { deserializer.deserialize_str(PrintableCommitmentVisitor) } } struct PrintableCommitmentVisitor; impl<'de> serde::de::Visitor<'de> for PrintableCommitmentVisitor { type Value = PrintableCommitment; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a Pedersen commitment") } fn visit_str(self, v: &str) -> Result where E: serde::de::Error, { Ok(PrintableCommitment { commit: pedersen::Commitment::from_vec( util::from_hex(String::from(v)).map_err(serde::de::Error::custom)?, ), }) } } // As above, except formatted a bit better for human viewing #[derive(Debug, Clone)] pub struct OutputPrintable { /// The type of output Coinbase|Transaction pub output_type: OutputType, /// The homomorphic commitment representing the output's amount /// (as hex string) pub commit: pedersen::Commitment, /// Whether the output has been spent pub spent: bool, /// Rangeproof (as hex string) pub proof: Option, /// Rangeproof hash (as hex string) pub proof_hash: String, /// Block height at which the output is found pub block_height: Option, /// Merkle Proof pub merkle_proof: Option, /// MMR Position pub mmr_index: u64, } impl OutputPrintable { pub fn from_output( output: &core::Output, chain: Arc, block_header: Option<&core::BlockHeader>, include_proof: bool, include_merkle_proof: bool, ) -> Result { let output_type = if output.is_coinbase() { OutputType::Coinbase } else { OutputType::Transaction }; let out_id = core::OutputIdentifier::from_output(&output); let res = chain.is_unspent(&out_id); let (spent, block_height) = if let Ok(output_pos) = res { (false, Some(output_pos.height)) } else { (true, None) }; let proof = if include_proof { Some(util::to_hex(output.proof.proof.to_vec())) } else { None }; // Get the Merkle proof for all unspent coinbase outputs (to verify maturity on // spend). We obtain the Merkle proof by rewinding the PMMR. // We require the rewind() to be stable even after the PMMR is pruned and // compacted so we can still recreate the necessary proof. let mut merkle_proof = None; if include_merkle_proof && output.is_coinbase() && !spent { if let Some(block_header) = block_header { merkle_proof = chain.get_merkle_proof(&out_id, &block_header).ok(); } }; let output_pos = chain.get_output_pos(&output.commit).unwrap_or(0); Ok(OutputPrintable { output_type, commit: output.commit, spent, proof, proof_hash: util::to_hex(output.proof.hash().to_vec()), block_height, merkle_proof, mmr_index: output_pos, }) } pub fn commit(&self) -> Result { Ok(self.commit.clone()) } pub fn range_proof(&self) -> Result { let proof_str = match self.proof.clone() { Some(p) => p, None => return Err(ser::Error::HexError(format!("output range_proof missing"))), }; let p_vec = util::from_hex(proof_str) .map_err(|_| ser::Error::HexError(format!("invalud output range_proof")))?; let mut p_bytes = [0; util::secp::constants::MAX_PROOF_SIZE]; for i in 0..p_bytes.len() { p_bytes[i] = p_vec[i]; } Ok(pedersen::RangeProof { proof: p_bytes, plen: p_bytes.len(), }) } } impl serde::ser::Serialize for OutputPrintable { fn serialize(&self, serializer: S) -> Result where S: serde::ser::Serializer, { let mut state = serializer.serialize_struct("OutputPrintable", 7)?; state.serialize_field("output_type", &self.output_type)?; state.serialize_field("commit", &util::to_hex(self.commit.0.to_vec()))?; state.serialize_field("spent", &self.spent)?; state.serialize_field("proof", &self.proof)?; state.serialize_field("proof_hash", &self.proof_hash)?; state.serialize_field("block_height", &self.block_height)?; let hex_merkle_proof = &self.merkle_proof.clone().map(|x| x.to_hex()); state.serialize_field("merkle_proof", &hex_merkle_proof)?; state.serialize_field("mmr_index", &self.mmr_index)?; state.end() } } impl<'de> serde::de::Deserialize<'de> for OutputPrintable { fn deserialize(deserializer: D) -> Result where D: serde::de::Deserializer<'de>, { #[derive(Deserialize)] #[serde(field_identifier, rename_all = "snake_case")] enum Field { OutputType, Commit, Spent, Proof, ProofHash, BlockHeight, MerkleProof, MmrIndex, } struct OutputPrintableVisitor; impl<'de> serde::de::Visitor<'de> for OutputPrintableVisitor { type Value = OutputPrintable; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("a print able Output") } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { let mut output_type = None; let mut commit = None; let mut spent = None; let mut proof = None; let mut proof_hash = None; let mut block_height = None; let mut merkle_proof = None; let mut mmr_index = None; while let Some(key) = map.next_key()? { match key { Field::OutputType => { no_dup!(output_type); output_type = Some(map.next_value()?) } Field::Commit => { no_dup!(commit); let val: String = map.next_value()?; let vec = util::from_hex(val.clone()).map_err(serde::de::Error::custom)?; commit = Some(pedersen::Commitment::from_vec(vec)); } Field::Spent => { no_dup!(spent); spent = Some(map.next_value()?) } Field::Proof => { no_dup!(proof); proof = map.next_value()? } Field::ProofHash => { no_dup!(proof_hash); proof_hash = Some(map.next_value()?) } Field::BlockHeight => { no_dup!(block_height); block_height = Some(map.next_value()?) } Field::MerkleProof => { no_dup!(merkle_proof); if let Some(hex) = map.next_value::>()? { if let Ok(res) = MerkleProof::from_hex(&hex) { merkle_proof = Some(res); } else { merkle_proof = Some(MerkleProof::empty()); } } } Field::MmrIndex => { no_dup!(mmr_index); mmr_index = Some(map.next_value()?) } } } if output_type.is_none() || commit.is_none() || spent.is_none() || proof_hash.is_none() || mmr_index.is_none() { return Err(serde::de::Error::custom("invalid output")); } Ok(OutputPrintable { output_type: output_type.unwrap(), commit: commit.unwrap(), spent: spent.unwrap(), proof: proof, proof_hash: proof_hash.unwrap(), block_height: block_height, merkle_proof: merkle_proof, mmr_index: mmr_index.unwrap(), }) } } const FIELDS: &'static [&'static str] = &[ "output_type", "commit", "spent", "proof", "proof_hash", "mmr_index", ]; deserializer.deserialize_struct("OutputPrintable", FIELDS, OutputPrintableVisitor) } } // Printable representation of a block #[derive(Debug, Serialize, Deserialize, Clone)] pub struct TxKernelPrintable { pub features: String, pub fee: u64, pub lock_height: u64, pub excess: String, pub excess_sig: String, } impl TxKernelPrintable { pub fn from_txkernel(k: &core::TxKernel) -> TxKernelPrintable { let features = k.features.as_string(); let (fee, lock_height) = match k.features { KernelFeatures::Plain { fee } => (fee, 0), KernelFeatures::Coinbase => (0, 0), KernelFeatures::HeightLocked { fee, lock_height } => (fee, lock_height), }; TxKernelPrintable { features, fee, lock_height, excess: util::to_hex(k.excess.0.to_vec()), excess_sig: util::to_hex(k.excess_sig.to_raw_data().to_vec()), } } } // Just the information required for wallet reconstruction #[derive(Debug, Serialize, Deserialize, Clone)] pub struct BlockHeaderInfo { // Hash pub hash: String, /// Height of this block since the genesis block (height 0) pub height: u64, /// Hash of the block previous to this in the chain. pub previous: String, } impl BlockHeaderInfo { pub fn from_header(header: &core::BlockHeader) -> BlockHeaderInfo { BlockHeaderInfo { hash: util::to_hex(header.hash().to_vec()), height: header.height, previous: util::to_hex(header.prev_hash.to_vec()), } } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct BlockHeaderPrintable { // Hash pub hash: String, /// Version of the block pub version: u16, /// Height of this block since the genesis block (height 0) pub height: u64, /// Hash of the block previous to this in the chain. pub previous: String, /// Root hash of the header MMR at the previous header. pub prev_root: String, /// rfc3339 timestamp at which the block was built. pub timestamp: String, /// Merklish root of all the commitments in the TxHashSet pub output_root: String, /// Merklish root of all range proofs in the TxHashSet pub range_proof_root: String, /// Merklish root of all transaction kernels in the TxHashSet pub kernel_root: String, /// Nonce increment used to mine this block. pub nonce: u64, /// Size of the cuckoo graph pub edge_bits: u8, /// Nonces of the cuckoo solution pub cuckoo_solution: Vec, /// Total accumulated difficulty since genesis block pub total_difficulty: u64, /// Variable difficulty scaling factor for secondary proof of work pub secondary_scaling: u32, /// Total kernel offset since genesis block pub total_kernel_offset: String, } impl BlockHeaderPrintable { pub fn from_header(header: &core::BlockHeader) -> BlockHeaderPrintable { BlockHeaderPrintable { hash: util::to_hex(header.hash().to_vec()), version: header.version.into(), height: header.height, previous: util::to_hex(header.prev_hash.to_vec()), prev_root: util::to_hex(header.prev_root.to_vec()), timestamp: header.timestamp.to_rfc3339(), output_root: util::to_hex(header.output_root.to_vec()), range_proof_root: util::to_hex(header.range_proof_root.to_vec()), kernel_root: util::to_hex(header.kernel_root.to_vec()), nonce: header.pow.nonce, edge_bits: header.pow.edge_bits(), cuckoo_solution: header.pow.proof.nonces.clone(), total_difficulty: header.pow.total_difficulty.to_num(), secondary_scaling: header.pow.secondary_scaling, total_kernel_offset: header.total_kernel_offset.to_hex(), } } } // Printable representation of a block #[derive(Debug, Serialize, Deserialize, Clone)] pub struct BlockPrintable { /// The block header pub header: BlockHeaderPrintable, // Input transactions pub inputs: Vec, /// A printable version of the outputs pub outputs: Vec, /// A printable version of the transaction kernels pub kernels: Vec, } impl BlockPrintable { pub fn from_block( block: &core::Block, chain: Arc, include_proof: bool, include_merkle_proof: bool, ) -> Result { let inputs = block .inputs() .iter() .map(|x| util::to_hex(x.commitment().0.to_vec())) .collect(); let outputs = block .outputs() .iter() .map(|output| { OutputPrintable::from_output( output, chain.clone(), Some(&block.header), include_proof, include_merkle_proof, ) }) .collect::, _>>()?; let kernels = block .kernels() .iter() .map(|kernel| TxKernelPrintable::from_txkernel(kernel)) .collect(); Ok(BlockPrintable { header: BlockHeaderPrintable::from_header(&block.header), inputs: inputs, outputs: outputs, kernels: kernels, }) } } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct CompactBlockPrintable { /// The block header pub header: BlockHeaderPrintable, /// Full outputs, specifically coinbase output(s) pub out_full: Vec, /// Full kernels, specifically coinbase kernel(s) pub kern_full: Vec, /// Kernels (hex short_ids) pub kern_ids: Vec, } impl CompactBlockPrintable { /// Convert a compact block into a printable representation suitable for /// api response pub fn from_compact_block( cb: &core::CompactBlock, chain: Arc, ) -> Result { let block = chain.get_block(&cb.hash())?; let out_full = cb .out_full() .iter() .map(|x| { OutputPrintable::from_output(x, chain.clone(), Some(&block.header), false, true) }) .collect::, _>>()?; let kern_full = cb .kern_full() .iter() .map(|x| TxKernelPrintable::from_txkernel(x)) .collect(); Ok(CompactBlockPrintable { header: BlockHeaderPrintable::from_header(&cb.header), out_full, kern_full, kern_ids: cb.kern_ids().iter().map(|x| x.to_hex()).collect(), }) } } // For wallet reconstruction, include the header info along with the // transactions in the block #[derive(Debug, Serialize, Deserialize, Clone)] pub struct BlockOutputs { /// The block header pub header: BlockHeaderInfo, /// A printable version of the outputs pub outputs: Vec, } // For traversing all outputs in the UTXO set // transactions in the block #[derive(Debug, Serialize, Deserialize, Clone)] pub struct OutputListing { /// The last available output index pub highest_index: u64, /// The last insertion index retrieved pub last_retrieved_index: u64, /// A printable version of the outputs pub outputs: Vec, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct LocatedTxKernel { pub tx_kernel: TxKernel, pub height: u64, pub mmr_index: u64, } #[derive(Serialize, Deserialize)] pub struct PoolInfo { /// Size of the pool pub pool_size: usize, } #[cfg(test)] mod test { use super::*; use serde_json; #[test] fn serialize_output_printable() { let hex_output = "{\ \"output_type\":\"Coinbase\",\ \"commit\":\"083eafae5d61a85ab07b12e1a51b3918d8e6de11fc6cde641d54af53608aa77b9f\",\ \"spent\":false,\ \"proof\":null,\ \"proof_hash\":\"ed6ba96009b86173bade6a9227ed60422916593fa32dd6d78b25b7a4eeef4946\",\ \"block_height\":0,\ \"merkle_proof\":null,\ \"mmr_index\":0\ }"; let deserialized: OutputPrintable = serde_json::from_str(&hex_output).unwrap(); let serialized = serde_json::to_string(&deserialized).unwrap(); assert_eq!(serialized, hex_output); } #[test] fn serialize_output() { let hex_commit = "{\ \"commit\":\"083eafae5d61a85ab07b12e1a51b3918d8e6de11fc6cde641d54af53608aa77b9f\",\ \"height\":0,\ \"mmr_index\":0\ }"; let deserialized: Output = serde_json::from_str(&hex_commit).unwrap(); let serialized = serde_json::to_string(&deserialized).unwrap(); assert_eq!(serialized, hex_commit); } }