1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright 2020 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.

//! Middleware service providing ways to compose transactions from the simpler building blocks.
//!
//! # Functionality overview
//!
//! ## Transaction batching
//!
//! [Batching] allows to atomically execute several transactions; if an error occurs
//! during execution, changes made by all transactions are rolled back. All transactions
//! in the batch are authorized in the same way as the batch itself.
//!
//! ## Checked call
//!
//! [Checked call] is a way to ensure that the called service corresponds to a specific artifact
//! with an expected version range. Unlike alternatives (e.g., finding out this information via
//! the `services` endpoint of the node HTTP API), using checked calls is most failsafe; by design,
//! it cannot suffer from [TOCTOU] issues. It does impose a certain overhead on the execution, though.
//!
//! [Batching]: trait.MiddlewareInterface.html#tymethod.batch
//! [Checked call]: trait.MiddlewareInterface.html#tymethod.checked_call
//! [TOCTOU]: https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use

#![deny(
    unsafe_code,
    bare_trait_objects,
    missing_docs,
    missing_debug_implementations
)]

pub use self::transactions::{
    Batch, CheckedCall, Error, MiddlewareInterface, MiddlewareInterfaceMut,
};

pub mod proto;
mod transactions;

use exonum::runtime::{versioning, InstanceId};
use exonum_derive::*;
use exonum_rust_runtime::{DefaultInstance, Service};

use std::{fmt, str::FromStr};

/// Middleware service.
#[derive(Debug, ServiceDispatcher, ServiceFactory)]
#[service_dispatcher(implements("MiddlewareInterface"))]
#[service_factory(proto_sources = "proto")]
pub struct MiddlewareService;

impl Service for MiddlewareService {}

impl DefaultInstance for MiddlewareService {
    const INSTANCE_ID: InstanceId = 1;
    const INSTANCE_NAME: &'static str = "middleware";
}

/// A wrapper around an artifact requirement.
///
/// Necessary as a separate type because of Rust orphaning rules: we want to use the requirement
/// as a stub, but the return type ([`CheckedCall`]) is defined in this crate.
///
/// [`CheckedCall`]: struct.CheckedCall.html
///
/// # Examples
///
/// ```
/// # use exonum::runtime::InstanceId;
/// # use exonum_derive::*;
/// # use exonum_middleware_service::{ArtifactReq, CheckedCall};
/// let req: ArtifactReq = "some.Service@^1.3.0".parse().unwrap();
///
/// // Suppose the interface for `some.Service` is defined as follows:
/// #[exonum_interface]
/// trait SomeService<Ctx> {
///     type Output;
///     #[interface_method(id = 0)]
///     fn do_something(&self, ctx: Ctx, arg: String) -> Self::Output;
/// }
///
/// // Then, requirements can be used to perform a checked call to the service.
/// const SERVICE_ID: InstanceId = 100;
/// let checked_call: CheckedCall = req.do_something(SERVICE_ID, "Arg".into());
/// ```
#[derive(Clone, PartialEq)]
pub struct ArtifactReq(pub versioning::ArtifactReq);

impl From<versioning::ArtifactReq> for ArtifactReq {
    fn from(value: versioning::ArtifactReq) -> Self {
        ArtifactReq(value)
    }
}

impl From<ArtifactReq> for versioning::ArtifactReq {
    fn from(value: ArtifactReq) -> Self {
        value.0
    }
}

impl fmt::Debug for ArtifactReq {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&self.0, formatter)
    }
}

impl fmt::Display for ArtifactReq {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(&self.0, formatter)
    }
}

impl FromStr for ArtifactReq {
    type Err = <versioning::ArtifactReq as FromStr>::Err;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        versioning::ArtifactReq::from_str(s).map(ArtifactReq)
    }
}