<commit_before>// Copyright 2017 The Exonum Team
//
// 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.

//! This module defines the Exonum services interfaces. Like smart contracts in some other
//! blockchain platforms, Exonum services encapsulate business logic of the blockchain application.

use serde_json::Value;
use iron::Handler;
use mount::Mount;

use std::fmt;
use std::sync::{Arc, RwLock};
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;

use crypto::{Hash, PublicKey, SecretKey};
use storage::{Fork, Snapshot};
use messages::{Message, RawTransaction};
use encoding::Error as MessageError;
use node::{ApiSender, Node, State};
use node::state::ValidatorState;
use blockchain::{Blockchain, ConsensusConfig, ValidatorKeys};
use helpers::{Height, Milliseconds, Round};

/// A trait that describes transaction processing rules (a group of sequential operations
/// with the Exonum storage) for the given `Message`.
pub trait Transaction: Message + 'static {
    /// Verifies the transaction, which includes the message signature verification and other
    /// specific internal constraints. verify is intended to check the internal consistency of
    /// a transaction; it has no access to the blockchain state.
    /// If a transaction fails verify, it is considered incorrect and cannot be included into
    /// any correct block proposal. Incorrect transactions are never included into the blockchain.
    ///
    /// *This method should not use external data, that is, it must be a pure function.*
    fn verify(&self) -> bool;
    /// Takes the current blockchain state via `fork` and can modify it if certain conditions
    /// are met.
    ///
    /// # Notes
    ///
    /// - When programming `execute`, you should perform state-related checks before any changes
    /// to the state and return early if these checks fail.
    /// - If the execute method of a transaction raises a `panic`, the changes made by the
    /// transactions are discarded, but the transaction itself is still considered committed.
    fn execute(&self, fork: &mut Fork);
    /// Returns the useful information about the transaction in the JSON format. The returned value
    /// is used to fill the [`TxInfo.content`] field in [the blockchain explorer][explorer].
    ///
    /// # Notes
    ///
    /// The default implementation returns `null`. For transactions defined with
    /// the [`message!`] macro, you may redefine `info()` as
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// extern crate serde_json;
    /// # use exonum::blockchain::Transaction;
    /// # use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 8;
    /// #       field foo: u64 [0 => 8]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     // Other methods...
    /// #   fn verify(&self) -> bool { true }
    /// #   fn execute(&self, _: &mut Fork) { }
    ///
    ///     fn info(&self) -> serde_json::Value {
    ///         serde_json::to_value(self).expect("Cannot serialize transaction to JSON")
    ///     }
    /// }
    /// # fn main() { }
    /// ```
    ///
    /// [`TxInfo.content`]: ../explorer/struct.TxInfo.html#structfield.content
    /// [explorer]: ../explorer/index.html
    /// [`message!`]: ../macro.message.html
    fn info(&self) -> Value {
        Value::Null
    }
}

/// A trait that describes a business-logic of the concrete service.
#[allow(unused_variables, unused_mut)]
pub trait Service: Send + Sync + 'static {
    /// Unique service identification for database schema and service messages.
    fn service_id(&self) -> u16;

    /// Unique human readable service name.
    fn service_name(&self) -> &'static str;

    /// Returns a list of root hashes of tables that determine the current state
    /// of the service database. These hashes are collected from all services in a common
    ///  `MerklePatriciaTable` that named [`state_hash_aggregator`][1].
    ///
    /// See also [`service_table_unique_key`][2].
    ///
    /// [1]: struct.Schema.html#method.state_hash_aggregator
    /// [2]: struct.Blockchain.html#method.service_table_unique_key
    fn state_hash(&self, _: &Snapshot) -> Vec<Hash> {
        Vec::new()
    }

    /// Tries to create `Transaction` object from the given raw message.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError>;

    /// By this method you can initialize information schema of service
    /// and generates initial service configuration.
    /// This method is called on genesis block creation event.
    fn initialize(&self, fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Handles commit event. This handler is invoked for each service after commit of the block.
    /// For example service can create some transaction if the specific condition occurred.
    ///
    /// *Try not to perform long operations here*.
    fn handle_commit(&self, context: &mut ServiceContext) {}

    /// Returns api handler for public users.
    fn public_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }

    /// Returns api handler for maintainers.
    fn private_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }
}

/// The current node state on which the blockchain is running, or in other words
/// execution context.
pub trait ServiceContext {
    /// If the current node is validator returns its state.
    /// For other nodes return `None`.
    fn validator_state(&self) -> &Option<ValidatorState>;

    /// Returns the current database snapshot.
    fn snapshot(&self) -> &Snapshot;

    /// Returns the current blockchain height. This height is 'height of last committed block` + 1.
    fn height(&self) -> Height;

    /// Returns the current node round.
    fn round(&self) -> Round;

    /// Returns the current list of validators.
    fn validators(&self) -> &[ValidatorKeys];

    /// Returns current node's public key.
    fn public_key(&self) -> &PublicKey;

    /// Returns current node's secret key.
    fn secret_key(&self) -> &SecretKey;

    /// Returns the actual blockchain global configuration.
    fn actual_consensus_config(&self) -> &ConsensusConfig;

    /// Returns service specific global variables as json value.
    fn actual_service_config(&self, service: &Service) -> &Value;

    /// Adds transaction to the queue.
    /// After the services handle commit event these transactions will be broadcast by node.
    fn add_transaction(&mut self, tx: Box<Transaction>);
}

#[derive(Debug, Default)]
pub struct ApiNodeState {
    incoming_connections: HashSet<SocketAddr>,
    outgoing_connections: HashSet<SocketAddr>,
    reconnects_timeout: HashMap<SocketAddr, Milliseconds>,
    //TODO: update on event?
    peers_info: HashMap<SocketAddr, PublicKey>,
}

impl ApiNodeState {
    fn new() -> ApiNodeState {
        Self::default()
    }
}

/// Shared part of the context, used to take some values from the `Node`s `State`
/// should be used to take some metrics.
#[derive(Clone, Debug)]
pub struct SharedNodeState {
    state: Arc<RwLock<ApiNodeState>>,
    /// Timeout to update api state.
    pub state_update_timeout: Milliseconds,
}

impl SharedNodeState {
    /// Creates new `SharedNodeState`
    pub fn new(state_update_timeout: Milliseconds) -> SharedNodeState {
        SharedNodeState {
            state: Arc::new(RwLock::new(ApiNodeState::new())),
            state_update_timeout,
        }
    }
    /// Return list of connected sockets
    pub fn incoming_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .incoming_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return list of our connection sockets
    pub fn outgoing_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .outgoing_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return reconnects list
    pub fn reconnects_timeout(&self) -> Vec<(SocketAddr, Milliseconds)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .reconnects_timeout
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Return peers info list
    pub fn peers_info(&self) -> Vec<(SocketAddr, PublicKey)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .peers_info
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Update internal state, from `Node` State`
    pub fn update_node_state(&self, state: &State) {
        for (p, c) in state.peers().iter() {
            self.state
                .write()
                .expect("Expected write lock.")
                .peers_info
                .insert(c.addr(), *p);
        }
    }

    /// Returns value of the `state_update_timeout`.
    pub fn state_update_timeout(&self) -> Milliseconds {
        self.state_update_timeout
    }

    /// add incomming connection into state
    pub fn add_incoming_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .insert(addr);
    }
    /// add outgoing connection into state
    pub fn add_outgoing_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .insert(addr);
    }

    /// remove incomming connection from state
    pub fn remove_incoming_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .remove(addr)
    }

    /// remove outgoing connection from state
    pub fn remove_outgoing_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .remove(addr)
    }

    /// Add reconect timeout
    pub fn add_reconnect_timeout(
        &self,
        addr: SocketAddr,
        timeout: Milliseconds,
    ) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .insert(addr, timeout)
    }

    /// Removes reconect timeout and returns the previous value.
    pub fn remove_reconnect_timeout(&self, addr: &SocketAddr) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .remove(addr)
    }
}

/// Provides the current node state to api handlers.
pub struct ApiContext {
    blockchain: Blockchain,
    node_channel: ApiSender,
    public_key: PublicKey,
    secret_key: SecretKey,
}

/// Provides the current node state to api handlers.
impl ApiContext {
    /// Constructs context for the given `Node`.
    pub fn new(node: &Node) -> ApiContext {
        let handler = node.handler();
        ApiContext {
            blockchain: handler.blockchain.clone(),
            node_channel: node.channel(),
            public_key: *node.state().service_public_key(),
            secret_key: node.state().service_secret_key().clone(),
        }
    }

    /// Returns reference to the node's blockchain.
    pub fn blockchain(&self) -> &Blockchain {
        &self.blockchain
    }

    /// Returns reference to the transaction sender.
    pub fn node_channel(&self) -> &ApiSender {
        &self.node_channel
    }

    /// Returns the public key of current node.
    pub fn public_key(&self) -> &PublicKey {
        &self.public_key
    }

    /// Returns the secret key of current node.
    pub fn secret_key(&self) -> &SecretKey {
        &self.secret_key
    }

    /// Returns `Mount` object that aggregates public api handlers.
    pub fn mount_public_api(&self) -> Mount {
        self.blockchain.mount_public_api(self)
    }

    /// Returns `Mount` object that aggregates private api handlers.
    pub fn mount_private_api(&self) -> Mount {
        self.blockchain.mount_private_api(self)
    }
}

impl ::std::fmt::Debug for ApiContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ApiContext(blockchain: {:?}, public_key: {:?})",
            self.blockchain,
            self.public_key
        )
    }
}
<commit_msg>Add "raw" constructor for `ApiContext`<commit_after>// Copyright 2017 The Exonum Team
//
// 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.

//! This module defines the Exonum services interfaces. Like smart contracts in some other
//! blockchain platforms, Exonum services encapsulate business logic of the blockchain application.

use serde_json::Value;
use iron::Handler;
use mount::Mount;

use std::fmt;
use std::sync::{Arc, RwLock};
use std::collections::{HashMap, HashSet};
use std::net::SocketAddr;

use crypto::{Hash, PublicKey, SecretKey};
use storage::{Fork, Snapshot};
use messages::{Message, RawTransaction};
use encoding::Error as MessageError;
use node::{ApiSender, Node, State};
use node::state::ValidatorState;
use blockchain::{Blockchain, ConsensusConfig, ValidatorKeys};
use helpers::{Height, Milliseconds, Round};

/// A trait that describes transaction processing rules (a group of sequential operations
/// with the Exonum storage) for the given `Message`.
pub trait Transaction: Message + 'static {
    /// Verifies the transaction, which includes the message signature verification and other
    /// specific internal constraints. verify is intended to check the internal consistency of
    /// a transaction; it has no access to the blockchain state.
    /// If a transaction fails verify, it is considered incorrect and cannot be included into
    /// any correct block proposal. Incorrect transactions are never included into the blockchain.
    ///
    /// *This method should not use external data, that is, it must be a pure function.*
    fn verify(&self) -> bool;
    /// Takes the current blockchain state via `fork` and can modify it if certain conditions
    /// are met.
    ///
    /// # Notes
    ///
    /// - When programming `execute`, you should perform state-related checks before any changes
    /// to the state and return early if these checks fail.
    /// - If the execute method of a transaction raises a `panic`, the changes made by the
    /// transactions are discarded, but the transaction itself is still considered committed.
    fn execute(&self, fork: &mut Fork);
    /// Returns the useful information about the transaction in the JSON format. The returned value
    /// is used to fill the [`TxInfo.content`] field in [the blockchain explorer][explorer].
    ///
    /// # Notes
    ///
    /// The default implementation returns `null`. For transactions defined with
    /// the [`message!`] macro, you may redefine `info()` as
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// extern crate serde_json;
    /// # use exonum::blockchain::Transaction;
    /// # use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 8;
    /// #       field foo: u64 [0 => 8]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     // Other methods...
    /// #   fn verify(&self) -> bool { true }
    /// #   fn execute(&self, _: &mut Fork) { }
    ///
    ///     fn info(&self) -> serde_json::Value {
    ///         serde_json::to_value(self).expect("Cannot serialize transaction to JSON")
    ///     }
    /// }
    /// # fn main() { }
    /// ```
    ///
    /// [`TxInfo.content`]: ../explorer/struct.TxInfo.html#structfield.content
    /// [explorer]: ../explorer/index.html
    /// [`message!`]: ../macro.message.html
    fn info(&self) -> Value {
        Value::Null
    }
}

/// A trait that describes a business-logic of the concrete service.
#[allow(unused_variables, unused_mut)]
pub trait Service: Send + Sync + 'static {
    /// Unique service identification for database schema and service messages.
    fn service_id(&self) -> u16;

    /// Unique human readable service name.
    fn service_name(&self) -> &'static str;

    /// Returns a list of root hashes of tables that determine the current state
    /// of the service database. These hashes are collected from all services in a common
    ///  `MerklePatriciaTable` that named [`state_hash_aggregator`][1].
    ///
    /// See also [`service_table_unique_key`][2].
    ///
    /// [1]: struct.Schema.html#method.state_hash_aggregator
    /// [2]: struct.Blockchain.html#method.service_table_unique_key
    fn state_hash(&self, _: &Snapshot) -> Vec<Hash> {
        Vec::new()
    }

    /// Tries to create `Transaction` object from the given raw message.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError>;

    /// By this method you can initialize information schema of service
    /// and generates initial service configuration.
    /// This method is called on genesis block creation event.
    fn initialize(&self, fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Handles commit event. This handler is invoked for each service after commit of the block.
    /// For example service can create some transaction if the specific condition occurred.
    ///
    /// *Try not to perform long operations here*.
    fn handle_commit(&self, context: &mut ServiceContext) {}

    /// Returns api handler for public users.
    fn public_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }

    /// Returns api handler for maintainers.
    fn private_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }
}

/// The current node state on which the blockchain is running, or in other words
/// execution context.
pub trait ServiceContext {
    /// If the current node is validator returns its state.
    /// For other nodes return `None`.
    fn validator_state(&self) -> &Option<ValidatorState>;

    /// Returns the current database snapshot.
    fn snapshot(&self) -> &Snapshot;

    /// Returns the current blockchain height. This height is 'height of last committed block` + 1.
    fn height(&self) -> Height;

    /// Returns the current node round.
    fn round(&self) -> Round;

    /// Returns the current list of validators.
    fn validators(&self) -> &[ValidatorKeys];

    /// Returns current node's public key.
    fn public_key(&self) -> &PublicKey;

    /// Returns current node's secret key.
    fn secret_key(&self) -> &SecretKey;

    /// Returns the actual blockchain global configuration.
    fn actual_consensus_config(&self) -> &ConsensusConfig;

    /// Returns service specific global variables as json value.
    fn actual_service_config(&self, service: &Service) -> &Value;

    /// Adds transaction to the queue.
    /// After the services handle commit event these transactions will be broadcast by node.
    fn add_transaction(&mut self, tx: Box<Transaction>);
}

#[derive(Debug, Default)]
pub struct ApiNodeState {
    incoming_connections: HashSet<SocketAddr>,
    outgoing_connections: HashSet<SocketAddr>,
    reconnects_timeout: HashMap<SocketAddr, Milliseconds>,
    //TODO: update on event?
    peers_info: HashMap<SocketAddr, PublicKey>,
}

impl ApiNodeState {
    fn new() -> ApiNodeState {
        Self::default()
    }
}

/// Shared part of the context, used to take some values from the `Node`s `State`
/// should be used to take some metrics.
#[derive(Clone, Debug)]
pub struct SharedNodeState {
    state: Arc<RwLock<ApiNodeState>>,
    /// Timeout to update api state.
    pub state_update_timeout: Milliseconds,
}

impl SharedNodeState {
    /// Creates new `SharedNodeState`
    pub fn new(state_update_timeout: Milliseconds) -> SharedNodeState {
        SharedNodeState {
            state: Arc::new(RwLock::new(ApiNodeState::new())),
            state_update_timeout,
        }
    }
    /// Return list of connected sockets
    pub fn incoming_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .incoming_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return list of our connection sockets
    pub fn outgoing_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .outgoing_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return reconnects list
    pub fn reconnects_timeout(&self) -> Vec<(SocketAddr, Milliseconds)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .reconnects_timeout
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Return peers info list
    pub fn peers_info(&self) -> Vec<(SocketAddr, PublicKey)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .peers_info
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Update internal state, from `Node` State`
    pub fn update_node_state(&self, state: &State) {
        for (p, c) in state.peers().iter() {
            self.state
                .write()
                .expect("Expected write lock.")
                .peers_info
                .insert(c.addr(), *p);
        }
    }

    /// Returns value of the `state_update_timeout`.
    pub fn state_update_timeout(&self) -> Milliseconds {
        self.state_update_timeout
    }

    /// add incomming connection into state
    pub fn add_incoming_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .insert(addr);
    }
    /// add outgoing connection into state
    pub fn add_outgoing_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .insert(addr);
    }

    /// remove incomming connection from state
    pub fn remove_incoming_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .remove(addr)
    }

    /// remove outgoing connection from state
    pub fn remove_outgoing_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .remove(addr)
    }

    /// Add reconect timeout
    pub fn add_reconnect_timeout(
        &self,
        addr: SocketAddr,
        timeout: Milliseconds,
    ) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .insert(addr, timeout)
    }

    /// Removes reconect timeout and returns the previous value.
    pub fn remove_reconnect_timeout(&self, addr: &SocketAddr) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .remove(addr)
    }
}

/// Provides the current node state to api handlers.
pub struct ApiContext {
    blockchain: Blockchain,
    node_channel: ApiSender,
    public_key: PublicKey,
    secret_key: SecretKey,
}

/// Provides the current node state to api handlers.
impl ApiContext {
    /// Constructs context for the given `Node`.
    pub fn new(node: &Node) -> ApiContext {
        let handler = node.handler();
        ApiContext {
            blockchain: handler.blockchain.clone(),
            node_channel: node.channel(),
            public_key: *node.state().service_public_key(),
            secret_key: node.state().service_secret_key().clone(),
        }
    }

    /// Constructs context from raw parts.
    pub fn from_parts(
        blockchain: &Blockchain,
        node_channel: ApiSender,
        public_key: &PublicKey,
        secret_key: &SecretKey,
    ) -> ApiContext {
        ApiContext {
            blockchain: blockchain.clone(),
            node_channel,
            public_key: *public_key,
            secret_key: secret_key.clone(),
        }
    }

    /// Returns reference to the node's blockchain.
    pub fn blockchain(&self) -> &Blockchain {
        &self.blockchain
    }

    /// Returns reference to the transaction sender.
    pub fn node_channel(&self) -> &ApiSender {
        &self.node_channel
    }

    /// Returns the public key of current node.
    pub fn public_key(&self) -> &PublicKey {
        &self.public_key
    }

    /// Returns the secret key of current node.
    pub fn secret_key(&self) -> &SecretKey {
        &self.secret_key
    }

    /// Returns `Mount` object that aggregates public api handlers.
    pub fn mount_public_api(&self) -> Mount {
        self.blockchain.mount_public_api(self)
    }

    /// Returns `Mount` object that aggregates private api handlers.
    pub fn mount_private_api(&self) -> Mount {
        self.blockchain.mount_private_api(self)
    }
}

impl ::std::fmt::Debug for ApiContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ApiContext(blockchain: {:?}, public_key: {:?})",
            self.blockchain,
            self.public_key
        )
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// 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.

//! This module defines the Exonum services interfaces. Like smart contracts in some other
//! blockchain platforms, Exonum services encapsulate business logic of the blockchain application.

use serde_json::Value;
use iron::Handler;
use mount::Mount;

use std::fmt;
use std::sync::{Arc, RwLock};
use std::collections::{HashSet, HashMap};
use std::net::SocketAddr;

use events::Milliseconds;
use crypto::{Hash, PublicKey, SecretKey};
use storage::{Snapshot, Fork};
use messages::{Message, RawTransaction};
use encoding::Error as MessageError;
use node::{Node, State, NodeChannel, ApiSender};
use node::state::ValidatorState;
use blockchain::{ConsensusConfig, Blockchain, ValidatorKeys};
use helpers::{Height, Round};

/// A trait that describes transaction processing rules (a group of sequential operations
/// with the Exonum storage) for the given `Message`.
pub trait Transaction: Message + 'static {
    /// Verifies the transaction, which includes the message signature verification and other
    /// specific internal constraints. verify is intended to check the internal consistency of
    /// a transaction; it has no access to the blockchain state.
    /// If a transaction fails verify, it is considered incorrect and cannot be included into
    /// any correct block proposal. Incorrect transactions are never included into the blockchain.
    ///
    /// *This method should not use external data, that is, it must be a pure function.*
    fn verify(&self) -> bool;
    /// Takes the current blockchain state via `fork` and can modify it if certain conditions
    /// are met.
    ///
    /// # Notes
    ///
    /// - When programming `execute`, you should perform state-related checks before any changes
    /// to the state and return early if these checks fail.
    /// - If the execute method of a transaction raises a `panic`, the changes made by the
    /// transactions are discarded, but the transaction itself is still considered committed.
    fn execute(&self, fork: &mut Fork);
    /// Returns the useful information about the transaction in the JSON format.
    fn info(&self) -> Value {
        Value::Null
    }
}

/// A trait that describes a business-logic of the concrete service.
#[allow(unused_variables, unused_mut)]
pub trait Service: Send + Sync + 'static {
    /// Unique service identification for database schema and service messages.
    fn service_id(&self) -> u16;

    /// Unique human readable service name.
    fn service_name(&self) -> &'static str;

    /// Returns a list of root hashes of tables that determine the current state
    /// of the service database. These hashes are collected from all services in a common
    ///  `MerklePatriciaTable` that named [`state_hash_aggregator`][1].
    ///
    /// See also [`service_table_unique_key`][2].
    ///
    /// [1]: struct.Schema.html#method.state_hash_aggregator
    /// [2]: struct.Blockchain.html#method.service_table_unique_key
    fn state_hash(&self, _: &Snapshot) -> Vec<Hash> {
        Vec::new()
    }

    /// Tries to create `Transaction` object from the given raw message.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError>;

    /// By this method you can initialize information schema of service
    /// and generates initial service configuration.
    /// This method is called on genesis block creation event.
    fn initialize(&self, fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Handles commit event. This handler is invoked for each service after commit of the block.
    /// For example service can create some transaction if the specific condition occurred.
    ///
    /// *Try not to perform long operations here*.
    fn handle_commit(&self, context: &mut ServiceContext) {}

    /// Returns api handler for public users.
    fn public_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }

    /// Returns api handler for maintainers.
    fn private_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }
}

/// The current node state on which the blockchain is running, or in other words
/// execution context.
pub struct ServiceContext<'a, 'b> {
    state: &'a mut State,
    snapshot: &'b Snapshot,
    txs: Vec<Box<Transaction>>,
}

impl<'a, 'b> ServiceContext<'a, 'b> {
    #[doc(hidden)]
    pub fn new(state: &'a mut State, snapshot: &'b Snapshot) -> ServiceContext<'a, 'b> {
        ServiceContext {
            state: state,
            snapshot: snapshot,
            txs: Vec::new(),
        }
    }

    /// If the current node is validator returns its state.
    /// For other nodes return `None`.
    pub fn validator_state(&self) -> &Option<ValidatorState> {
        self.state.validator_state()
    }

    /// Returns the current database snapshot.
    pub fn snapshot(&self) -> &'b Snapshot {
        self.snapshot
    }

    /// Returns the current blockchain height. This height is 'height of last committed block` + 1.
    pub fn height(&self) -> Height {
        self.state.height()
    }

    /// Returns the current node round.
    pub fn round(&self) -> Round {
        self.state.round()
    }

    /// Returns the current list of validators.
    pub fn validators(&self) -> &[ValidatorKeys] {
        self.state.validators()
    }

    /// Returns current node's public key.
    pub fn public_key(&self) -> &PublicKey {
        self.state.service_public_key()
    }

    /// Returns current node's secret key.
    pub fn secret_key(&self) -> &SecretKey {
        self.state.service_secret_key()
    }

    /// Returns the actual blockchain global configuration.
    pub fn actual_consensus_config(&self) -> &ConsensusConfig {
        self.state.consensus_config()
    }

    /// Returns service specific global variables as json value.
    pub fn actual_service_config(&self, service: &Service) -> &Value {
        let name = service.service_name();
        self.state.services_config().get(name).unwrap()
    }

    /// Adds transaction to the queue.
    /// After the services handle commit event these transactions will be broadcast by node.
    pub fn add_transaction(&mut self, tx: Box<Transaction>) {
        assert!(tx.verify());
        self.txs.push(tx);
    }

    #[doc(hidden)]
    pub fn transactions(self) -> Vec<Box<Transaction>> {
        self.txs
    }
}

impl<'a, 'b> fmt::Debug for ServiceContext<'a, 'b> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ServiceContext(state: {:?}, txs: {:?})",
            self.state,
            self.txs
        )
    }
}

#[derive(Debug, Default)]
pub struct ApiNodeState {
    incoming_connections: HashSet<SocketAddr>,
    outgoing_connections: HashSet<SocketAddr>,
    reconnects_timeout: HashMap<SocketAddr, Milliseconds>,
    //TODO: update on event?
    peers_info: HashMap<SocketAddr, PublicKey>,
}
impl ApiNodeState {
    fn new() -> ApiNodeState {
        Self::default()
    }
}

/// Shared part of the context, used to take some values from the `Node`s `State`
/// should be used to take some metrics.
#[derive(Clone, Debug)]
pub struct SharedNodeState {
    state: Arc<RwLock<ApiNodeState>>,
    /// Timeout to update api state.
    pub state_update_timeout: Milliseconds,
}

impl SharedNodeState {
    /// Creates new `SharedNodeState`
    pub fn new(state_update_timeout: Milliseconds) -> SharedNodeState {
        SharedNodeState {
            state: Arc::new(RwLock::new(ApiNodeState::new())),
            state_update_timeout,
        }
    }
    /// Return list of connected sockets
    pub fn incoming_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .incoming_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return list of our connection sockets
    pub fn outgoing_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .outgoing_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return reconnects list
    pub fn reconnects_timeout(&self) -> Vec<(SocketAddr, Milliseconds)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .reconnects_timeout
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Return peers info list
    pub fn peers_info(&self) -> Vec<(SocketAddr, PublicKey)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .peers_info
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Update internal state, from `Node` State`
    pub fn update_node_state(&self, state: &State) {
        for (p, c) in state.peers().iter() {
            self.state
                .write()
                .expect("Expected write lock.")
                .peers_info
                .insert(c.addr(), *p);
        }
    }

    /// Returns value of the `state_update_timeout`.
    pub fn state_update_timeout(&self) -> Milliseconds {
        self.state_update_timeout
    }

    /// add incomming connection into state
    pub fn add_incoming_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .insert(addr);
    }
    /// add outgoing connection into state
    pub fn add_outgoing_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .insert(addr);
    }

    /// remove incomming connection from state
    pub fn remove_incoming_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .remove(addr)
    }

    /// remove outgoing connection from state
    pub fn remove_outgoing_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .remove(addr)
    }

    /// Add reconect timeout
    pub fn add_reconnect_timeout(
        &self,
        addr: SocketAddr,
        timeout: Milliseconds,
    ) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .insert(addr, timeout)
    }

    /// Removes reconect timeout and returns the previous value.
    pub fn remove_reconnect_timeout(&self, addr: &SocketAddr) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .remove(addr)
    }
}

/// Provides the current node state to api handlers.
pub struct ApiContext {
    blockchain: Blockchain,
    node_channel: ApiSender<NodeChannel>,
    public_key: PublicKey,
    secret_key: SecretKey,
}

/// Provides the current node state to api handlers.
impl ApiContext {
    /// Constructs context for the given `Node`.
    pub fn new(node: &Node) -> ApiContext {
        let handler = node.handler();
        ApiContext {
            blockchain: handler.blockchain.clone(),
            node_channel: node.channel(),
            public_key: *node.state().service_public_key(),
            secret_key: node.state().service_secret_key().clone(),
        }
    }

    /// Returns reference to the node's blockchain.
    pub fn blockchain(&self) -> &Blockchain {
        &self.blockchain
    }

    /// Returns reference to the transaction sender.
    pub fn node_channel(&self) -> &ApiSender<NodeChannel> {
        &self.node_channel
    }

    /// Returns the public key of current node.
    pub fn public_key(&self) -> &PublicKey {
        &self.public_key
    }

    /// Returns the secret key of current node.
    pub fn secret_key(&self) -> &SecretKey {
        &self.secret_key
    }

    /// Returns `Mount` object that aggregates public api handlers.
    pub fn mount_public_api(&self) -> Mount {
        self.blockchain.mount_public_api(self)
    }

    /// Returns `Mount` object that aggregates private api handlers.
    pub fn mount_private_api(&self) -> Mount {
        self.blockchain.mount_private_api(self)
    }
}

impl ::std::fmt::Debug for ApiContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ApiContext(blockchain: {:?}, public_key: {:?})",
            self.blockchain,
            self.public_key
        )
    }
}
<commit_msg>Clarify `Transaction.info()` usage<commit_after>// Copyright 2017 The Exonum Team
//
// 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.

//! This module defines the Exonum services interfaces. Like smart contracts in some other
//! blockchain platforms, Exonum services encapsulate business logic of the blockchain application.

use serde_json::Value;
use iron::Handler;
use mount::Mount;

use std::fmt;
use std::sync::{Arc, RwLock};
use std::collections::{HashSet, HashMap};
use std::net::SocketAddr;

use events::Milliseconds;
use crypto::{Hash, PublicKey, SecretKey};
use storage::{Snapshot, Fork};
use messages::{Message, RawTransaction};
use encoding::Error as MessageError;
use node::{Node, State, NodeChannel, ApiSender};
use node::state::ValidatorState;
use blockchain::{ConsensusConfig, Blockchain, ValidatorKeys};
use helpers::{Height, Round};

/// A trait that describes transaction processing rules (a group of sequential operations
/// with the Exonum storage) for the given `Message`.
pub trait Transaction: Message + 'static {
    /// Verifies the transaction, which includes the message signature verification and other
    /// specific internal constraints. verify is intended to check the internal consistency of
    /// a transaction; it has no access to the blockchain state.
    /// If a transaction fails verify, it is considered incorrect and cannot be included into
    /// any correct block proposal. Incorrect transactions are never included into the blockchain.
    ///
    /// *This method should not use external data, that is, it must be a pure function.*
    fn verify(&self) -> bool;
    /// Takes the current blockchain state via `fork` and can modify it if certain conditions
    /// are met.
    ///
    /// # Notes
    ///
    /// - When programming `execute`, you should perform state-related checks before any changes
    /// to the state and return early if these checks fail.
    /// - If the execute method of a transaction raises a `panic`, the changes made by the
    /// transactions are discarded, but the transaction itself is still considered committed.
    fn execute(&self, fork: &mut Fork);
    /// Returns the useful information about the transaction in the JSON format. The returned value
    /// is used to fill the [`TxInfo.content`] field in [the blockchain explorer][explorer].
    ///
    /// # Notes
    ///
    /// The default implementation returns `null`. For transactions defined with
    /// the [`message!`] macro, you may redefine `info()` as
    ///
    /// ```
    /// # #[macro_use] extern crate exonum;
    /// extern crate serde_json;
    /// # use exonum::blockchain::Transaction;
    /// # use exonum::storage::Fork;
    ///
    /// message! {
    ///     struct MyTransaction {
    ///         // Transaction definition...
    /// #       const TYPE = 1;
    /// #       const ID = 1;
    /// #       const SIZE = 8;
    /// #       field foo: u64 [0 => 8]
    ///     }
    /// }
    ///
    /// impl Transaction for MyTransaction {
    ///     // Other methods...
    /// #   fn verify(&self) -> bool { true }
    /// #   fn execute(&self, _: &mut Fork) { }
    ///
    ///     fn info(&self) -> serde_json::Value {
    ///         serde_json::to_value(self).expect("Cannot serialize transaction to JSON")
    ///     }
    /// }
    /// # fn main() { }
    /// ```
    ///
    /// [`TxInfo.content`]: ../explorer/struct.TxInfo.html#structfield.content
    /// [explorer]: ../explorer/index.html
    /// [`message!`]: ../macro.message.html
    fn info(&self) -> Value {
        Value::Null
    }
}

/// A trait that describes a business-logic of the concrete service.
#[allow(unused_variables, unused_mut)]
pub trait Service: Send + Sync + 'static {
    /// Unique service identification for database schema and service messages.
    fn service_id(&self) -> u16;

    /// Unique human readable service name.
    fn service_name(&self) -> &'static str;

    /// Returns a list of root hashes of tables that determine the current state
    /// of the service database. These hashes are collected from all services in a common
    ///  `MerklePatriciaTable` that named [`state_hash_aggregator`][1].
    ///
    /// See also [`service_table_unique_key`][2].
    ///
    /// [1]: struct.Schema.html#method.state_hash_aggregator
    /// [2]: struct.Blockchain.html#method.service_table_unique_key
    fn state_hash(&self, _: &Snapshot) -> Vec<Hash> {
        Vec::new()
    }

    /// Tries to create `Transaction` object from the given raw message.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError>;

    /// By this method you can initialize information schema of service
    /// and generates initial service configuration.
    /// This method is called on genesis block creation event.
    fn initialize(&self, fork: &mut Fork) -> Value {
        Value::Null
    }

    /// Handles commit event. This handler is invoked for each service after commit of the block.
    /// For example service can create some transaction if the specific condition occurred.
    ///
    /// *Try not to perform long operations here*.
    fn handle_commit(&self, context: &mut ServiceContext) {}

    /// Returns api handler for public users.
    fn public_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }

    /// Returns api handler for maintainers.
    fn private_api_handler(&self, context: &ApiContext) -> Option<Box<Handler>> {
        None
    }
}

/// The current node state on which the blockchain is running, or in other words
/// execution context.
pub struct ServiceContext<'a, 'b> {
    state: &'a mut State,
    snapshot: &'b Snapshot,
    txs: Vec<Box<Transaction>>,
}

impl<'a, 'b> ServiceContext<'a, 'b> {
    #[doc(hidden)]
    pub fn new(state: &'a mut State, snapshot: &'b Snapshot) -> ServiceContext<'a, 'b> {
        ServiceContext {
            state: state,
            snapshot: snapshot,
            txs: Vec::new(),
        }
    }

    /// If the current node is validator returns its state.
    /// For other nodes return `None`.
    pub fn validator_state(&self) -> &Option<ValidatorState> {
        self.state.validator_state()
    }

    /// Returns the current database snapshot.
    pub fn snapshot(&self) -> &'b Snapshot {
        self.snapshot
    }

    /// Returns the current blockchain height. This height is 'height of last committed block` + 1.
    pub fn height(&self) -> Height {
        self.state.height()
    }

    /// Returns the current node round.
    pub fn round(&self) -> Round {
        self.state.round()
    }

    /// Returns the current list of validators.
    pub fn validators(&self) -> &[ValidatorKeys] {
        self.state.validators()
    }

    /// Returns current node's public key.
    pub fn public_key(&self) -> &PublicKey {
        self.state.service_public_key()
    }

    /// Returns current node's secret key.
    pub fn secret_key(&self) -> &SecretKey {
        self.state.service_secret_key()
    }

    /// Returns the actual blockchain global configuration.
    pub fn actual_consensus_config(&self) -> &ConsensusConfig {
        self.state.consensus_config()
    }

    /// Returns service specific global variables as json value.
    pub fn actual_service_config(&self, service: &Service) -> &Value {
        let name = service.service_name();
        self.state.services_config().get(name).unwrap()
    }

    /// Adds transaction to the queue.
    /// After the services handle commit event these transactions will be broadcast by node.
    pub fn add_transaction(&mut self, tx: Box<Transaction>) {
        assert!(tx.verify());
        self.txs.push(tx);
    }

    #[doc(hidden)]
    pub fn transactions(self) -> Vec<Box<Transaction>> {
        self.txs
    }
}

impl<'a, 'b> fmt::Debug for ServiceContext<'a, 'b> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ServiceContext(state: {:?}, txs: {:?})",
            self.state,
            self.txs
        )
    }
}

#[derive(Debug, Default)]
pub struct ApiNodeState {
    incoming_connections: HashSet<SocketAddr>,
    outgoing_connections: HashSet<SocketAddr>,
    reconnects_timeout: HashMap<SocketAddr, Milliseconds>,
    //TODO: update on event?
    peers_info: HashMap<SocketAddr, PublicKey>,
}
impl ApiNodeState {
    fn new() -> ApiNodeState {
        Self::default()
    }
}

/// Shared part of the context, used to take some values from the `Node`s `State`
/// should be used to take some metrics.
#[derive(Clone, Debug)]
pub struct SharedNodeState {
    state: Arc<RwLock<ApiNodeState>>,
    /// Timeout to update api state.
    pub state_update_timeout: Milliseconds,
}

impl SharedNodeState {
    /// Creates new `SharedNodeState`
    pub fn new(state_update_timeout: Milliseconds) -> SharedNodeState {
        SharedNodeState {
            state: Arc::new(RwLock::new(ApiNodeState::new())),
            state_update_timeout,
        }
    }
    /// Return list of connected sockets
    pub fn incoming_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .incoming_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return list of our connection sockets
    pub fn outgoing_connections(&self) -> Vec<SocketAddr> {
        self.state
            .read()
            .expect("Expected read lock.")
            .outgoing_connections
            .iter()
            .cloned()
            .collect()
    }
    /// Return reconnects list
    pub fn reconnects_timeout(&self) -> Vec<(SocketAddr, Milliseconds)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .reconnects_timeout
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Return peers info list
    pub fn peers_info(&self) -> Vec<(SocketAddr, PublicKey)> {
        self.state
            .read()
            .expect("Expected read lock.")
            .peers_info
            .iter()
            .map(|(c, e)| (*c, *e))
            .collect()
    }
    /// Update internal state, from `Node` State`
    pub fn update_node_state(&self, state: &State) {
        for (p, c) in state.peers().iter() {
            self.state
                .write()
                .expect("Expected write lock.")
                .peers_info
                .insert(c.addr(), *p);
        }
    }

    /// Returns value of the `state_update_timeout`.
    pub fn state_update_timeout(&self) -> Milliseconds {
        self.state_update_timeout
    }

    /// add incomming connection into state
    pub fn add_incoming_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .insert(addr);
    }
    /// add outgoing connection into state
    pub fn add_outgoing_connection(&self, addr: SocketAddr) {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .insert(addr);
    }

    /// remove incomming connection from state
    pub fn remove_incoming_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .incoming_connections
            .remove(addr)
    }

    /// remove outgoing connection from state
    pub fn remove_outgoing_connection(&self, addr: &SocketAddr) -> bool {
        self.state
            .write()
            .expect("Expected write lock")
            .outgoing_connections
            .remove(addr)
    }

    /// Add reconect timeout
    pub fn add_reconnect_timeout(
        &self,
        addr: SocketAddr,
        timeout: Milliseconds,
    ) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .insert(addr, timeout)
    }

    /// Removes reconect timeout and returns the previous value.
    pub fn remove_reconnect_timeout(&self, addr: &SocketAddr) -> Option<Milliseconds> {
        self.state
            .write()
            .expect("Expected write lock")
            .reconnects_timeout
            .remove(addr)
    }
}

/// Provides the current node state to api handlers.
pub struct ApiContext {
    blockchain: Blockchain,
    node_channel: ApiSender<NodeChannel>,
    public_key: PublicKey,
    secret_key: SecretKey,
}

/// Provides the current node state to api handlers.
impl ApiContext {
    /// Constructs context for the given `Node`.
    pub fn new(node: &Node) -> ApiContext {
        let handler = node.handler();
        ApiContext {
            blockchain: handler.blockchain.clone(),
            node_channel: node.channel(),
            public_key: *node.state().service_public_key(),
            secret_key: node.state().service_secret_key().clone(),
        }
    }

    /// Returns reference to the node's blockchain.
    pub fn blockchain(&self) -> &Blockchain {
        &self.blockchain
    }

    /// Returns reference to the transaction sender.
    pub fn node_channel(&self) -> &ApiSender<NodeChannel> {
        &self.node_channel
    }

    /// Returns the public key of current node.
    pub fn public_key(&self) -> &PublicKey {
        &self.public_key
    }

    /// Returns the secret key of current node.
    pub fn secret_key(&self) -> &SecretKey {
        &self.secret_key
    }

    /// Returns `Mount` object that aggregates public api handlers.
    pub fn mount_public_api(&self) -> Mount {
        self.blockchain.mount_public_api(self)
    }

    /// Returns `Mount` object that aggregates private api handlers.
    pub fn mount_private_api(&self) -> Mount {
        self.blockchain.mount_private_api(self)
    }
}

impl ::std::fmt::Debug for ApiContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "ApiContext(blockchain: {:?}, public_key: {:?})",
            self.blockchain,
            self.public_key
        )
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use common::PerfLogger;
use lsp_types::request::Request;
use schema_documentation::SchemaDocumentation;

use crate::{LSPRuntimeResult, LSPState};

pub(crate) struct HeartbeatRequest;

impl Request for HeartbeatRequest {
    type Params = ();
    type Result = String;
    const METHOD: &'static str = "relay/heartbeat";
}

#[allow(clippy::unnecessary_wraps)]
pub(crate) fn on_heartbeat<
    TPerfLogger: PerfLogger + 'static,
    TSchemaDocumentation: SchemaDocumentation,
>(
    _state: &LSPState<TPerfLogger, TSchemaDocumentation>,
    _params: <HeartbeatRequest as Request>::Params,
) -> LSPRuntimeResult<<HeartbeatRequest as Request>::Result> {
    Ok("Connected.".to_string())
}
<commit_msg>use GlobalState in heartbeat<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use lsp_types::request::Request;

use crate::LSPRuntimeResult;

use super::GlobalState;

pub(crate) struct HeartbeatRequest;

impl Request for HeartbeatRequest {
    type Params = ();
    type Result = String;
    const METHOD: &'static str = "relay/heartbeat";
}

#[allow(clippy::unnecessary_wraps)]
pub(crate) fn on_heartbeat(
    _state: &impl GlobalState,
    _params: <HeartbeatRequest as Request>::Params,
) -> LSPRuntimeResult<<HeartbeatRequest as Request>::Result> {
    Ok("Connected.".to_string())
}
<|endoftext|>


<commit_before>use memory::Memory;
use std;

/// Like `/dev/null`.
///
/// The `NullMemory` is always 2^32 bytes large (osci’s memory size),
/// reads `0` everywhere and discards all writes.
///
/// # Examples
/// ```
/// use osciemu::memory::Memory;
/// use osciemu::memory::NullMemory;
///
/// let mut m = NullMemory::new();
/// assert_eq!(m.get(0), 0);
/// m.set(0, 1);
/// assert_eq!(m.get(0), 0);
/// ```
pub struct NullMemory;

impl NullMemory {
    pub fn new() -> NullMemory {
        NullMemory
    }
}

impl Memory for NullMemory {
    #[inline]
    fn get(&self, _: usize) -> u32 {
        0
    }

    #[inline]
    fn set(&mut self, _: usize, _: u32) {}

    #[inline]
    fn size(&self) -> usize {
        return std::u32::MAX as usize;
    }
}

#[cfg(test)]
mod test {
    use memory::Memory;

    #[test]
    fn nullmemory_read() {
        let m = super::NullMemory::new();
        assert_eq!(m.get(0), 0);
        assert_eq!(m.get(123), 0);
        assert_eq!(m.get(u32::max_value() as usize), 0);
    }

    #[test]
    fn nullmemory_write() {
        let mut m = super::NullMemory::new();
        assert_eq!(m.get(0), 0);
        m.set(0, 4);
        assert_eq!(m.get(0), 0);
    }
}
<commit_msg>style(nullmemory): Don’t use return you n00b<commit_after>use memory::Memory;
use std;

/// Like `/dev/null`.
///
/// The `NullMemory` is always 2^32 bytes large (osci’s memory size),
/// reads `0` everywhere and discards all writes.
///
/// # Examples
/// ```
/// use osciemu::memory::Memory;
/// use osciemu::memory::NullMemory;
///
/// let mut m = NullMemory::new();
/// assert_eq!(m.get(0), 0);
/// m.set(0, 1);
/// assert_eq!(m.get(0), 0);
/// ```
pub struct NullMemory;

impl NullMemory {
    pub fn new() -> NullMemory {
        NullMemory
    }
}

impl Memory for NullMemory {
    #[inline]
    fn get(&self, _: usize) -> u32 {
        0
    }

    #[inline]
    fn set(&mut self, _: usize, _: u32) {}

    #[inline]
    fn size(&self) -> usize {
        std::u32::MAX as usize
    }
}

#[cfg(test)]
mod test {
    use memory::Memory;

    #[test]
    fn nullmemory_read() {
        let m = super::NullMemory::new();
        assert_eq!(m.get(0), 0);
        assert_eq!(m.get(123), 0);
        assert_eq!(m.get(u32::max_value() as usize), 0);
    }

    #[test]
    fn nullmemory_write() {
        let mut m = super::NullMemory::new();
        assert_eq!(m.get(0), 0);
        m.set(0, 4);
        assert_eq!(m.get(0), 0);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use std::cmp::max;
use std::ops::Deref;

use glib::{Cast, Object};
use gtk::{
    Align,
    CellRendererText,
    ContainerExt,
    IsA,
    ListStore,
    ScrolledWindow,
    ScrolledWindowExt,
    TreeIter,
    TreeModel,
    TreeModelExt,
    TreeSelection,
    TreeView,
    TreeViewColumn,
    WidgetExt,
};
use gtk::PolicyType::{Automatic, Never};
use pango_sys::PangoEllipsizeMode;

use completion::Column::{self, Expand};
use gobject::ObjectExtManual;
use scrolled_window::ScrolledWindowExtManual;
use super::Completer;

const COMPLETION_VIEW_MAX_HEIGHT: i32 = 300;

/// A widget to show completions for the command entry.
pub struct CompletionView {
    unselect_callback: Option<Box<Fn()>>,
    tree_view: TreeView,
    view: ScrolledWindow,
}

impl CompletionView {
    /// Create a new completion view.
    pub fn new() -> Box<Self> {
        let tree_view = TreeView::new();

        tree_view.get_selection().unselect_all();
        tree_view.set_enable_search(false);
        tree_view.set_headers_visible(false);
        tree_view.set_can_focus(false);

        let scrolled_window = ScrolledWindow::new(None, None);
        scrolled_window.set_valign(Align::End);
        scrolled_window.add(&tree_view);
        scrolled_window.set_max_content_height(COMPLETION_VIEW_MAX_HEIGHT);
        scrolled_window.set_propagate_natural_height(true);

        let view = Box::new(CompletionView {
            unselect_callback: None,
            tree_view: tree_view,
            view: scrolled_window,
        });

        view.add_columns(2);

        view
    }

    /// Add a column to the tree view.
    fn add_column(&self, index: i32, foreground_index: i32, column: Column) {
        let view_column = TreeViewColumn::new();
        let cell = CellRendererText::new();
        if column == Expand {
            cell.set_ellipsize_data("ellipsize", PangoEllipsizeMode::End);
            view_column.set_expand(true);
        }
        view_column.pack_start(&cell, true);
        view_column.add_attribute(&cell, "text", index);
        view_column.add_attribute(&cell, "foreground", foreground_index);
        self.tree_view.append_column(&view_column);
    }

    /// Add the specified number of columns.
    fn add_columns(&self, column_count: i32) {
        self.remove_columns();
        for i in 0 .. column_count {
            self.add_column(i, column_count + i, Expand);
        }
    }

    /// Add the specified number of columns.
    fn add_columns_from_completer(&self, completer: &Completer) {
        self.remove_columns();
        let columns = completer.columns();
        let column_count = columns.len() as i32;
        for (i, column) in columns.iter().enumerate() {
            let i = i as i32;
            self.add_column(i, column_count + i, *column);
        }
    }

    /// Adjust the columns from the completer.
    pub fn adjust_columns(&self, completer: &Completer) {
        self.add_columns_from_completer(completer);
    }

    /// Adjust the policy of the scrolled window to avoid having extra space around the tree view.
    pub fn adjust_policy<M: IsA<Object> + IsA<TreeModel>>(&self, model: &M) {
        self.tree_view.set_model(Some(model));
        let policy =
            if model.iter_n_children(None) < 2 {
                Never
            }
            else {
                Automatic
            };
        self.view.set_policy(Never, policy);
    }

    /// Add a callback to the selection changed event.
    pub fn connect_selection_changed<F: Fn(&TreeSelection) + 'static>(&self, callback: F) {
        self.tree_view.get_selection().connect_changed(callback);
    }

    /// Add a callback to the unselect event.
    pub fn connect_unselect<F: Fn() + 'static>(&mut self, callback: F) {
        self.unselect_callback = Some(Box::new(callback));
    }

    /// Delete the current completion item.
    pub fn delete_current_completion_item(&self) {
        if let Some((model, iter)) = self.tree_view.get_selection().get_selected() {
            if let Ok(model) = model.downcast::<ListStore>() {
                self.select_next();
                model.remove(&iter);
            }
        }
    }

    /// Adjust the policy of the scrolled window to avoid having extra space around the tree view.
    pub fn disable_scrollbars(&self) {
        self.view.set_policy(Never, Never);
    }

    /// Remove all the columns.
    fn remove_columns(&self) {
        for column in &self.tree_view.get_columns() {
            self.tree_view.remove_column(column);
        }
    }

    /// Scroll to the selected row.
    fn scroll(&self, model: &TreeModel, iter: &TreeIter) {
        if let Some(path) = model.get_path(iter) {
            self.tree_view.scroll_to_cell(Some(&path), None, false, 0.0, 0.0);
        }
    }

    /// Scroll to the first row.
    pub fn scroll_to_first(&self) {
        if let Some(model) = self.tree_view.get_model() {
            if let Some(iter) = model.get_iter_first() {
                if let Some(path) = model.get_path(&iter) {
                    self.tree_view.scroll_to_cell(Some(&path), None, false, 0.0, 0.0);
                }
            }
        }
    }

    /// Select the next item.
    /// This loops with the value that started the completion.
    pub fn select_next(&self) {
        if let Some(model) = self.tree_view.get_model() {
            let selection = self.tree_view.get_selection();
            if let Some((model, selected_iter)) = selection.get_selected() {
                if model.iter_next(&selected_iter) {
                    selection.select_iter(&selected_iter);
                    self.scroll(&model, &selected_iter);
                }
                else {
                    self.unselect();
                    self.show_original_input();
                }
            }
            else if let Some(iter) = model.get_iter_first() {
                self.scroll(&model, &iter);
                selection.select_iter(&iter);
            }
        }
    }

    /// Select the previous item.
    /// This loops with the value that started the completion.
    pub fn select_previous(&self) {
        if let Some(model) = self.tree_view.get_model() {
            let selection = self.tree_view.get_selection();
            if let Some((model, selected_iter)) = selection.get_selected() {
                if model.iter_previous(&selected_iter) {
                    selection.select_iter(&selected_iter);
                    self.scroll(&model, &selected_iter);
                }
                else {
                    self.unselect();
                    self.show_original_input();
                }
            }
            else if let Some(iter) = model.iter_nth_child(None, max(0, model.iter_n_children(None) - 1)) {
                self.scroll(&model, &iter);
                selection.select_iter(&iter);
            }
        }
    }

    /// Set the model.
    pub fn set_model<T: IsA<TreeModel>>(&self, model: Option<&T>) {
        self.tree_view.set_model(model);
    }

    /// Emit the event to show the original input.
    /// This is emitted when the user unselect from the completion view.
    fn show_original_input(&self) {
        if let Some(ref callback) = self.unselect_callback {
            callback();
        }
    }

    /// Unselect the item.
    pub fn unselect(&self) {
        let selection = self.tree_view.get_selection();
        selection.unselect_all();
    }
}

impl Deref for CompletionView {
    type Target = ScrolledWindow;

    fn deref(&self) -> &Self::Target {
        &self.view
    }
}
<commit_msg>Added missing import.<commit_after>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

use std::cmp::max;
use std::ops::Deref;

use glib::{Cast, Object};
use gtk::{
    Align,
    CellLayoutExt,
    CellRendererText,
    ContainerExt,
    IsA,
    ListStore,
    ScrolledWindow,
    ScrolledWindowExt,
    TreeIter,
    TreeModel,
    TreeModelExt,
    TreeSelection,
    TreeView,
    TreeViewColumn,
    WidgetExt,
};
use gtk::PolicyType::{Automatic, Never};
use pango_sys::PangoEllipsizeMode;

use completion::Column::{self, Expand};
use gobject::ObjectExtManual;
use scrolled_window::ScrolledWindowExtManual;
use super::Completer;

const COMPLETION_VIEW_MAX_HEIGHT: i32 = 300;

/// A widget to show completions for the command entry.
pub struct CompletionView {
    unselect_callback: Option<Box<Fn()>>,
    tree_view: TreeView,
    view: ScrolledWindow,
}

impl CompletionView {
    /// Create a new completion view.
    pub fn new() -> Box<Self> {
        let tree_view = TreeView::new();

        tree_view.get_selection().unselect_all();
        tree_view.set_enable_search(false);
        tree_view.set_headers_visible(false);
        tree_view.set_can_focus(false);

        let scrolled_window = ScrolledWindow::new(None, None);
        scrolled_window.set_valign(Align::End);
        scrolled_window.add(&tree_view);
        scrolled_window.set_max_content_height(COMPLETION_VIEW_MAX_HEIGHT);
        scrolled_window.set_propagate_natural_height(true);

        let view = Box::new(CompletionView {
            unselect_callback: None,
            tree_view: tree_view,
            view: scrolled_window,
        });

        view.add_columns(2);

        view
    }

    /// Add a column to the tree view.
    fn add_column(&self, index: i32, foreground_index: i32, column: Column) {
        let view_column = TreeViewColumn::new();
        let cell = CellRendererText::new();
        if column == Expand {
            cell.set_ellipsize_data("ellipsize", PangoEllipsizeMode::End);
            view_column.set_expand(true);
        }
        view_column.pack_start(&cell, true);
        view_column.add_attribute(&cell, "text", index);
        view_column.add_attribute(&cell, "foreground", foreground_index);
        self.tree_view.append_column(&view_column);
    }

    /// Add the specified number of columns.
    fn add_columns(&self, column_count: i32) {
        self.remove_columns();
        for i in 0 .. column_count {
            self.add_column(i, column_count + i, Expand);
        }
    }

    /// Add the specified number of columns.
    fn add_columns_from_completer(&self, completer: &Completer) {
        self.remove_columns();
        let columns = completer.columns();
        let column_count = columns.len() as i32;
        for (i, column) in columns.iter().enumerate() {
            let i = i as i32;
            self.add_column(i, column_count + i, *column);
        }
    }

    /// Adjust the columns from the completer.
    pub fn adjust_columns(&self, completer: &Completer) {
        self.add_columns_from_completer(completer);
    }

    /// Adjust the policy of the scrolled window to avoid having extra space around the tree view.
    pub fn adjust_policy<M: IsA<Object> + IsA<TreeModel>>(&self, model: &M) {
        self.tree_view.set_model(Some(model));
        let policy =
            if model.iter_n_children(None) < 2 {
                Never
            }
            else {
                Automatic
            };
        self.view.set_policy(Never, policy);
    }

    /// Add a callback to the selection changed event.
    pub fn connect_selection_changed<F: Fn(&TreeSelection) + 'static>(&self, callback: F) {
        self.tree_view.get_selection().connect_changed(callback);
    }

    /// Add a callback to the unselect event.
    pub fn connect_unselect<F: Fn() + 'static>(&mut self, callback: F) {
        self.unselect_callback = Some(Box::new(callback));
    }

    /// Delete the current completion item.
    pub fn delete_current_completion_item(&self) {
        if let Some((model, iter)) = self.tree_view.get_selection().get_selected() {
            if let Ok(model) = model.downcast::<ListStore>() {
                self.select_next();
                model.remove(&iter);
            }
        }
    }

    /// Adjust the policy of the scrolled window to avoid having extra space around the tree view.
    pub fn disable_scrollbars(&self) {
        self.view.set_policy(Never, Never);
    }

    /// Remove all the columns.
    fn remove_columns(&self) {
        for column in &self.tree_view.get_columns() {
            self.tree_view.remove_column(column);
        }
    }

    /// Scroll to the selected row.
    fn scroll(&self, model: &TreeModel, iter: &TreeIter) {
        if let Some(path) = model.get_path(iter) {
            self.tree_view.scroll_to_cell(Some(&path), None, false, 0.0, 0.0);
        }
    }

    /// Scroll to the first row.
    pub fn scroll_to_first(&self) {
        if let Some(model) = self.tree_view.get_model() {
            if let Some(iter) = model.get_iter_first() {
                if let Some(path) = model.get_path(&iter) {
                    self.tree_view.scroll_to_cell(Some(&path), None, false, 0.0, 0.0);
                }
            }
        }
    }

    /// Select the next item.
    /// This loops with the value that started the completion.
    pub fn select_next(&self) {
        if let Some(model) = self.tree_view.get_model() {
            let selection = self.tree_view.get_selection();
            if let Some((model, selected_iter)) = selection.get_selected() {
                if model.iter_next(&selected_iter) {
                    selection.select_iter(&selected_iter);
                    self.scroll(&model, &selected_iter);
                }
                else {
                    self.unselect();
                    self.show_original_input();
                }
            }
            else if let Some(iter) = model.get_iter_first() {
                self.scroll(&model, &iter);
                selection.select_iter(&iter);
            }
        }
    }

    /// Select the previous item.
    /// This loops with the value that started the completion.
    pub fn select_previous(&self) {
        if let Some(model) = self.tree_view.get_model() {
            let selection = self.tree_view.get_selection();
            if let Some((model, selected_iter)) = selection.get_selected() {
                if model.iter_previous(&selected_iter) {
                    selection.select_iter(&selected_iter);
                    self.scroll(&model, &selected_iter);
                }
                else {
                    self.unselect();
                    self.show_original_input();
                }
            }
            else if let Some(iter) = model.iter_nth_child(None, max(0, model.iter_n_children(None) - 1)) {
                self.scroll(&model, &iter);
                selection.select_iter(&iter);
            }
        }
    }

    /// Set the model.
    pub fn set_model<T: IsA<TreeModel>>(&self, model: Option<&T>) {
        self.tree_view.set_model(model);
    }

    /// Emit the event to show the original input.
    /// This is emitted when the user unselect from the completion view.
    fn show_original_input(&self) {
        if let Some(ref callback) = self.unselect_callback {
            callback();
        }
    }

    /// Unselect the item.
    pub fn unselect(&self) {
        let selection = self.tree_view.get_selection();
        selection.unselect_all();
    }
}

impl Deref for CompletionView {
    type Target = ScrolledWindow;

    fn deref(&self) -> &Self::Target {
        &self.view
    }
}
<|endoftext|>


<commit_before>use io::regs;

use std::fmt;
use std::mem;

use openssl::bn;

bfdesc!(RegCnt: u32, {
    busy: 0 => 0,
    keyslot: 4 => 5,
    little_endian: 8 => 8,
    normal_order: 9 => 9
});

bfdesc!(RegSlotCnt: u32, {
    key_set: 0 => 0,
    key_prot: 1 => 1
});

pub struct RsaKeyslot {
    write_pos: usize,
    buf: [u8; 0x100]
}

impl Clone for RsaKeyslot {
    fn clone(&self) -> RsaKeyslot {
        let mut new_buf = [0u8; 0x100];
        new_buf.copy_from_slice(&self.buf[..]);

        RsaKeyslot {
            write_pos: self.write_pos,
            buf: new_buf
        }
    }
}

pub struct RsaDeviceState {
    slots: [RsaKeyslot; 4],
    modulus: [u8; 0x100],
    message: [u8; 0x100],
}

impl Default for RsaDeviceState {
    fn default() -> RsaDeviceState {
        let new_keyslot = RsaKeyslot {
            write_pos: 0,
            buf: [0; 0x100],
        };
        RsaDeviceState {
            slots: [new_keyslot.clone(), new_keyslot.clone(),
                    new_keyslot.clone(), new_keyslot],
            modulus: [0; 0x100],
            message: [0; 0x100],
        }
    }
}

impl fmt::Debug for RsaDeviceState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RsaDeviceState {{ }}")
    }
}

fn get_keydata(dev: &RsaDevice, keyslot: usize) -> (u32, u32) {
    match keyslot {
        0 => (dev.slot0_cnt.get(), dev.slot0_len.get()),
        1 => (dev.slot1_cnt.get(), dev.slot1_len.get()),
        2 => (dev.slot2_cnt.get(), dev.slot2_len.get()),
        3 => (dev.slot3_cnt.get(), dev.slot3_len.get()),
        _ => unreachable!()
    }
}

fn reg_slot_cnt_update(dev: &mut RsaDevice, keyslot: usize) {
    let (slot_cnt, slot_len) = get_keydata(dev, keyslot);
    if bf!(slot_cnt @ RegSlotCnt::key_set) == 1 {
        assert_eq!(dev._internal_state.slots[keyslot].write_pos, (slot_len * 4) as usize);
    } else {
        dev._internal_state.slots[keyslot].write_pos = 0;
    }
}

fn reg_cnt_update(dev: &mut RsaDevice) {
    let cnt = dev.cnt.get();
    trace!("Wrote 0x{:08X} to RSA CNT register!", cnt);

    assert_eq!(bf!(cnt @ RegCnt::little_endian), 1);
    assert_eq!(bf!(cnt @ RegCnt::normal_order), 1);
    if bf!(cnt @ RegCnt::busy) == 1 {
        let keyslot = bf!(cnt @ RegCnt::keyslot) as usize;
        let (slot_cnt, _) = get_keydata(dev, keyslot);
        assert_eq!(bf!(slot_cnt @ RegSlotCnt::key_set), 1);

        info!("Performing RSA arithmetic!");

        let base = bn::BigNum::from_slice(&dev._internal_state.message[..]).unwrap();
        let exponent = bn::BigNum::from_slice(&dev._internal_state.slots[keyslot].buf[..]).unwrap();
        let modulus = bn::BigNum::from_slice(&dev._internal_state.modulus[..]).unwrap();

        let mut res = bn::BigNum::new().unwrap();
        res.mod_exp(&base, &exponent, &modulus, &mut bn::BigNumContext::new().unwrap()).unwrap();

        for b in dev._internal_state.message.iter_mut() {
            *b = 0;
        }
        let res_vec = res.to_vec();
        // Copy result to the back of the buffer
        dev._internal_state.message[0x100 - res_vec.len() .. 0x100].copy_from_slice(res_vec.as_slice());

        dev.cnt.set_unchecked(bf!(cnt @ RegCnt::busy as 0));
    }
}

fn reg_mod_read(dev: &mut RsaDevice, buf_pos: usize, dest: &mut [u8]) {
    trace!("Reading {} bytes from RSA MOD at +0x{:X}", dest.len(), buf_pos);
    let src_slice = &dev._internal_state.modulus[buf_pos .. buf_pos + dest.len()];
    dest.clone_from_slice(src_slice);
}

fn reg_mod_write(dev: &mut RsaDevice, buf_pos: usize, src: &[u8]) {
    trace!("Writing {} bytes to RSA MOD at +0x{:X}", src.len(), buf_pos);
    let dst_slice = &mut dev._internal_state.modulus[buf_pos .. buf_pos + src.len()];
    dst_slice.clone_from_slice(src);
}

fn reg_txt_read(dev: &mut RsaDevice, buf_pos: usize, dest: &mut [u8]) {
    trace!("Reading {} bytes from RSA TXT at +0x{:X}", dest.len(), buf_pos);
    let src_slice = &dev._internal_state.message[buf_pos .. buf_pos + dest.len()];
    dest.clone_from_slice(src_slice);
}

fn reg_txt_write(dev: &mut RsaDevice, buf_pos: usize, src: &[u8]) {
    trace!("Writing {} bytes to RSA TXT at +0x{:X}", src.len(), buf_pos);
    let dst_slice = &mut dev._internal_state.message[buf_pos .. buf_pos + src.len()];
    dst_slice.clone_from_slice(src);
}

fn reg_exp_fifo_write(dev: &mut RsaDevice) {
    let keyslot = bf!((dev.cnt.get()) @ RegCnt::keyslot) as usize;
    let (slot_cnt, slot_len) = get_keydata(dev, keyslot);

    assert_eq!(slot_len, 0x40);
    assert_eq!(bf!(slot_cnt @ RegSlotCnt::key_set), 0);
    assert_eq!(bf!(slot_cnt @ RegSlotCnt::key_prot), 0);

    let write_pos = dev._internal_state.slots[keyslot].write_pos;
    if write_pos == 0 { // Just starting to update key, clear previous key
        for b in dev._internal_state.slots[keyslot].buf.iter_mut() {
            *b = 0;
        }
    }
    let word_bytes: [u8; 4] = unsafe { mem::transmute(dev.exp_fifo.get()) };
    dev._internal_state.slots[keyslot].buf[write_pos .. write_pos + 4].copy_from_slice(&word_bytes[..]);

    trace!("Writing bytes {:02X},{:02X},{:02X},{:02X} to RSA exponent FIFO!",
        word_bytes[0], word_bytes[1], word_bytes[2], word_bytes[3]);

    dev._internal_state.slots[keyslot].write_pos += 4;
}

iodevice!(RsaDevice, {
    internal_state: RsaDeviceState;
    regs: {
        0x000 => cnt: u32 { write_effect = reg_cnt_update; }
        0x0F0 => unk: u32 { }
        0x100 => slot0_cnt: u32 { write_effect = |dev: &mut RsaDevice| reg_slot_cnt_update(dev, 0); }
        0x104 => slot0_len: u32 { }
        0x110 => slot1_cnt: u32 { write_effect = |dev: &mut RsaDevice| reg_slot_cnt_update(dev, 1); }
        0x114 => slot1_len: u32 { }
        0x120 => slot2_cnt: u32 { write_effect = |dev: &mut RsaDevice| reg_slot_cnt_update(dev, 2); }
        0x124 => slot2_len: u32 { }
        0x130 => slot3_cnt: u32 { write_effect = |dev: &mut RsaDevice| reg_slot_cnt_update(dev, 3); }
        0x134 => slot3_len: u32 { }
        0x200 => exp_fifo: u32 { write_effect = reg_exp_fifo_write; }
    }
    ranges: {
        0x400;0x100 => {
            read_effect = reg_mod_read;
            write_effect = reg_mod_write;
        }
        0x800;0x100 => {
            read_effect = reg_txt_read;
            write_effect = reg_txt_write;
        }
    }
});<commit_msg>RSA: Add support for reversed modulus, text data<commit_after>use io::regs;

use std::fmt;
use std::mem;

use openssl::bn;

bfdesc!(RegCnt: u32, {
    busy: 0 => 0,
    keyslot: 4 => 5,
    little_endian: 8 => 8,
    normal_order: 9 => 9
});

bfdesc!(RegSlotCnt: u32, {
    key_set: 0 => 0,
    key_prot: 1 => 1
});

pub struct RsaKeyslot {
    write_pos: usize,
    buf: [u8; 0x100]
}

impl Clone for RsaKeyslot {
    fn clone(&self) -> RsaKeyslot {
        let mut new_buf = [0u8; 0x100];
        new_buf.copy_from_slice(&self.buf[..]);

        RsaKeyslot {
            write_pos: self.write_pos,
            buf: new_buf
        }
    }
}

pub struct RsaDeviceState {
    slots: [RsaKeyslot; 4],
    modulus: [u8; 0x100],
    message: [u8; 0x100],
}

impl Default for RsaDeviceState {
    fn default() -> RsaDeviceState {
        let new_keyslot = RsaKeyslot {
            write_pos: 0,
            buf: [0; 0x100],
        };
        RsaDeviceState {
            slots: [new_keyslot.clone(), new_keyslot.clone(),
                    new_keyslot.clone(), new_keyslot],
            modulus: [0; 0x100],
            message: [0; 0x100],
        }
    }
}

impl fmt::Debug for RsaDeviceState {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RsaDeviceState {{ }}")
    }
}

fn get_keydata(dev: &RsaDevice, keyslot: usize) -> (u32, u32) {
    match keyslot {
        0 => (dev.slot0_cnt.get(), dev.slot0_len.get()),
        1 => (dev.slot1_cnt.get(), dev.slot1_len.get()),
        2 => (dev.slot2_cnt.get(), dev.slot2_len.get()),
        3 => (dev.slot3_cnt.get(), dev.slot3_len.get()),
        _ => unreachable!()
    }
}

fn reg_slot_cnt_update(dev: &mut RsaDevice, keyslot: usize) {
    let (slot_cnt, _) = get_keydata(dev, keyslot);
    if bf!(slot_cnt @ RegSlotCnt::key_set) == 1 {
        assert_eq!(dev._internal_state.slots[keyslot].write_pos, 0x100);
    } else {
        dev._internal_state.slots[keyslot].write_pos = 0;
    }
}

fn reg_cnt_update(dev: &mut RsaDevice) {
    let cnt = dev.cnt.get();
    trace!("Wrote 0x{:08X} to RSA CNT register!", cnt);

    if bf!(cnt @ RegCnt::busy) == 1 {
        let keyslot = bf!(cnt @ RegCnt::keyslot) as usize;
        let (slot_cnt, _) = get_keydata(dev, keyslot);
        assert_eq!(bf!(slot_cnt @ RegSlotCnt::key_set), 1);

        info!("Performing RSA arithmetic!");

        let mut base_buf = [0u8; 0x100];
        base_buf.copy_from_slice(&dev._internal_state.message[..]);
        let mut modulus_buf = [0u8; 0x100];
        modulus_buf.copy_from_slice(&dev._internal_state.modulus[..]);

        match (bf!(cnt @ RegCnt::little_endian), bf!(cnt @ RegCnt::normal_order)) {
            (1, 1) => {},
            (0, 0) => {
                base_buf.reverse();
                modulus_buf.reverse();
            }
            _ => unimplemented!()
        }

        let base = bn::BigNum::from_slice(&base_buf[..]).unwrap();
        let exponent = bn::BigNum::from_slice(&dev._internal_state.slots[keyslot].buf[..]).unwrap();
        let modulus = bn::BigNum::from_slice(&modulus_buf[..]).unwrap();

        let mut res = bn::BigNum::new().unwrap();
        res.mod_exp(&base, &exponent, &modulus, &mut bn::BigNumContext::new().unwrap()).unwrap();

        for b in dev._internal_state.message.iter_mut() {
            *b = 0;
        }
        let res_vec = res.to_vec();
        // Copy result to the back of the buffer
        dev._internal_state.message[0x100 - res_vec.len() .. 0x100].copy_from_slice(res_vec.as_slice());

        dev.cnt.set_unchecked(bf!(cnt @ RegCnt::busy as 0));
    }
}

fn reg_mod_read(dev: &mut RsaDevice, buf_pos: usize, dest: &mut [u8]) {
    trace!("Reading {} bytes from RSA MOD at +0x{:X}", dest.len(), buf_pos);
    let src_slice = &dev._internal_state.modulus[buf_pos .. buf_pos + dest.len()];
    dest.clone_from_slice(src_slice);
}

fn reg_mod_write(dev: &mut RsaDevice, buf_pos: usize, src: &[u8]) {
    trace!("Writing {} bytes to RSA MOD at +0x{:X}", src.len(), buf_pos);
    let dst_slice = &mut dev._internal_state.modulus[buf_pos .. buf_pos + src.len()];
    dst_slice.clone_from_slice(src);
}

fn reg_txt_read(dev: &mut RsaDevice, buf_pos: usize, dest: &mut [u8]) {
    trace!("Reading {} bytes from RSA TXT at +0x{:X}", dest.len(), buf_pos);
    let src_slice = &dev._internal_state.message[buf_pos .. buf_pos + dest.len()];
    dest.clone_from_slice(src_slice);
}

fn reg_txt_write(dev: &mut RsaDevice, buf_pos: usize, src: &[u8]) {
    trace!("Writing {} bytes to RSA TXT at +0x{:X}", src.len(), buf_pos);
    let dst_slice = &mut dev._internal_state.message[buf_pos .. buf_pos + src.len()];
    dst_slice.clone_from_slice(src);
}

fn reg_exp_fifo_write(dev: &mut RsaDevice) {
    let keyslot = bf!((dev.cnt.get()) @ RegCnt::keyslot) as usize;
    let (slot_cnt, _) = get_keydata(dev, keyslot);

    assert_eq!(bf!(slot_cnt @ RegSlotCnt::key_set), 0);
    assert_eq!(bf!(slot_cnt @ RegSlotCnt::key_prot), 0);

    let write_pos = dev._internal_state.slots[keyslot].write_pos;
    if write_pos == 0 { // Just starting to update key, clear previous key
        for b in dev._internal_state.slots[keyslot].buf.iter_mut() {
            *b = 0;
        }
    }
    let word_bytes: [u8; 4] = unsafe { mem::transmute(dev.exp_fifo.get()) };
    dev._internal_state.slots[keyslot].buf[write_pos .. write_pos + 4].copy_from_slice(&word_bytes[..]);

    trace!("Writing bytes {:02X},{:02X},{:02X},{:02X} to RSA exponent FIFO!",
        word_bytes[0], word_bytes[1], word_bytes[2], word_bytes[3]);

    dev._internal_state.slots[keyslot].write_pos += 4;
}

iodevice!(RsaDevice, {
    internal_state: RsaDeviceState;
    regs: {
        0x000 => cnt: u32 { write_effect = reg_cnt_update; }
        0x0F0 => unk: u32 { }
        0x100 => slot0_cnt: u32 { write_effect = |dev: &mut RsaDevice| reg_slot_cnt_update(dev, 0); }
        0x104 => slot0_len: u32 { }
        0x110 => slot1_cnt: u32 { write_effect = |dev: &mut RsaDevice| reg_slot_cnt_update(dev, 1); }
        0x114 => slot1_len: u32 { }
        0x120 => slot2_cnt: u32 { write_effect = |dev: &mut RsaDevice| reg_slot_cnt_update(dev, 2); }
        0x124 => slot2_len: u32 { }
        0x130 => slot3_cnt: u32 { write_effect = |dev: &mut RsaDevice| reg_slot_cnt_update(dev, 3); }
        0x134 => slot3_len: u32 { }
        0x200 => exp_fifo: u32 { write_effect = reg_exp_fifo_write; }
    }
    ranges: {
        0x204;0xFC => { } // A bug in bootrom causes it to write all over this area
        0x400;0x100 => {
            read_effect = reg_mod_read;
            write_effect = reg_mod_write;
        }
        0x800;0x100 => {
            read_effect = reg_txt_read;
            write_effect = reg_txt_write;
        }
    }
});<|endoftext|>