// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see . //! Sender-side request logic. Some things from this module are also used on the receiver side, eg //! [`extrinsic_delay`], but most of the receiver-side request logic lives elsewhere. use super::{config::SubstrateConfig, error::Error}; use blake2::{ digest::{consts::U16, Mac}, Blake2bMac, }; use codec::{Decode, DecodeAll}; use futures::channel::oneshot; use log::debug; use mixnet::core::{Delay, MessageId, PostErr, Scattered}; use sp_core::Bytes; use std::time::Duration; const LOG_TARGET: &str = "mixnet"; fn send_err(reply_sender: oneshot::Sender>, err: Error) { if let Err(Err(err)) = reply_sender.send(Err(err)) { debug!(target: LOG_TARGET, "Failed to inform requester of error: {err}"); } } fn send_reply(reply_sender: oneshot::Sender>, mut data: &[u8]) { let res = match Result::decode_all(&mut data) { Ok(res) => res.map_err(Error::Remote), Err(_) => Err(Error::BadReply), }; match reply_sender.send(res) { Ok(_) => (), Err(Ok(_)) => debug!(target: LOG_TARGET, "Failed to send reply to requester"), Err(Err(err)) => debug!(target: LOG_TARGET, "Failed to inform requester of error: {err}"), } } /// First byte of a submit extrinsic request, identifying it as such. pub const SUBMIT_EXTRINSIC: u8 = 1; const EXTRINSIC_DELAY_PERSONA: &[u8; 16] = b"submit-extrn-dly"; /// Returns the artificial delay that should be inserted between receipt of a submit extrinsic /// request with the given message ID and import of the extrinsic into the local transaction pool. pub fn extrinsic_delay(message_id: &MessageId, config: &SubstrateConfig) -> Duration { let h = Blake2bMac::::new_with_salt_and_personal(message_id, b"", EXTRINSIC_DELAY_PERSONA) .expect("Key, salt, and persona sizes are fixed and small enough"); let delay = Delay::exp(h.finalize().into_bytes().as_ref()); delay.to_duration(config.mean_extrinsic_delay) } /// Request parameters and local reply channel. Stored by the /// [`RequestManager`](mixnet::request_manager::RequestManager). pub enum Request { SubmitExtrinsic { extrinsic: Bytes, reply_sender: oneshot::Sender> }, } impl Request { /// Forward an error to the user of the mixnet service. fn send_err(self, err: Error) { match self { Request::SubmitExtrinsic { reply_sender, .. } => send_err(reply_sender, err), } } /// Forward a reply to the user of the mixnet service. pub fn send_reply(self, data: &[u8]) { match self { Request::SubmitExtrinsic { reply_sender, .. } => send_reply(reply_sender, data), } } } impl mixnet::request_manager::Request for Request { type Context = SubstrateConfig; fn with_data(&self, f: impl FnOnce(Scattered) -> T, _context: &Self::Context) -> T { match self { Request::SubmitExtrinsic { extrinsic, .. } => f([&[SUBMIT_EXTRINSIC], extrinsic.as_ref()].as_slice().into()), } } fn num_surbs(&self, context: &Self::Context) -> usize { match self { Request::SubmitExtrinsic { .. } => context.surb_factor, } } fn handling_delay(&self, message_id: &MessageId, context: &Self::Context) -> Duration { match self { Request::SubmitExtrinsic { .. } => extrinsic_delay(message_id, context), } } fn handle_post_err(self, err: PostErr, _context: &Self::Context) { self.send_err(err.into()); } fn handle_retry_limit_reached(self, _context: &Self::Context) { self.send_err(Error::NoReply); } }