14 releases (8 breaking)

✓ Uses Rust 2018 edition

new 0.13.0-rc.2 Dec 4, 2019
0.12.0 Aug 14, 2019
0.11.0 Mar 15, 2019
0.10.0 Dec 14, 2018
0.6.0 Mar 6, 2018

#29 in Cryptocurrencies

Download history 111/week @ 2019-08-21 70/week @ 2019-08-28 122/week @ 2019-09-04 68/week @ 2019-09-11 71/week @ 2019-09-18 72/week @ 2019-09-25 40/week @ 2019-10-02 16/week @ 2019-10-09 54/week @ 2019-10-16 38/week @ 2019-10-23 53/week @ 2019-10-30 20/week @ 2019-11-06 49/week @ 2019-11-13 18/week @ 2019-11-20 51/week @ 2019-11-27

205 downloads per month

Apache-2.0

1.5MB
34K SLoC

exonum-time

Travis Build Status Docs.rs License: Apache-2.0 rust 1.36.0+ required

Exonum-time is a time oracle service for Exonum blockchain framework. This service allows to determine time, import it from the external world to the blockchain and keep its current value in the blockchain.

Usage

Include exonum-time as a dependency in your Cargo.toml:

[dependencies]
exonum = "0.13.0-rc.2"
exonum-cli = "0.13.0-rc.2"
exonum-time = "0.13.0-rc.2"

Add the time oracle service to the blockchain in the main project file:

extern crate exonum;
extern crate exonum_cli;
extern crate exonum_time;

use exonum_cli::NodeBuilder;
use exonum_time::TimeServiceFactory;
use simple_service::MarkerService;

fn main() -> Result<(), failure::Error> {
    exonum::helpers::init_logger().unwrap();
    NodeBuilder::new()
        .with_service(TimeServiceFactory::default())
        .with_service(MarkerService)
        .run()
}

Importing the data schema

Typical usage of the service boils down to importing the schema and calling its time() or validators_time() methods.

Below is an example of a method for processing a transaction, which must be executed no later than the specified time (this time is written in the transaction body in a separate field):

/// The argument of the `MarkerInterface::mark` method.
#[derive(Serialize, Deserialize, Debug, Clone, ProtobufConvert, BinaryValue, ObjectHash)]
#[protobuf_convert(source = "proto::TxMarker")]
pub struct TxMarker {
    mark: i32,
    time: DateTime<Utc>,
}

/// Marker service transactions interface definition.
#[exonum_interface]
pub trait MarkerTransactions {
    /// Transaction, which must be executed no later
    /// than the specified time (field `time`).
    fn mark(&self, context: CallContext<'_>, arg: TxMarker) -> Result<(), ExecutionError>;
}

#[derive(Debug, ServiceDispatcher, ServiceFactory)]
#[service_factory(
    artifact_name = "marker",
    artifact_version = "0.1.0",
    proto_sources = "proto"
)]
#[service_dispatcher(implements("MarkerTransactions"))]
struct MarkerService;

/// Marker service database schema.
#[derive(Debug, FromAccess)]
pub struct MarkerSchema<T: Access> {
    pub marks: ProofMapIndex<T::Base, PublicKey, i32>,
}

impl<T: Access> MarkerSchema<T> {
    /// Returns hashes for stored table.
    fn state_hash(&self) -> Vec<Hash> {
        vec![self.marks.object_hash()]
    }
}

impl MarkerTransactions for MarkerService {
    fn mark(
      &self,
      context: CallContext<'_>,
      arg: TxMarker
    ) -> Result<(), ExecutionError> {
        let author = context
            .caller()
            .author()
            .expect("Wrong `TxMarker` initiator");

        let data = context.data();
        let time_service_data = data
            .for_service(TIME_SERVICE_NAME)
            .expect("No time service data");
        let time = TimeSchema::new(time_service_data).time.get();
        match time {
            Some(current_time) if current_time <= arg.time => {
                let mut schema = MarkerSchema::new(context.service_data());
                schema.marks.put(&author, arg.mark);
            }
            _ => {}
        }
        Ok(())
    }
}

impl Service for MarkerService {
    fn state_hash(&self, data: BlockchainData<&dyn Snapshot>) -> Vec<Hash> {
        MarkerSchema::new(data.for_executing_service()).state_hash()
    }
}

See the full implementation of the service, which uses the time oracle.

You can get the time of each validator node in the same manner the consolidated time of the system is obtained:

// Gets the data of time service instance
let data = context.data();
let time_service_data = data
    .for_service("time_service_name")
    .expect("No time service data");
let time_schema = TimeSchema::new(time_service_data);
// Gets the times of all validators.
let validators_time = time_schema.time.get();
// Gets the time of validator with a public key equal to `public_key`.
let validator_time = time_schema.validators_times.get(&public_key);

Further Reading

Consult the crate docs for more details about the service Rust API, and the service description in Exonum docs for a more high-level perspective, in particular, the design rationale and the proof of correctness.

Other languages support

License

exonum-time is licensed under the Apache License (Version 2.0). See LICENSE for details.

Dependencies

~44MB
~851K SLoC