├── .gitignore ├── rust-toolchain.toml ├── blocknet ├── src │ ├── lib.rs │ ├── libp2p │ │ ├── peer_info │ │ │ ├── codec.rs │ │ │ ├── protocol.rs │ │ │ ├── json.rs │ │ │ ├── mod.rs │ │ │ ├── behaviour.rs │ │ │ └── handler.rs │ │ └── mod.rs │ └── service.rs ├── Cargo.toml └── examples │ └── simple_libp2p.rs ├── tinyjam ├── Cargo.toml └── src │ ├── lib.rs │ └── core_seal.rs ├── util └── sync-extra │ ├── Cargo.toml │ └── src │ └── lib.rs ├── blockchain ├── Cargo.toml ├── src │ ├── lib.rs │ ├── block.rs │ ├── memory │ │ ├── mod.rs │ │ ├── state.rs │ │ └── chain.rs │ ├── state.rs │ └── chain.rs └── tests │ └── simple.rs ├── .editorconfig ├── Cargo.toml ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | shell.nix 4 | .vscode/settings.json 5 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.75.0" 3 | profile = "minimal" 4 | components = [ "rustfmt", "clippy" ] 5 | -------------------------------------------------------------------------------- /blocknet/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod service; 2 | 3 | pub mod libp2p; 4 | 5 | pub use crate::service::{ 6 | BroadcastService, Event, Message, NotifyService, RequestService, Service, 7 | }; 8 | -------------------------------------------------------------------------------- /tinyjam/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tinyjam" 3 | version = "0.1.0" 4 | description = "A tiny amount of jam." 5 | 6 | authors.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | -------------------------------------------------------------------------------- /util/sync-extra/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sync-extra" 3 | version = "0.1.0" 4 | description = "Convenience functions to Mutex and RwLock." 5 | 6 | authors.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | -------------------------------------------------------------------------------- /blockchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blockchain" 3 | version = "0.9.2" 4 | description = "Unopinioned blockchain framework" 5 | 6 | authors.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | itertools = "0.12" 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | [*] 3 | indent_style=tab 4 | indent_size=tab 5 | tab_width=4 6 | end_of_line=lf 7 | charset=utf-8 8 | trim_trailing_whitespace=true 9 | max_line_length=80 10 | insert_final_newline=true 11 | 12 | [*.yml] 13 | indent_style=space 14 | indent_size=2 15 | tab_width=8 16 | end_of_line=lf 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace.package] 2 | authors = ["Wei Tang "] 3 | license = "Apache-2.0" 4 | edition = "2021" 5 | repository = "https://github.com/rust-blockchain/blockchain" 6 | 7 | [workspace] 8 | resolver = "2" 9 | members = [ 10 | "blockchain", 11 | "blocknet", 12 | "tinyjam", 13 | "util/sync-extra", 14 | ] 15 | -------------------------------------------------------------------------------- /blockchain/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! General block framework. 2 | 3 | #![warn(missing_docs)] 4 | 5 | mod block; 6 | mod chain; 7 | pub mod memory; 8 | mod state; 9 | 10 | pub use crate::block::{Headered, Identified, Keyed}; 11 | pub use crate::chain::{BlockBuilder, ForkTree, ForkTreeMut, ForkTreeTransactional, ImportBlock}; 12 | pub use crate::state::{FlatState, FlatStateMut, FlatStateTransactional, OverlayedFlatState}; 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust Blockchain 2 | 3 | [![crates.io](https://img.shields.io/crates/v/blockchain.svg)](https://crates.io/crates/blockchain) 4 | [![Documentation](https://docs.rs/blockchain/badge.svg)](https://docs.rs/blockchain) 5 | 6 | *Rust Blockchain* is an unopinioned blockchain framework that helps 7 | you to develop a blockchain project. 8 | 9 | ## Chain 10 | 11 | The `chain` module handles block import and state storage. Assumptions 12 | we have in this module: 13 | 14 | * We have `Block`, which consists of a hash, and has a parent 15 | block. It forms a chain. 16 | * At each `Block` there is a corresponding `State`. 17 | * An executor that takes a block, and parent block's state. Executing 18 | it should get the current block's state. 19 | -------------------------------------------------------------------------------- /tinyjam/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Tinyjam 2 | //! 3 | //! A tiny implementation of the Polkadot JAM protocol. 4 | //! 5 | //! ## Consensus logic 6 | //! 7 | //! The consensus logic of `tinyjam` consists of three parts: 8 | //! 9 | //! * Block production and finality: to make relay chain blocks. 10 | //! * Map-reduce: the logic of refine, accumulate, and on transfer. 11 | //! * In-core sealing: guaranteeing, availability, auditing and judging. 12 | //! 13 | //! The code is structured so that the parts are swappable. You can replace 14 | //! the block production algorithm to simple Aura. Or you can disable 15 | //! map-reduce, so that the chain notes raw blobs without any functionality. 16 | 17 | pub mod core_seal; 18 | 19 | pub struct State { 20 | /// State of the consensus, such as Safrole. 21 | pub consensus: Consensus, 22 | } 23 | -------------------------------------------------------------------------------- /blocknet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blocknet" 3 | version = "0.1.0" 4 | description = "Network management for the blockchain framework." 5 | 6 | authors.workspace = true 7 | license.workspace = true 8 | edition.workspace = true 9 | 10 | [dependencies] 11 | async-trait = "0.1" 12 | asynchronous-codec = "0.7.0" 13 | futures = "0.3" 14 | futures-timer = "3.0.3" 15 | futures-bounded = "0.2.3" 16 | serde = { version = "1.0", features = ["derive"] } 17 | serde_json = "1.0" 18 | libp2p = { version = "0.53", features = ["full"] } 19 | quick-protobuf-codec = "0.3.1" 20 | quick-protobuf = "0.8" 21 | tracing = "0.1.37" 22 | thiserror = "1.0" 23 | void = "1.0" 24 | either = "1.11.0" 25 | smallvec = "1.13.2" 26 | lru = "0.12.1" 27 | 28 | sync-extra = { version = "0.1.0", path = "../util/sync-extra" } 29 | 30 | [dev-dependencies] 31 | tokio = { version = "1.37", features = ["full"] } 32 | tracing-subscriber = "0.3" 33 | -------------------------------------------------------------------------------- /blocknet/src/libp2p/peer_info/codec.rs: -------------------------------------------------------------------------------- 1 | use super::{Info, UpgradeError}; 2 | use async_trait::async_trait; 3 | use futures::prelude::*; 4 | 5 | /// A `Codec` defines the request and response types 6 | /// for a request-response [`Behaviour`](crate::Behaviour) protocol or 7 | /// protocol family and how they are encoded / decoded on an I/O stream. 8 | #[async_trait] 9 | pub trait Codec: Send + 'static { 10 | async fn read_info(io: T) -> Result 11 | where 12 | T: AsyncRead + Unpin + Send; 13 | 14 | async fn read_push_info(io: T) -> Result 15 | where 16 | T: AsyncRead + Unpin + Send; 17 | 18 | async fn write_info(io: T, info: TInfo) -> Result<(), UpgradeError> 19 | where 20 | T: AsyncWrite + Unpin + Send; 21 | 22 | async fn write_push_info(io: T, info: TInfo::Push) -> Result<(), UpgradeError> 23 | where 24 | T: AsyncWrite + Unpin + Send; 25 | } 26 | -------------------------------------------------------------------------------- /util/sync-extra/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; 2 | 3 | pub trait MutexExtra { 4 | type Value: ?Sized; 5 | 6 | fn lock_unwrap(&self) -> MutexGuard<'_, Self::Value>; 7 | } 8 | 9 | impl MutexExtra for Mutex { 10 | type Value = T; 11 | 12 | fn lock_unwrap(&self) -> MutexGuard<'_, Self::Value> { 13 | self.lock().expect("lock is poisioned") 14 | } 15 | } 16 | 17 | pub trait RwLockExtra { 18 | type Value: ?Sized; 19 | 20 | fn read_unwrap(&self) -> RwLockReadGuard<'_, Self::Value>; 21 | fn write_unwrap(&self) -> RwLockWriteGuard<'_, Self::Value>; 22 | } 23 | 24 | impl RwLockExtra for RwLock { 25 | type Value = T; 26 | 27 | fn read_unwrap(&self) -> RwLockReadGuard<'_, Self::Value> { 28 | self.read().expect("lock is poisioned") 29 | } 30 | 31 | fn write_unwrap(&self) -> RwLockWriteGuard<'_, Self::Value> { 32 | self.write().expect("lock is poisioned") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /blockchain/src/block.rs: -------------------------------------------------------------------------------- 1 | /// A block or a header that is identified. That is, it contains a hash, and 2 | /// reference a parent via a parent hash. 3 | /// 4 | /// The hash of a block is unique. 5 | pub trait Identified { 6 | /// Identifier type. 7 | type Identifier: Clone + Copy + Eq + PartialEq + core::hash::Hash; 8 | 9 | /// Get the block hash. 10 | fn id(&self) -> Self::Identifier; 11 | /// Get the parent block hash. None if this block is genesis. 12 | fn parent_id(&self) -> Option; 13 | } 14 | 15 | /// Additional index keys for a block or a header. This can be, for example, 16 | /// transaction hashes or block numbers. 17 | pub trait Keyed { 18 | /// Get the block key. 19 | fn key(&self) -> Key; 20 | } 21 | 22 | /// A block where we can derive a header from. 23 | /// 24 | /// The block can be thought as with a header and a body. However, in many 25 | /// blockchains, certain tricks are used to make it so that a header and a block 26 | /// can easily get the same hash, with structures unchanged. In those cases, the 27 | /// header can only be "derived" from the block, and there's no clear 28 | /// "boundary" of a header and a body. 29 | pub trait Headered { 30 | /// Header type. 31 | type Header; 32 | 33 | /// Get the block header. 34 | fn header(&self) -> Self::Header; 35 | } 36 | -------------------------------------------------------------------------------- /blocknet/src/libp2p/peer_info/protocol.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Parity Technologies (UK) Ltd. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | use libp2p::core::multiaddr; 22 | use libp2p::identity; 23 | use std::fmt::Debug; 24 | use thiserror::Error; 25 | 26 | pub trait Info: Debug + Clone + Send + 'static { 27 | type Push: From + Debug + Clone + Send + 'static; 28 | 29 | fn merge(&mut self, push: Self::Push); 30 | } 31 | 32 | #[derive(Debug, Error)] 33 | pub enum UpgradeError { 34 | #[error("Codec error")] 35 | Codec(String), 36 | #[error("I/O interaction failed")] 37 | Io(#[from] std::io::Error), 38 | #[error("Stream closed")] 39 | StreamClosed, 40 | #[error("Failed decoding multiaddr")] 41 | Multiaddr(#[from] multiaddr::Error), 42 | #[error("Failed decoding public key")] 43 | PublicKey(#[from] identity::DecodingError), 44 | } 45 | -------------------------------------------------------------------------------- /blockchain/src/memory/mod.rs: -------------------------------------------------------------------------------- 1 | //! Memory-only implementations. 2 | 3 | mod chain; 4 | mod state; 5 | 6 | pub use self::chain::{MemoryForkTree, MemoryForkTreeInsertError, MemoryForkTreeQueryError}; 7 | pub use self::state::MemoryFlatState; 8 | 9 | use core::ops::{Deref, DerefMut}; 10 | 11 | /// A memory transactional. 12 | /// 13 | /// The struct `MemoryTransactional` allows memory-only implementations (such as 14 | /// memory fork tree and memory state) to be transactional. We do this by 15 | /// keeping two copies of the state. When an operation happens, we try to apply 16 | /// it to both. If either fails, the operation halts immediately and the other 17 | /// state is copied back. 18 | #[derive(Debug, Clone)] 19 | pub struct MemoryTransactional { 20 | first: Inner, 21 | second: Inner, 22 | } 23 | 24 | impl MemoryTransactional { 25 | /// Create a new memory transactional. 26 | pub fn new(inner: Inner) -> Self { 27 | Self { 28 | second: inner.clone(), 29 | first: inner, 30 | } 31 | } 32 | 33 | /// Apply some changes. 34 | pub fn apply Result>(&mut self, f: F) -> Result { 35 | match f(&mut self.first) { 36 | Ok(first_ret) => match f(&mut self.second) { 37 | Ok(_second_ret) => Ok(first_ret), 38 | Err(_second_err) => { 39 | self.second = self.first.clone(); 40 | Ok(first_ret) 41 | } 42 | }, 43 | Err(first_err) => { 44 | self.first = self.second.clone(); 45 | Err(first_err) 46 | } 47 | } 48 | } 49 | } 50 | 51 | impl Deref for MemoryTransactional { 52 | type Target = Inner; 53 | 54 | fn deref(&self) -> &Inner { 55 | &self.first 56 | } 57 | } 58 | 59 | impl DerefMut for MemoryTransactional { 60 | fn deref_mut(&mut self) -> &mut Inner { 61 | &mut self.first 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /blocknet/src/libp2p/peer_info/json.rs: -------------------------------------------------------------------------------- 1 | use super::{Info, UpgradeError}; 2 | use async_trait::async_trait; 3 | use futures::prelude::*; 4 | use serde::{de::DeserializeOwned, Serialize}; 5 | use std::marker::PhantomData; 6 | 7 | pub type Behaviour = super::Behaviour>; 8 | 9 | /// Max size in bytes 10 | const SIZE_MAXIMUM: u64 = 1024 * 1024; 11 | 12 | pub struct Codec { 13 | _marker: PhantomData, 14 | } 15 | 16 | #[async_trait] 17 | impl super::Codec for Codec 18 | where 19 | TInfo: Serialize + DeserializeOwned + Info, 20 | TInfo::Push: Serialize + DeserializeOwned, 21 | { 22 | async fn read_info(io: T) -> Result 23 | where 24 | T: AsyncRead + Unpin + Send, 25 | { 26 | let mut vec = Vec::new(); 27 | io.take(SIZE_MAXIMUM).read_to_end(&mut vec).await?; 28 | 29 | let info: TInfo = serde_json::from_slice(vec.as_slice()) 30 | .map_err(|e| UpgradeError::Codec(format!("{:?}", e)))?; 31 | Ok(info) 32 | } 33 | 34 | async fn read_push_info(io: T) -> Result 35 | where 36 | T: AsyncRead + Unpin + Send, 37 | { 38 | let mut vec = Vec::new(); 39 | io.take(SIZE_MAXIMUM).read_to_end(&mut vec).await?; 40 | 41 | let info: TInfo::Push = serde_json::from_slice(vec.as_slice()) 42 | .map_err(|e| UpgradeError::Codec(format!("{:?}", e)))?; 43 | Ok(info) 44 | } 45 | 46 | async fn write_info(mut io: T, info: TInfo) -> Result<(), UpgradeError> 47 | where 48 | T: AsyncWrite + Unpin + Send, 49 | { 50 | let info: TInfo = info.into(); 51 | let data = 52 | serde_json::to_vec(&info).map_err(|e| UpgradeError::Codec(format!("{:?}", e)))?; 53 | 54 | io.write_all(data.as_ref()).await?; 55 | Ok(()) 56 | } 57 | 58 | async fn write_push_info(mut io: T, info: TInfo::Push) -> Result<(), UpgradeError> 59 | where 60 | T: AsyncWrite + Unpin + Send, 61 | { 62 | let info: TInfo::Push = info.into(); 63 | let data = 64 | serde_json::to_vec(&info).map_err(|e| UpgradeError::Codec(format!("{:?}", e)))?; 65 | 66 | io.write_all(data.as_ref()).await?; 67 | Ok(()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /blocknet/src/service.rs: -------------------------------------------------------------------------------- 1 | use futures::stream::Stream; 2 | use std::{future::Future, ops::Deref}; 3 | 4 | pub trait Service: Send { 5 | type PeerId; 6 | type PeerInfo; 7 | type Error; 8 | 9 | fn local_info(&self) -> Self::PeerInfo; 10 | fn set_local_info(&mut self, info: Self::PeerInfo); 11 | fn peers(&self) -> impl IntoIterator; 12 | } 13 | 14 | pub trait Event { 15 | type Origin; 16 | type Value; 17 | 18 | fn origin(&self) -> impl Deref; 19 | fn value(&self) -> impl Deref; 20 | fn into_value(self) -> Self::Value; 21 | } 22 | 23 | pub trait Message { 24 | type Topic; 25 | 26 | fn topic(&self) -> Self::Topic; 27 | } 28 | 29 | pub trait BroadcastService: Service { 30 | type Event: Event; 31 | 32 | fn listen( 33 | &mut self, 34 | topic: Msg::Topic, 35 | ) -> impl Future + Send, Self::Error>> + Send; 36 | fn broadcast(&mut self, message: Msg) -> impl Future> + Send; 37 | } 38 | 39 | pub trait NotifyService: Service { 40 | type Event: Event; 41 | 42 | fn listen( 43 | &mut self, 44 | ) -> impl Future + Send, Self::Error>> + Send; 45 | fn notify( 46 | &mut self, 47 | peer: Self::PeerId, 48 | notification: Not, 49 | ) -> impl Future> + Send; 50 | } 51 | 52 | pub trait Request { 53 | type Response; 54 | } 55 | 56 | pub trait RequestService: Service { 57 | type Event: Event; 58 | type Channel; 59 | 60 | fn listen( 61 | &mut self, 62 | ) -> impl Future< 63 | Output = Result + Send, Self::Error>, 64 | > + Send; 65 | fn request( 66 | &mut self, 67 | peer: Self::PeerId, 68 | request: Req, 69 | ) -> impl Future> + Send; 70 | fn respond( 71 | &mut self, 72 | channel: Self::Channel, 73 | response: Req::Response, 74 | ) -> impl Future> + Send; 75 | } 76 | -------------------------------------------------------------------------------- /blocknet/src/libp2p/peer_info/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Parity Technologies (UK) Ltd. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | //! Implementation of the [Identify](https://github.com/libp2p/specs/blob/master/identify/README.md) protocol. 22 | //! 23 | //! This implementation of the protocol periodically exchanges 24 | //! [`Info`] messages between the peers on an established connection. 25 | //! 26 | //! At least one identification request is sent on a newly established 27 | //! connection, beyond which the behaviour does not keep connections alive. 28 | //! 29 | //! # Important Discrepancies 30 | //! 31 | //! - **Using Identify with other protocols** Unlike some other libp2p implementations, 32 | //! rust-libp2p does not treat Identify as a core protocol. This means that other protocols cannot 33 | //! rely upon the existence of Identify, and need to be manually hooked up to Identify in order to 34 | //! make use of its capabilities. 35 | //! 36 | //! # Usage 37 | //! 38 | //! The [`Behaviour`] struct implements a [`NetworkBehaviour`](libp2p_swarm::NetworkBehaviour) 39 | //! that negotiates and executes the protocol on every established connection, emitting 40 | //! [`Event`]s. 41 | 42 | #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] 43 | 44 | pub use self::behaviour::{Behaviour, Config, Event}; 45 | pub use self::codec::Codec; 46 | pub use self::protocol::{Info, UpgradeError}; 47 | 48 | mod behaviour; 49 | mod codec; 50 | mod handler; 51 | pub mod json; 52 | mod protocol; 53 | -------------------------------------------------------------------------------- /blocknet/examples/simple_libp2p.rs: -------------------------------------------------------------------------------- 1 | use blocknet::{BroadcastService, Message}; 2 | use futures::{select, stream::StreamExt, FutureExt}; 3 | use serde::{Deserialize, Serialize}; 4 | use tokio::{io, io::AsyncBufReadExt}; 5 | use tracing::{error, info}; 6 | 7 | #[derive(Debug, Clone, Serialize, Deserialize)] 8 | struct PeerInfo { 9 | best_block: u64, 10 | } 11 | 12 | #[derive(Debug, Clone, Serialize, Deserialize)] 13 | struct SimpleBroadcast { 14 | message: String, 15 | } 16 | 17 | struct SimpleBroadcastTopic; 18 | 19 | impl Message for SimpleBroadcast { 20 | type Topic = SimpleBroadcastTopic; 21 | 22 | fn topic(&self) -> Self::Topic { 23 | SimpleBroadcastTopic 24 | } 25 | } 26 | 27 | impl Into for SimpleBroadcastTopic { 28 | fn into(self) -> String { 29 | "simple_broadcast_topic".into() 30 | } 31 | } 32 | 33 | #[tokio::main] 34 | async fn main() -> Result<(), Box> { 35 | tracing_subscriber::fmt::init(); 36 | 37 | let worker = blocknet::libp2p::Worker::new(PeerInfo { best_block: 1 })?; 38 | let service = worker.service(); 39 | let mut worker_handle = tokio::spawn(worker.run()).fuse(); 40 | 41 | let mut stdin = io::BufReader::new(io::stdin()).lines(); 42 | let mut stream_service = service.clone(); 43 | let mut broadcast_stream = Box::pin( 44 | BroadcastService::::listen(&mut stream_service, SimpleBroadcastTopic) 45 | .await? 46 | .fuse(), 47 | ); 48 | 49 | loop { 50 | let mut service = service.clone(); 51 | 52 | select! { 53 | stdin_next = stdin.next_line().fuse() => { 54 | if let Ok(Some(line)) = stdin_next { 55 | if line == "exit" { 56 | break 57 | } 58 | match BroadcastService::::broadcast( 59 | &mut service, 60 | SimpleBroadcast { 61 | message: line, 62 | }, 63 | ).await { 64 | Ok(()) => info!("Broadcasted"), 65 | Err(e) => info!("Broadcast error: {:?}", e), 66 | } 67 | } 68 | }, 69 | message = broadcast_stream.select_next_some() => { 70 | info!("Received message: {:?}", message) 71 | }, 72 | worker_err = worker_handle => { 73 | if let Err(e) = worker_err { 74 | error!("Worker returned: {:?}", e); 75 | break 76 | } 77 | } 78 | } 79 | } 80 | 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /blockchain/src/memory/state.rs: -------------------------------------------------------------------------------- 1 | use core::hash::Hash; 2 | use core::ops::Bound; 3 | use std::collections::{BTreeMap, HashMap}; 4 | 5 | use crate::{FlatState, FlatStateMut, ForkTree, Identified}; 6 | 7 | /// A flat state that is stored in memory. 8 | #[derive(Debug, Clone)] 9 | pub struct MemoryFlatState { 10 | state: HashMap>>>, 11 | } 12 | 13 | impl MemoryFlatState 14 | where 15 | K: Eq + PartialEq + Hash, 16 | V: Clone, 17 | Identifier: Eq + PartialEq + Hash + Clone, 18 | { 19 | /// Create a new empty flat state. 20 | pub fn new() -> Self { 21 | Self { 22 | state: HashMap::new(), 23 | } 24 | } 25 | } 26 | 27 | impl FlatState for MemoryFlatState 28 | where 29 | K: Eq + PartialEq + Hash, 30 | V: Clone, 31 | Identifier: Eq + PartialEq + Hash + Clone, 32 | FT: ForkTree, 33 | B: Identified, 34 | { 35 | type Key = K; 36 | type Value = V; 37 | type QueryError = FT::QueryError; 38 | 39 | fn get( 40 | &self, 41 | key: &Self::Key, 42 | block_id: &::Identifier, 43 | fork_tree: &FT, 44 | ) -> Result, Self::QueryError> { 45 | if let Some(depth_to_id_value) = self.state.get(key) { 46 | let depth = fork_tree.block_depth(block_id)?; 47 | let search_range = depth_to_id_value 48 | .range((Bound::Unbounded, Bound::Included(depth))) 49 | .rev(); 50 | 51 | for (_, search_id_to_value) in search_range { 52 | for (search_id, search_value) in search_id_to_value { 53 | if fork_tree.is_ancestor(block_id, search_id)? { 54 | return Ok(search_value.clone()); 55 | } 56 | } 57 | } 58 | } 59 | 60 | Ok(None) 61 | } 62 | } 63 | 64 | impl FlatStateMut for MemoryFlatState 65 | where 66 | K: Eq + PartialEq + Hash, 67 | V: Clone, 68 | Identifier: Eq + PartialEq + Hash + Clone, 69 | FT: ForkTree, 70 | B: Identified, 71 | { 72 | type ApplyError = FT::QueryError; 73 | 74 | fn apply)>>( 75 | &mut self, 76 | changeset: I, 77 | block_id: ::Identifier, 78 | fork_tree: &FT, 79 | ) -> Result<(), Self::ApplyError> { 80 | let depth = fork_tree.block_depth(&block_id)?; 81 | 82 | for (key, value) in changeset { 83 | self.state 84 | .entry(key) 85 | .or_default() 86 | .entry(depth) 87 | .or_default() 88 | .insert(block_id.clone(), value); 89 | } 90 | 91 | Ok(()) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tinyjam/src/core_seal.rs: -------------------------------------------------------------------------------- 1 | //! # In-core sealing algorithm. 2 | //! 3 | //! The in-core sealing protocol is a generalization of Polkadot JAM's part 4 | //! of the in-core computation. It ensures the following: 5 | //! 6 | //! * Given a work package, it ensures that it is *authorized* on the current 7 | //! core. 8 | //! * Participating validators would then *refine* the work package to produce 9 | //! a work report. 10 | //! * It further ensures availability, and later handle disputes. 11 | //! 12 | //! The types of the in-core sealing algorithm defined here deals with only a 13 | //! single core (in another word, there's no core ID). It is expected that 14 | //! different cores will need their own worker thread anyway. 15 | //! 16 | //! We do not have the concept of a block in this module. The algorithm works 17 | //! through a *handle*, which acts as a state machine, with always-up-to-date 18 | //! information. It's the responsibility of the handle to fetch states and to 19 | //! update states to the relay chain blocks. In practice, work packages 20 | //! usually have their own pins to specific blocks. No block building or 21 | //! transaction creation is done in this module. In all other cases, it operates 22 | //! over the current best blocks. 23 | //! 24 | //! Related to the specification, this module only handles in-core. This means 25 | //! `authorize` and `refine`, but not later stages of `accumulate` and 26 | //! `on_transfer`. 27 | //! 28 | //! ## Cycle of the worker 29 | //! 30 | //! The worker thread accepts a stream receiving work packages. Upon checking 31 | //! that the work package is authorized, it takes ownership of it, refines it to 32 | //! get the work report, and then attest it to publish on the relay chain. 33 | //! 34 | //! Another stream will receive a tuple of work packages and work reports 35 | //! already generated, and attest them. 36 | 37 | use std::future::Future; 38 | 39 | /// Handle for the in-core sealing. 40 | /// 41 | /// This works like a state machine. Work packages usually have their own pins, 42 | /// and if not specified, it works against the best block / the most updated 43 | /// network. 44 | pub trait CoreSealHandle { 45 | /// Error type for the handle. 46 | type Error; 47 | 48 | /// A work package, pre-refine. 49 | type WorkPackage; 50 | /// A work report from a work package, post-refine. 51 | type WorkReport; 52 | 53 | /// Whether the work package is authorized on the current core. 54 | fn is_authorized(&self, work: &Self::WorkPackage) -> bool; 55 | /// Refine from a work package into a work report. 56 | fn refine( 57 | &self, 58 | work: Self::WorkPackage, 59 | ) -> impl Future> + Send; 60 | 61 | /// Attest to a work report and submit it. 62 | fn attest( 63 | &mut self, 64 | report: Self::WorkReport, 65 | ) -> impl Future> + Send; 66 | /// Dispute a work report and submit it. 67 | fn dispute( 68 | &mut self, 69 | own: Self::WorkReport, 70 | other: Self::WorkReport, 71 | ) -> impl Future> + Send; 72 | } 73 | -------------------------------------------------------------------------------- /blockchain/src/state.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ForkTree, Identified}; 4 | 5 | /// Flat state. 6 | /// 7 | /// A flat state is a type of key-value database that contains all historical 8 | /// data, yet without using a merkle tree. It is usually implemented by storing 9 | /// the changeset in the database, and fetch them on demand. 10 | pub trait FlatState { 11 | /// Key type. 12 | type Key; 13 | /// Value type. 14 | type Value; 15 | /// Error type. 16 | type QueryError; 17 | 18 | /// Get the value at particular block id. 19 | fn get( 20 | &self, 21 | key: &Self::Key, 22 | block_id: &::Identifier, 23 | fork_tree: &FT, 24 | ) -> Result, Self::QueryError>; 25 | 26 | /// Overlayed state. 27 | fn overlayed<'fs, 'ft>( 28 | &'fs self, 29 | block_id: ::Identifier, 30 | fork_tree: &'ft FT, 31 | ) -> OverlayedFlatState<'fs, 'ft, Self, FT> { 32 | OverlayedFlatState { 33 | flat_state: self, 34 | block_id, 35 | fork_tree, 36 | changeset: HashMap::new(), 37 | } 38 | } 39 | } 40 | 41 | /// Mutable flat state. 42 | pub trait FlatStateMut: FlatState { 43 | /// Apply error type. 44 | type ApplyError; 45 | 46 | /// Apply a changeset to a particular block id. 47 | fn apply)>>( 48 | &mut self, 49 | changeset: I, 50 | block_id: ::Identifier, 51 | fork_tree: &FT, 52 | ) -> Result<(), Self::ApplyError>; 53 | } 54 | 55 | /// Transactional flat state. 56 | pub trait FlatStateTransactional: FlatState { 57 | /// Transaction type. 58 | type Transaction; 59 | /// Apply error type. 60 | type ApplyError; 61 | 62 | /// Apply a changeset to a particular block id. 63 | fn apply)>>( 64 | &self, 65 | transaction: &mut Self::Transaction, 66 | changeset: I, 67 | block_id: &::Identifier, 68 | fork_tree: &FT, 69 | ) -> Result<(), Self::ApplyError>; 70 | } 71 | 72 | /// Convinence function for building a changeset of a flat state. 73 | pub struct OverlayedFlatState<'fs, 'ft, FS: FlatState + ?Sized, FT: ForkTree> { 74 | flat_state: &'fs FS, 75 | fork_tree: &'ft FT, 76 | block_id: ::Identifier, 77 | changeset: HashMap>, 78 | } 79 | 80 | impl<'fs, 'ft, FS, FT> OverlayedFlatState<'fs, 'ft, FS, FT> 81 | where 82 | FS: FlatState + ?Sized, 83 | FS::Key: Clone + Eq + PartialEq + core::hash::Hash, 84 | FS::Value: Clone, 85 | FT: ForkTree, 86 | { 87 | /// Get a value from the overlay. 88 | pub fn get(&self, key: &FS::Key) -> Result, FS::QueryError> { 89 | if let Some(value) = self.changeset.get(key) { 90 | Ok(value.clone()) 91 | } else { 92 | self.flat_state.get(key, &self.block_id, self.fork_tree) 93 | } 94 | } 95 | 96 | /// Insert a new value. 97 | pub fn insert(&mut self, key: FS::Key, value: FS::Value) { 98 | self.changeset.insert(key, Some(value)); 99 | } 100 | 101 | /// Remove an existing value. 102 | pub fn remove(&mut self, key: &FS::Key) { 103 | self.changeset.insert(key.clone(), None); 104 | } 105 | 106 | /// Into changeset. 107 | pub fn into_changeset(self) -> impl Iterator)> { 108 | self.changeset.into_iter() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /blockchain/src/chain.rs: -------------------------------------------------------------------------------- 1 | use crate::Identified; 2 | 3 | /// Fork tree. 4 | /// 5 | /// A fork tree tracks blocks of forks. However, it does not track the best 6 | /// block. Instead, that is supposed to be handled by the `Chain` directly. The 7 | /// reason for this is that, except storing the best block hash, the majority of 8 | /// best block tracking is used for optimizations -- rebalancing the trees, 9 | /// moving non-canon blocks out of cache, pruning nodes, etc. They are not only 10 | /// needed in a fork tree, but also in states, as well as other related structs. 11 | pub trait ForkTree { 12 | /// The type of the identified. It can be a block or a header. 13 | type Block: Identified; 14 | /// Query error type. 15 | type QueryError; 16 | 17 | /// Get a block by its id. 18 | fn block( 19 | &self, 20 | id: &::Identifier, 21 | ) -> Result; 22 | 23 | /// Get a block depth by its id. 24 | fn block_depth( 25 | &self, 26 | id: &::Identifier, 27 | ) -> Result; 28 | 29 | /// Find an ancestor block at given depth. 30 | /// 31 | /// If ancestor depth equals the provided block's depth, return the provided block ID. 32 | fn ancestor_id_at_depth( 33 | &self, 34 | id: &::Identifier, 35 | ancestor_depth: usize, 36 | ) -> Result<::Identifier, Self::QueryError>; 37 | 38 | /// Whether is ancestor. 39 | /// 40 | /// If ancestor depth equals the provided block's depth, return true. 41 | fn is_ancestor( 42 | &self, 43 | id: &::Identifier, 44 | ancestor_id: &::Identifier, 45 | ) -> Result { 46 | let ancestor_depth = self.block_depth(ancestor_id)?; 47 | 48 | Ok(self.ancestor_id_at_depth(id, ancestor_depth)? == *ancestor_id) 49 | } 50 | } 51 | 52 | /// A structure representing a chain with possible forks. 53 | pub trait ForkTreeMut: ForkTree { 54 | /// Insert error type. 55 | type InsertError; 56 | 57 | /// Insert a new block. 58 | fn insert(&mut self, block: Self::Block) -> Result<(), Self::InsertError>; 59 | } 60 | 61 | /// Transactional fork tree. 62 | pub trait ForkTreeTransactional: ForkTree { 63 | /// Transaction type. 64 | type Transaction; 65 | /// Insert error type. 66 | type InsertError; 67 | 68 | /// Insert a new block. 69 | fn insert( 70 | &self, 71 | transaction: &mut Self::Transaction, 72 | block: Self::Block, 73 | ) -> Result<(), Self::InsertError>; 74 | } 75 | 76 | /// A chain that can import external blocks. 77 | pub trait ImportBlock { 78 | /// Type of the block. 79 | type Block; 80 | /// Error type. 81 | type Error; 82 | 83 | /// Import a new block, given a fork tree. 84 | fn import(&mut self, block: Self::Block) -> Result<(), Self::Error>; 85 | } 86 | 87 | /// Block builder. 88 | pub trait BlockBuilder<'chain>: Sized { 89 | /// Type of the chain. 90 | type Chain; 91 | /// Type of the block. 92 | type Block: Identified; 93 | /// Type of extrinsic. 94 | type Extrinsic; 95 | /// Type of error. 96 | type Error; 97 | /// Type of pre-log. 98 | type PreLog; 99 | /// Type of post-log. 100 | type PostLog; 101 | 102 | /// Initialize a new block. 103 | fn initialize( 104 | chain: &'chain Self::Chain, 105 | parent_id: ::Identifier, 106 | pre_log: Self::PreLog, 107 | ) -> Result; 108 | /// Apply a new extrinsic. 109 | fn apply_extrinsic(&mut self, extrinsic: Self::Extrinsic) -> Result<(), Self::Error>; 110 | /// Finalize the current block. 111 | fn finalize(self, post_log: Self::PostLog) -> Result; 112 | } 113 | -------------------------------------------------------------------------------- /blockchain/src/memory/chain.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | use std::collections::HashMap; 3 | 4 | use crate::{ForkTree, ForkTreeMut, Identified}; 5 | 6 | #[derive(Clone, Debug)] 7 | struct MemoryForkTreeItem { 8 | block: Block, 9 | depth: usize, 10 | children: Vec, 11 | ancestors: Vec<(usize, Block::Identifier)>, 12 | } 13 | 14 | /// A fork tree that resides entirely in memory. Useful for testing. 15 | #[derive(Debug, Clone)] 16 | pub struct MemoryForkTree { 17 | blocks: HashMap>, 18 | depths: HashMap>, 19 | } 20 | 21 | impl MemoryForkTree { 22 | /// Create a new fork tree. 23 | pub fn new() -> Self { 24 | Self { 25 | blocks: HashMap::new(), 26 | depths: HashMap::new(), 27 | } 28 | } 29 | } 30 | 31 | /// Query error for memory fork tree. 32 | #[derive(Debug, Clone)] 33 | pub enum MemoryForkTreeQueryError { 34 | /// Block is unknown. 35 | UnknownBlock, 36 | /// Ancestor depth provided is greater than current block depth. 37 | InvalidAncestorDepth, 38 | } 39 | 40 | impl ForkTree for MemoryForkTree { 41 | type Block = Block; 42 | type QueryError = MemoryForkTreeQueryError; 43 | 44 | fn block(&self, id: &Block::Identifier) -> Result { 45 | Ok(self 46 | .blocks 47 | .get(&id) 48 | .ok_or(MemoryForkTreeQueryError::UnknownBlock)? 49 | .block 50 | .clone()) 51 | } 52 | 53 | fn block_depth(&self, id: &Block::Identifier) -> Result { 54 | Ok(self 55 | .blocks 56 | .get(&id) 57 | .ok_or(MemoryForkTreeQueryError::UnknownBlock)? 58 | .depth) 59 | } 60 | 61 | fn ancestor_id_at_depth( 62 | &self, 63 | id: &Block::Identifier, 64 | ancestor_depth: usize, 65 | ) -> Result { 66 | let mut current_block = self 67 | .blocks 68 | .get(&id) 69 | .ok_or(MemoryForkTreeQueryError::UnknownBlock)?; 70 | 71 | if current_block.depth > ancestor_depth { 72 | return Err(MemoryForkTreeQueryError::InvalidAncestorDepth); 73 | } 74 | 75 | loop { 76 | if current_block.depth < ancestor_depth { 77 | return Err(MemoryForkTreeQueryError::InvalidAncestorDepth); 78 | } 79 | 80 | if current_block.depth == ancestor_depth { 81 | return Ok(current_block.block.id()); 82 | } 83 | 84 | let parent_id = current_block 85 | .block 86 | .parent_id() 87 | // If the current block depth is 0, then the ancestor depth 88 | // provided must be invalid. 89 | .ok_or(MemoryForkTreeQueryError::InvalidAncestorDepth)?; 90 | 91 | let next_ancestor_id = current_block 92 | .ancestors 93 | .iter() 94 | // Find all ancestor depth that is greater than the target 95 | // ancestor depth. 96 | .filter(|(d, _)| *d >= ancestor_depth) 97 | .cloned() 98 | // Then find the minimum of all the values. 99 | .reduce(|(min_depth, min_id), (d, id)| { 100 | if d < min_depth { 101 | (d, id) 102 | } else { 103 | (min_depth, min_id) 104 | } 105 | }) 106 | .map(|(_, id)| id) 107 | .unwrap_or(parent_id); 108 | 109 | current_block = self 110 | .blocks 111 | .get(&next_ancestor_id) 112 | .ok_or(MemoryForkTreeQueryError::UnknownBlock)?; 113 | } 114 | } 115 | } 116 | 117 | /// Insert error for memory fork tree. 118 | #[derive(Debug, Clone)] 119 | pub enum MemoryForkTreeInsertError { 120 | /// Parent is unknown. 121 | UnknownParent, 122 | /// Encounted a query issue in insertion. 123 | Query(MemoryForkTreeQueryError), 124 | } 125 | 126 | impl From for MemoryForkTreeInsertError { 127 | fn from(query: MemoryForkTreeQueryError) -> MemoryForkTreeInsertError { 128 | MemoryForkTreeInsertError::Query(query) 129 | } 130 | } 131 | 132 | /// Skip depths for ancestor list. 133 | const SKIP_DEPTHS: [usize; 16] = [ 134 | 4usize.pow(1), 135 | 4usize.pow(2), 136 | 4usize.pow(3), 137 | 4usize.pow(4), 138 | 4usize.pow(5), 139 | 4usize.pow(6), 140 | 4usize.pow(7), 141 | 4usize.pow(8), 142 | 4usize.pow(9), 143 | 4usize.pow(10), 144 | 4usize.pow(11), 145 | 4usize.pow(12), 146 | 4usize.pow(13), 147 | 4usize.pow(14), 148 | 4usize.pow(15), 149 | 4usize.pow(16), 150 | ]; 151 | 152 | impl ForkTreeMut for MemoryForkTree { 153 | type InsertError = MemoryForkTreeInsertError; 154 | 155 | fn insert(&mut self, block: Block) -> Result<(), Self::InsertError> { 156 | let block_id = block.id(); 157 | 158 | let depth = if let Some(parent_id) = block.parent_id() { 159 | let parent = self 160 | .blocks 161 | .get_mut(&parent_id) 162 | .ok_or(MemoryForkTreeInsertError::UnknownParent)?; 163 | parent.children.push(block.id()); 164 | parent.depth + 1 165 | } else { 166 | 0 167 | }; 168 | 169 | let ancestors = if let Some(parent_id) = block.parent_id() { 170 | // Build a skip list of ancestors. If the current block depth can be 171 | // divided by `SKIP_DEPTHS`, then we call `ancestor_id_at_depth` to 172 | // track back on the ancestor block. 173 | let ancestor_depths = SKIP_DEPTHS 174 | .iter() 175 | .filter(|skip_depth| &depth >= *skip_depth && depth % *skip_depth == 0) 176 | .map(|skip_depth| depth - skip_depth) 177 | .unique(); 178 | 179 | let mut ancestors = Vec::new(); 180 | for ancestor_depth in ancestor_depths { 181 | ancestors.push(( 182 | ancestor_depth, 183 | self.ancestor_id_at_depth(&parent_id, ancestor_depth)?, 184 | )); 185 | } 186 | 187 | ancestors 188 | } else { 189 | Vec::new() 190 | }; 191 | 192 | self.depths.entry(depth).or_default().push(block_id); 193 | self.blocks.insert( 194 | block_id, 195 | MemoryForkTreeItem { 196 | block, 197 | depth, 198 | children: Vec::new(), 199 | ancestors, 200 | }, 201 | ); 202 | 203 | Ok(()) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /blockchain/tests/simple.rs: -------------------------------------------------------------------------------- 1 | //! This is a simple chain test, with fixed hashes and seal. 2 | 3 | use blockchain::memory::{ 4 | MemoryFlatState, MemoryForkTree, MemoryForkTreeInsertError, MemoryForkTreeQueryError, 5 | MemoryTransactional, 6 | }; 7 | use blockchain::{ 8 | BlockBuilder, FlatState, FlatStateMut, ForkTree, ForkTreeMut, Headered, Identified, 9 | ImportBlock, Keyed, OverlayedFlatState, 10 | }; 11 | 12 | /// A simple seal. 13 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 14 | pub enum Seal { 15 | /// Valid seal. 16 | ValidSeal, 17 | /// Invalid seal. 18 | InvalidSeal, 19 | } 20 | 21 | /// Specific block hashes. 22 | #[derive(Debug, Clone, Copy, Eq, PartialEq, core::hash::Hash)] 23 | pub struct BlockId { 24 | fork: u32, 25 | number: u32, 26 | } 27 | 28 | /// Extrinsic type. 29 | #[derive(Debug, Clone)] 30 | pub enum Extrinsic { 31 | Set(u32, u32), 32 | } 33 | 34 | /// Simple block structure. 35 | #[derive(Debug, Clone)] 36 | pub struct Block { 37 | pub seal: Seal, 38 | pub id: BlockId, 39 | pub parent_id: Option, 40 | pub number: u32, 41 | pub extrinsics: Vec, 42 | } 43 | 44 | /// Header is simply block with extrinsic removed. 45 | #[derive(Debug, Clone)] 46 | pub struct Header { 47 | pub seal: Seal, 48 | pub id: BlockId, 49 | pub parent_id: Option, 50 | pub number: u32, 51 | } 52 | 53 | impl Identified for Block { 54 | type Identifier = BlockId; 55 | 56 | fn id(&self) -> BlockId { 57 | self.id 58 | } 59 | 60 | fn parent_id(&self) -> Option { 61 | self.parent_id 62 | } 63 | } 64 | 65 | // Block can also be keyed by its block number instead of block id. 66 | impl Keyed for Block { 67 | fn key(&self) -> u32 { 68 | self.number 69 | } 70 | } 71 | 72 | impl Headered for Block { 73 | type Header = Header; 74 | 75 | fn header(&self) -> Header { 76 | Header { 77 | seal: self.seal, 78 | id: self.id, 79 | parent_id: self.parent_id, 80 | number: self.number, 81 | } 82 | } 83 | } 84 | 85 | #[derive(Debug, Clone)] 86 | pub struct ChainData { 87 | pub fork_tree: MemoryForkTree, 88 | pub state: MemoryFlatState, 89 | } 90 | 91 | /// Define the chain. 92 | #[derive(Debug, Clone)] 93 | pub struct Chain { 94 | pub data: MemoryTransactional, 95 | } 96 | 97 | #[derive(Debug, Clone)] 98 | pub enum ChainError { 99 | InvalidSeal, 100 | CantImportGenesis, 101 | ForkTreeInsert(MemoryForkTreeInsertError), 102 | ForkTreeQuery(MemoryForkTreeQueryError), 103 | } 104 | 105 | impl From for ChainError { 106 | fn from(err: MemoryForkTreeInsertError) -> Self { 107 | Self::ForkTreeInsert(err) 108 | } 109 | } 110 | 111 | impl From for ChainError { 112 | fn from(err: MemoryForkTreeQueryError) -> Self { 113 | Self::ForkTreeQuery(err) 114 | } 115 | } 116 | 117 | impl ImportBlock for Chain { 118 | type Block = Block; 119 | type Error = ChainError; 120 | 121 | fn import(&mut self, block: Block) -> Result<(), Self::Error> { 122 | // Verify the seal is valid. 123 | if block.seal != Seal::ValidSeal { 124 | return Err(ChainError::InvalidSeal); 125 | } 126 | 127 | self.data.apply(|data| { 128 | let parent_id = block.parent_id().ok_or(ChainError::CantImportGenesis)?; 129 | 130 | data.fork_tree.insert(block.clone())?; 131 | 132 | let mut overlay = data.state.overlayed(parent_id, &data.fork_tree); 133 | for extrinsic in &block.extrinsics { 134 | match extrinsic { 135 | Extrinsic::Set(key, value) => { 136 | overlay.insert(*key, *value); 137 | } 138 | } 139 | } 140 | 141 | let changeset = overlay.into_changeset(); 142 | data.state.apply(changeset, block.id(), &data.fork_tree)?; 143 | 144 | Ok(()) 145 | }) 146 | } 147 | } 148 | 149 | /// Chain builder. 150 | pub struct ChainBlockBuilder<'chain> { 151 | pub chain: &'chain Chain, 152 | pub block: Block, 153 | pub overlay: OverlayedFlatState< 154 | 'chain, 155 | 'chain, 156 | MemoryFlatState, 157 | MemoryForkTree, 158 | >, 159 | } 160 | 161 | impl<'chain> BlockBuilder<'chain> for ChainBlockBuilder<'chain> { 162 | type Block = Block; 163 | type Extrinsic = Extrinsic; 164 | type Error = ChainError; 165 | type PreLog = (); 166 | type PostLog = Seal; 167 | type Chain = Chain; 168 | 169 | fn initialize( 170 | chain: &'chain Chain, 171 | parent_id: ::Identifier, 172 | _pre_log: (), 173 | ) -> Result { 174 | let _parent_block = chain.data.fork_tree.block(&parent_id)?; 175 | 176 | let block = Block { 177 | seal: Seal::InvalidSeal, 178 | parent_id: Some(parent_id), 179 | id: BlockId { 180 | fork: parent_id.fork, 181 | number: parent_id.number + 1, 182 | }, 183 | number: parent_id.number + 1, 184 | extrinsics: Vec::new(), 185 | }; 186 | 187 | Ok(ChainBlockBuilder { 188 | block, 189 | chain, 190 | overlay: chain.data.state.overlayed(parent_id, &chain.data.fork_tree), 191 | }) 192 | } 193 | 194 | fn apply_extrinsic(&mut self, extrinsic: Extrinsic) -> Result<(), Self::Error> { 195 | self.block.extrinsics.push(extrinsic); 196 | Ok(()) 197 | } 198 | 199 | fn finalize(mut self, post_log: Seal) -> Result { 200 | self.block.seal = post_log; 201 | Ok(self.block) 202 | } 203 | } 204 | 205 | #[test] 206 | fn basic_build_and_import() -> Result<(), ChainError> { 207 | // Define a genesis block. 208 | let genesis_block = Block { 209 | seal: Seal::InvalidSeal, // Genesis block does not need to be verified. 210 | id: BlockId { fork: 0, number: 0 }, 211 | parent_id: None, 212 | number: 0, 213 | extrinsics: Vec::new(), 214 | }; 215 | 216 | // Define a genesis state. 217 | let genesis_state = vec![(100, Some(100)), (200, Some(200))]; 218 | 219 | // Create a new chain. 220 | let mut chain = Chain { 221 | data: MemoryTransactional::new(ChainData { 222 | fork_tree: MemoryForkTree::new(), 223 | state: MemoryFlatState::new(), 224 | }), 225 | }; 226 | 227 | // Import the genesis into fork tree. 228 | chain.data.apply(|data| { 229 | data.fork_tree.insert(genesis_block.clone())?; 230 | 231 | // It's possible to handle extrinsics in a genesis, but it's a rare thing, 232 | // and here we just assert that it's empty. 233 | assert!(genesis_block.extrinsics.is_empty()); 234 | 235 | data.state.apply( 236 | genesis_state.clone().into_iter(), 237 | genesis_block.id(), 238 | &data.fork_tree, 239 | )?; 240 | 241 | Ok::<_, ChainError>(()) 242 | })?; 243 | 244 | // Build a new block. 245 | let mut builder = ChainBlockBuilder::initialize(&chain, genesis_block.id(), ())?; 246 | builder.apply_extrinsic(Extrinsic::Set(100, 200))?; 247 | let block = builder.finalize(Seal::ValidSeal)?; 248 | 249 | // Import the block. 250 | chain.import(block.clone())?; 251 | 252 | // Check that the state is actually set. 253 | assert_eq!( 254 | chain 255 | .data 256 | .state 257 | .get(&100, &genesis_block.id(), &chain.data.fork_tree)?, 258 | Some(100), 259 | ); 260 | assert_eq!( 261 | chain 262 | .data 263 | .state 264 | .get(&100, &block.id(), &chain.data.fork_tree)?, 265 | Some(200), 266 | ); 267 | 268 | // Create a fork. 269 | let mut block2 = block.clone(); 270 | block2.id = BlockId { 271 | fork: 1, 272 | number: block2.id.number, 273 | }; 274 | block2.extrinsics[0] = Extrinsic::Set(100, 300); 275 | chain.import(block2.clone())?; 276 | assert_eq!( 277 | chain 278 | .data 279 | .state 280 | .get(&100, &block2.id(), &chain.data.fork_tree)?, 281 | Some(300), 282 | ); 283 | 284 | Ok(()) 285 | } 286 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /blocknet/src/libp2p/peer_info/behaviour.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Parity Technologies (UK) Ltd. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | use super::handler::{self, Handler, InEvent}; 22 | use super::{Codec, Info, UpgradeError}; 23 | use libp2p::core::{ConnectedPoint, Endpoint, Multiaddr}; 24 | use libp2p::identity::PeerId; 25 | use libp2p::identity::PublicKey; 26 | use libp2p::swarm::behaviour::{ConnectionClosed, ConnectionEstablished, FromSwarm}; 27 | use libp2p::swarm::{ 28 | ConnectionDenied, ExternalAddresses, ListenAddresses, NetworkBehaviour, NotifyHandler, 29 | StreamProtocol, StreamUpgradeError, THandlerInEvent, ToSwarm, 30 | }; 31 | use libp2p::swarm::{ConnectionId, THandler, THandlerOutEvent}; 32 | use std::{ 33 | collections::{HashMap, HashSet, VecDeque}, 34 | marker::PhantomData, 35 | task::Context, 36 | task::Poll, 37 | time::Duration, 38 | }; 39 | 40 | /// Network behaviour that automatically identifies nodes periodically, returns information 41 | /// about them, and answers identify queries from other nodes. 42 | /// 43 | /// All external addresses of the local node supposedly observed by remotes 44 | /// are reported via [`ToSwarm::NewExternalAddrCandidate`]. 45 | pub struct Behaviour> { 46 | config: Config, 47 | /// For each peer we're connected to, the observed address to send back to it. 48 | connected: HashMap>, 49 | 50 | /// The address a remote observed for us. 51 | our_observed_addresses: HashMap, 52 | 53 | /// Pending events to be emitted when polled. 54 | events: VecDeque, InEvent>>, 55 | 56 | listen_addresses: ListenAddresses, 57 | external_addresses: ExternalAddresses, 58 | 59 | local_info: TInfo, 60 | 61 | _marker: PhantomData<(TInfo, TCodec)>, 62 | } 63 | 64 | /// Configuration for the [`identify::Behaviour`](Behaviour). 65 | #[non_exhaustive] 66 | #[derive(Debug, Clone)] 67 | pub struct Config { 68 | /// Application-specific version of the protocol family used by the peer, 69 | /// e.g. `ipfs/1.0.0` or `polkadot/1.0.0`. 70 | pub protocol_version: String, 71 | /// The public key of the local node. To report on the wire. 72 | pub local_public_key: PublicKey, 73 | /// Name and version of the local peer implementation, similar to the 74 | /// `User-Agent` header in the HTTP protocol. 75 | /// 76 | /// Defaults to `rust-libp2p/`. 77 | pub agent_version: String, 78 | /// The interval at which identification requests are sent to 79 | /// the remote on established connections after the first request, 80 | /// i.e. the delay between identification requests. 81 | /// 82 | /// Defaults to 5 minutes. 83 | pub interval: Duration, 84 | 85 | /// Whether new or expired listen addresses of the local node should 86 | /// trigger an active push of an identify message to all connected peers. 87 | /// 88 | /// Enabling this option can result in connected peers being informed 89 | /// earlier about new or expired listen addresses of the local node, 90 | /// i.e. before the next periodic identify request with each peer. 91 | /// 92 | /// Disabled by default. 93 | pub push_listen_addr_updates: bool, 94 | 95 | /// How many entries of discovered peers to keep before we discard 96 | /// the least-recently used one. 97 | /// 98 | /// Disabled by default. 99 | pub cache_size: usize, 100 | 101 | /// Protocol name. 102 | pub protocol_name: StreamProtocol, 103 | 104 | /// Push protocol name. 105 | pub push_protocol_name: StreamProtocol, 106 | } 107 | 108 | impl Config { 109 | /// Creates a new configuration for the identify [`Behaviour`] that 110 | /// advertises the given protocol version and public key. 111 | pub fn new( 112 | protocol_version: String, 113 | local_public_key: PublicKey, 114 | protocol_name: StreamProtocol, 115 | push_protocol_name: StreamProtocol, 116 | ) -> Self { 117 | Self { 118 | protocol_version, 119 | agent_version: format!("rust-libp2p/{}", env!("CARGO_PKG_VERSION")), 120 | local_public_key, 121 | interval: Duration::from_secs(5 * 60), 122 | push_listen_addr_updates: false, 123 | cache_size: 100, 124 | protocol_name, 125 | push_protocol_name, 126 | } 127 | } 128 | 129 | /// Configures the agent version sent to peers. 130 | pub fn with_agent_version(mut self, v: String) -> Self { 131 | self.agent_version = v; 132 | self 133 | } 134 | 135 | /// Configures the interval at which identification requests are 136 | /// sent to peers after the initial request. 137 | pub fn with_interval(mut self, d: Duration) -> Self { 138 | self.interval = d; 139 | self 140 | } 141 | 142 | /// Configures whether new or expired listen addresses of the local 143 | /// node should trigger an active push of an identify message to all 144 | /// connected peers. 145 | pub fn with_push_listen_addr_updates(mut self, b: bool) -> Self { 146 | self.push_listen_addr_updates = b; 147 | self 148 | } 149 | 150 | /// Configures the size of the LRU cache, caching addresses of discovered peers. 151 | pub fn with_cache_size(mut self, cache_size: usize) -> Self { 152 | self.cache_size = cache_size; 153 | self 154 | } 155 | } 156 | 157 | impl> Behaviour { 158 | /// Creates a new identify [`Behaviour`]. 159 | pub fn new(config: Config, local_info: TInfo) -> Self { 160 | Self { 161 | config, 162 | connected: HashMap::new(), 163 | our_observed_addresses: Default::default(), 164 | events: VecDeque::new(), 165 | listen_addresses: Default::default(), 166 | external_addresses: Default::default(), 167 | local_info, 168 | _marker: PhantomData, 169 | } 170 | } 171 | 172 | /// Initiates an active push of the local peer information to the given peers. 173 | pub fn push(&mut self, peers: I) 174 | where 175 | I: IntoIterator, 176 | { 177 | for p in peers { 178 | if !self.connected.contains_key(&p) { 179 | tracing::debug!(peer=%p, "Not pushing to peer because we are not connected"); 180 | continue; 181 | } 182 | 183 | self.events.push_back(ToSwarm::NotifyHandler { 184 | peer_id: p, 185 | handler: NotifyHandler::Any, 186 | event: InEvent::Push, 187 | }); 188 | } 189 | } 190 | 191 | fn on_connection_established( 192 | &mut self, 193 | ConnectionEstablished { 194 | peer_id, 195 | connection_id: conn, 196 | endpoint, 197 | .. 198 | }: ConnectionEstablished, 199 | ) { 200 | let addr = match endpoint { 201 | ConnectedPoint::Dialer { address, .. } => address.clone(), 202 | ConnectedPoint::Listener { send_back_addr, .. } => send_back_addr.clone(), 203 | }; 204 | 205 | self.connected 206 | .entry(peer_id) 207 | .or_default() 208 | .insert(conn, addr); 209 | } 210 | 211 | fn all_addresses(&self) -> HashSet { 212 | self.listen_addresses 213 | .iter() 214 | .chain(self.external_addresses.iter()) 215 | .cloned() 216 | .collect() 217 | } 218 | } 219 | 220 | impl> NetworkBehaviour for Behaviour { 221 | type ConnectionHandler = Handler; 222 | type ToSwarm = Event; 223 | 224 | fn handle_established_inbound_connection( 225 | &mut self, 226 | _: ConnectionId, 227 | peer: PeerId, 228 | _: &Multiaddr, 229 | _remote_addr: &Multiaddr, 230 | ) -> Result, ConnectionDenied> { 231 | Ok(Handler::new( 232 | self.config.interval, 233 | peer, 234 | self.all_addresses(), 235 | self.local_info.clone(), 236 | self.config.protocol_name.clone(), 237 | self.config.push_protocol_name.clone(), 238 | )) 239 | } 240 | 241 | fn handle_established_outbound_connection( 242 | &mut self, 243 | _: ConnectionId, 244 | peer: PeerId, 245 | _addr: &Multiaddr, 246 | _: Endpoint, 247 | ) -> Result, ConnectionDenied> { 248 | Ok(Handler::new( 249 | self.config.interval, 250 | peer, 251 | self.all_addresses(), 252 | self.local_info.clone(), 253 | self.config.protocol_name.clone(), 254 | self.config.push_protocol_name.clone(), 255 | )) 256 | } 257 | 258 | fn on_connection_handler_event( 259 | &mut self, 260 | peer_id: PeerId, 261 | _id: ConnectionId, 262 | event: THandlerOutEvent, 263 | ) { 264 | match event { 265 | handler::Event::Identified(info) => { 266 | self.events 267 | .push_back(ToSwarm::GenerateEvent(Event::Received { peer_id, info })); 268 | } 269 | handler::Event::Identification => { 270 | self.events 271 | .push_back(ToSwarm::GenerateEvent(Event::Sent { peer_id })); 272 | } 273 | handler::Event::IdentificationPushed(info) => { 274 | self.events 275 | .push_back(ToSwarm::GenerateEvent(Event::Pushed { peer_id, info })); 276 | } 277 | handler::Event::IdentificationError(error) => { 278 | self.events 279 | .push_back(ToSwarm::GenerateEvent(Event::Error { peer_id, error })); 280 | } 281 | } 282 | } 283 | 284 | #[tracing::instrument(level = "trace", name = "NetworkBehaviour::poll", skip(self))] 285 | fn poll(&mut self, _: &mut Context<'_>) -> Poll>> { 286 | if let Some(event) = self.events.pop_front() { 287 | return Poll::Ready(event); 288 | } 289 | 290 | Poll::Pending 291 | } 292 | 293 | fn on_swarm_event(&mut self, event: FromSwarm) { 294 | let listen_addr_changed = self.listen_addresses.on_swarm_event(&event); 295 | let external_addr_changed = self.external_addresses.on_swarm_event(&event); 296 | 297 | if listen_addr_changed || external_addr_changed { 298 | // notify all connected handlers about our changed addresses 299 | let change_events = self 300 | .connected 301 | .iter() 302 | .flat_map(|(peer, map)| map.keys().map(|id| (*peer, id))) 303 | .map(|(peer_id, connection_id)| ToSwarm::NotifyHandler { 304 | peer_id, 305 | handler: NotifyHandler::One(*connection_id), 306 | event: InEvent::AddressesChanged(self.all_addresses()), 307 | }) 308 | .collect::>(); 309 | 310 | self.events.extend(change_events) 311 | } 312 | 313 | if listen_addr_changed && self.config.push_listen_addr_updates { 314 | // trigger an identify push for all connected peers 315 | let push_events = self.connected.keys().map(|peer| ToSwarm::NotifyHandler { 316 | peer_id: *peer, 317 | handler: NotifyHandler::Any, 318 | event: InEvent::Push, 319 | }); 320 | 321 | self.events.extend(push_events); 322 | } 323 | 324 | match event { 325 | FromSwarm::ConnectionEstablished(connection_established) => { 326 | self.on_connection_established(connection_established) 327 | } 328 | FromSwarm::ConnectionClosed(ConnectionClosed { 329 | peer_id, 330 | connection_id, 331 | remaining_established, 332 | .. 333 | }) => { 334 | if remaining_established == 0 { 335 | self.connected.remove(&peer_id); 336 | } else if let Some(addrs) = self.connected.get_mut(&peer_id) { 337 | addrs.remove(&connection_id); 338 | } 339 | 340 | self.our_observed_addresses.remove(&connection_id); 341 | } 342 | _ => {} 343 | } 344 | } 345 | } 346 | 347 | /// Event emitted by the `Identify` behaviour. 348 | #[allow(clippy::large_enum_variant)] 349 | #[derive(Debug)] 350 | pub enum Event { 351 | /// Identification information has been received from a peer. 352 | Received { 353 | /// The peer that has been identified. 354 | peer_id: PeerId, 355 | /// The information provided by the peer. 356 | info: TInfo, 357 | }, 358 | /// Identification information of the local node has been sent to a peer in 359 | /// response to an identification request. 360 | Sent { 361 | /// The peer that the information has been sent to. 362 | peer_id: PeerId, 363 | }, 364 | /// Identification information of the local node has been actively pushed to 365 | /// a peer. 366 | Pushed { 367 | /// The peer that the information has been sent to. 368 | peer_id: PeerId, 369 | /// The full Info struct we pushed to the remote peer. Clients must 370 | /// do some diff'ing to know what has changed since the last push. 371 | info: TInfo::Push, 372 | }, 373 | /// Error while attempting to identify the remote. 374 | Error { 375 | /// The peer with whom the error originated. 376 | peer_id: PeerId, 377 | /// The error that occurred. 378 | error: StreamUpgradeError, 379 | }, 380 | } 381 | -------------------------------------------------------------------------------- /blocknet/src/libp2p/peer_info/handler.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Parity Technologies (UK) Ltd. 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a 4 | // copy of this software and associated documentation files (the "Software"), 5 | // to deal in the Software without restriction, including without limitation 6 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | // and/or sell copies of the Software, and to permit persons to whom the 8 | // Software is furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 14 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | // DEALINGS IN THE SOFTWARE. 20 | 21 | use super::{Codec, Info, UpgradeError}; 22 | use either::Either; 23 | use futures::prelude::*; 24 | use futures_bounded::Timeout; 25 | use futures_timer::Delay; 26 | use libp2p::core::upgrade::{ReadyUpgrade, SelectUpgrade}; 27 | use libp2p::core::Multiaddr; 28 | use libp2p::identity::PeerId; 29 | use libp2p::swarm::handler::{ 30 | ConnectionEvent, DialUpgradeError, FullyNegotiatedInbound, FullyNegotiatedOutbound, 31 | }; 32 | use libp2p::swarm::{ 33 | ConnectionHandler, ConnectionHandlerEvent, StreamProtocol, StreamUpgradeError, 34 | SubstreamProtocol, SupportedProtocols, 35 | }; 36 | use smallvec::SmallVec; 37 | use std::collections::HashSet; 38 | use std::{marker::PhantomData, task::Context, task::Poll, time::Duration}; 39 | use tracing::Level; 40 | 41 | const STREAM_TIMEOUT: Duration = Duration::from_secs(60); 42 | const MAX_CONCURRENT_STREAMS_PER_CONNECTION: usize = 10; 43 | 44 | /// Protocol handler for sending and receiving identification requests. 45 | /// 46 | /// Outbound requests are sent periodically. The handler performs expects 47 | /// at least one identification request to be answered by the remote before 48 | /// permitting the underlying connection to be closed. 49 | pub struct Handler> { 50 | remote_peer_id: PeerId, 51 | /// Pending events to yield. 52 | events: SmallVec< 53 | [ConnectionHandlerEvent< 54 | Either, ReadyUpgrade>, 55 | (), 56 | Event, 57 | >; 4], 58 | >, 59 | 60 | active_streams: futures_bounded::FuturesSet, UpgradeError>>, 61 | 62 | /// Future that fires when we need to identify the node again. 63 | trigger_next_identify: Delay, 64 | 65 | /// Whether we have exchanged at least one periodic identify. 66 | exchanged_one_periodic_identify: bool, 67 | 68 | /// The interval of `trigger_next_identify`, i.e. the recurrent delay. 69 | interval: Duration, 70 | 71 | /// Local info. 72 | local_info: TInfo, 73 | 74 | /// Identify information about the remote peer. 75 | remote_info: Option, 76 | 77 | local_supported_protocols: SupportedProtocols, 78 | external_addresses: HashSet, 79 | 80 | protocol_name: StreamProtocol, 81 | push_protocol_name: StreamProtocol, 82 | 83 | _marker: PhantomData<(TInfo, TCodec)>, 84 | } 85 | 86 | /// An event from `Behaviour` with the information requested by the `Handler`. 87 | #[derive(Debug)] 88 | pub enum InEvent { 89 | AddressesChanged(HashSet), 90 | Push, 91 | } 92 | 93 | /// Event produced by the `Handler`. 94 | #[derive(Debug)] 95 | #[allow(clippy::large_enum_variant)] 96 | pub enum Event { 97 | /// We obtained identification information from the remote. 98 | Identified(TInfo), 99 | /// We replied to an identification request from the remote. 100 | Identification, 101 | /// We actively pushed our identification information to the remote. 102 | IdentificationPushed(TInfo::Push), 103 | /// Failed to identify the remote, or to reply to an identification request. 104 | IdentificationError(StreamUpgradeError), 105 | } 106 | 107 | impl> Handler { 108 | /// Creates a new `Handler`. 109 | pub fn new( 110 | interval: Duration, 111 | remote_peer_id: PeerId, 112 | external_addresses: HashSet, 113 | local_info: TInfo, 114 | protocol_name: StreamProtocol, 115 | push_protocol_name: StreamProtocol, 116 | ) -> Self { 117 | Self { 118 | remote_peer_id, 119 | events: SmallVec::new(), 120 | active_streams: futures_bounded::FuturesSet::new( 121 | STREAM_TIMEOUT, 122 | MAX_CONCURRENT_STREAMS_PER_CONNECTION, 123 | ), 124 | trigger_next_identify: Delay::new(Duration::ZERO), 125 | exchanged_one_periodic_identify: false, 126 | interval, 127 | local_supported_protocols: SupportedProtocols::default(), 128 | remote_info: Default::default(), 129 | external_addresses, 130 | local_info, 131 | protocol_name, 132 | push_protocol_name, 133 | _marker: PhantomData, 134 | } 135 | } 136 | 137 | fn on_fully_negotiated_inbound( 138 | &mut self, 139 | FullyNegotiatedInbound { 140 | protocol: output, .. 141 | }: FullyNegotiatedInbound< 142 | ::InboundProtocol, 143 | ::InboundOpenInfo, 144 | >, 145 | ) { 146 | match output { 147 | future::Either::Left(stream) => { 148 | let info = self.local_info.clone(); 149 | 150 | if self 151 | .active_streams 152 | .try_push(TCodec::write_info(stream, info).map_ok(|_| Success::SentIdentify)) 153 | .is_err() 154 | { 155 | tracing::warn!("Dropping inbound stream because we are at capacity"); 156 | } else { 157 | self.exchanged_one_periodic_identify = true; 158 | } 159 | } 160 | future::Either::Right(stream) => { 161 | if self 162 | .active_streams 163 | .try_push(TCodec::read_push_info(stream).map_ok(Success::ReceivedIdentifyPush)) 164 | .is_err() 165 | { 166 | tracing::warn!( 167 | "Dropping inbound identify push stream because we are at capacity" 168 | ); 169 | } 170 | } 171 | } 172 | } 173 | 174 | fn on_fully_negotiated_outbound( 175 | &mut self, 176 | FullyNegotiatedOutbound { 177 | protocol: output, .. 178 | }: FullyNegotiatedOutbound< 179 | ::OutboundProtocol, 180 | ::OutboundOpenInfo, 181 | >, 182 | ) { 183 | match output { 184 | future::Either::Left(stream) => { 185 | if self 186 | .active_streams 187 | .try_push(TCodec::read_info(stream).map_ok(Success::ReceivedIdentify)) 188 | .is_err() 189 | { 190 | tracing::warn!("Dropping outbound identify stream because we are at capacity"); 191 | } 192 | } 193 | future::Either::Right(stream) => { 194 | let info: TInfo::Push = self.local_info.clone().into(); 195 | 196 | if self 197 | .active_streams 198 | .try_push( 199 | TCodec::write_push_info(stream, info.clone()) 200 | .map_ok(|()| Success::SentIdentifyPush(info)), 201 | ) 202 | .is_err() 203 | { 204 | tracing::warn!( 205 | "Dropping outbound identify push stream because we are at capacity" 206 | ); 207 | } 208 | } 209 | } 210 | } 211 | 212 | fn handle_incoming_info(&mut self, info: &TInfo) { 213 | self.remote_info.replace(info.clone()); 214 | } 215 | 216 | fn local_protocols_to_string(&mut self) -> String { 217 | self.local_supported_protocols 218 | .iter() 219 | .map(|p| p.to_string()) 220 | .collect::>() 221 | .join(", ") 222 | } 223 | } 224 | 225 | impl> ConnectionHandler for Handler { 226 | type FromBehaviour = InEvent; 227 | type ToBehaviour = Event; 228 | type InboundProtocol = 229 | SelectUpgrade, ReadyUpgrade>; 230 | type OutboundProtocol = Either, ReadyUpgrade>; 231 | type OutboundOpenInfo = (); 232 | type InboundOpenInfo = (); 233 | 234 | fn listen_protocol(&self) -> SubstreamProtocol { 235 | SubstreamProtocol::new( 236 | SelectUpgrade::new( 237 | ReadyUpgrade::new(self.protocol_name.clone()), 238 | ReadyUpgrade::new(self.push_protocol_name.clone()), 239 | ), 240 | (), 241 | ) 242 | } 243 | 244 | fn on_behaviour_event(&mut self, event: Self::FromBehaviour) { 245 | match event { 246 | InEvent::AddressesChanged(addresses) => { 247 | self.external_addresses = addresses; 248 | } 249 | InEvent::Push => { 250 | self.events 251 | .push(ConnectionHandlerEvent::OutboundSubstreamRequest { 252 | protocol: SubstreamProtocol::new( 253 | Either::Right(ReadyUpgrade::new(self.push_protocol_name.clone())), 254 | (), 255 | ), 256 | }); 257 | } 258 | } 259 | } 260 | 261 | #[tracing::instrument(level = "trace", name = "ConnectionHandler::poll", skip(self, cx))] 262 | fn poll( 263 | &mut self, 264 | cx: &mut Context<'_>, 265 | ) -> Poll>> 266 | { 267 | if let Some(event) = self.events.pop() { 268 | return Poll::Ready(event); 269 | } 270 | 271 | // Poll the future that fires when we need to identify the node again. 272 | if let Poll::Ready(()) = self.trigger_next_identify.poll_unpin(cx) { 273 | self.trigger_next_identify.reset(self.interval); 274 | let event = ConnectionHandlerEvent::OutboundSubstreamRequest { 275 | protocol: SubstreamProtocol::new( 276 | Either::Left(ReadyUpgrade::new(self.protocol_name.clone())), 277 | (), 278 | ), 279 | }; 280 | return Poll::Ready(event); 281 | } 282 | 283 | match self.active_streams.poll_unpin(cx) { 284 | Poll::Ready(Ok(Ok(Success::ReceivedIdentify(remote_info)))) => { 285 | self.handle_incoming_info(&remote_info); 286 | 287 | return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour(Event::Identified( 288 | remote_info, 289 | ))); 290 | } 291 | Poll::Ready(Ok(Ok(Success::SentIdentifyPush(info)))) => { 292 | return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( 293 | Event::IdentificationPushed(info), 294 | )); 295 | } 296 | Poll::Ready(Ok(Ok(Success::SentIdentify))) => { 297 | return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( 298 | Event::Identification, 299 | )); 300 | } 301 | Poll::Ready(Ok(Ok(Success::ReceivedIdentifyPush(remote_push_info)))) => { 302 | if let Some(mut info) = self.remote_info.clone() { 303 | info.merge(remote_push_info); 304 | self.handle_incoming_info(&info); 305 | 306 | return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( 307 | Event::Identified(info), 308 | )); 309 | }; 310 | } 311 | Poll::Ready(Ok(Err(e))) => { 312 | return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( 313 | Event::IdentificationError(StreamUpgradeError::Apply(e)), 314 | )); 315 | } 316 | Poll::Ready(Err(Timeout { .. })) => { 317 | return Poll::Ready(ConnectionHandlerEvent::NotifyBehaviour( 318 | Event::IdentificationError(StreamUpgradeError::Timeout), 319 | )); 320 | } 321 | Poll::Pending => {} 322 | } 323 | 324 | Poll::Pending 325 | } 326 | 327 | fn on_connection_event( 328 | &mut self, 329 | event: ConnectionEvent< 330 | Self::InboundProtocol, 331 | Self::OutboundProtocol, 332 | Self::InboundOpenInfo, 333 | Self::OutboundOpenInfo, 334 | >, 335 | ) { 336 | match event { 337 | ConnectionEvent::FullyNegotiatedInbound(fully_negotiated_inbound) => { 338 | self.on_fully_negotiated_inbound(fully_negotiated_inbound) 339 | } 340 | ConnectionEvent::FullyNegotiatedOutbound(fully_negotiated_outbound) => { 341 | self.on_fully_negotiated_outbound(fully_negotiated_outbound) 342 | } 343 | ConnectionEvent::DialUpgradeError(DialUpgradeError { error, .. }) => { 344 | self.events.push(ConnectionHandlerEvent::NotifyBehaviour( 345 | Event::IdentificationError( 346 | error.map_upgrade_err(|e| void::unreachable(e.into_inner())), 347 | ), 348 | )); 349 | self.trigger_next_identify.reset(self.interval); 350 | } 351 | ConnectionEvent::LocalProtocolsChange(change) => { 352 | let before = tracing::enabled!(Level::DEBUG) 353 | .then(|| self.local_protocols_to_string()) 354 | .unwrap_or_default(); 355 | let protocols_changed = self.local_supported_protocols.on_protocols_change(change); 356 | let after = tracing::enabled!(Level::DEBUG) 357 | .then(|| self.local_protocols_to_string()) 358 | .unwrap_or_default(); 359 | 360 | if protocols_changed && self.exchanged_one_periodic_identify { 361 | tracing::debug!( 362 | peer=%self.remote_peer_id, 363 | %before, 364 | %after, 365 | "Supported listen protocols changed, pushing to peer" 366 | ); 367 | 368 | self.events 369 | .push(ConnectionHandlerEvent::OutboundSubstreamRequest { 370 | protocol: SubstreamProtocol::new( 371 | Either::Right(ReadyUpgrade::new(self.push_protocol_name.clone())), 372 | (), 373 | ), 374 | }); 375 | } 376 | } 377 | _ => {} 378 | } 379 | } 380 | } 381 | 382 | enum Success { 383 | SentIdentify, 384 | ReceivedIdentify(TInfo), 385 | SentIdentifyPush(TInfo::Push), 386 | ReceivedIdentifyPush(TInfo::Push), 387 | } 388 | -------------------------------------------------------------------------------- /blocknet/src/libp2p/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod peer_info; 2 | 3 | use crate::{ 4 | BroadcastService as BroadcastServiceT, Event as EventT, Message as MessageT, 5 | Service as ServiceT, 6 | }; 7 | use futures::{ 8 | channel::mpsc, 9 | select, 10 | sink::SinkExt, 11 | stream::{Stream, StreamExt, TryStreamExt}, 12 | }; 13 | use libp2p::{ 14 | gossipsub, identify, kad, mdns, request_response, 15 | swarm::{NetworkBehaviour, StreamProtocol, Swarm, SwarmEvent}, 16 | }; 17 | use serde::{de::DeserializeOwned, Deserialize, Serialize}; 18 | use std::{ 19 | borrow::Cow, 20 | collections::HashMap, 21 | convert::Infallible, 22 | fmt::Debug, 23 | future::Future, 24 | ops::Deref, 25 | sync::{Arc, RwLock}, 26 | }; 27 | use sync_extra::RwLockExtra; 28 | use thiserror::Error; 29 | use tracing::error; 30 | 31 | const MESSAGE_CHANNEL_BUFFER_SIZE: usize = 16; 32 | const ACTION_CHANNEL_BUFFER_SIZE: usize = 64; 33 | 34 | pub type PeerId = libp2p::PeerId; 35 | pub type ProtocolName = Cow<'static, str>; 36 | 37 | #[derive(Serialize, Deserialize, Debug, Clone)] 38 | pub struct AnyRequest { 39 | pub protocol_id: String, 40 | pub serialized: Vec, 41 | } 42 | 43 | #[derive(Serialize, Deserialize, Debug, Clone)] 44 | pub struct AnyResponse { 45 | pub protocol_id: String, 46 | pub serialized: Vec, 47 | } 48 | 49 | #[derive(Serialize, Deserialize, Debug, Clone)] 50 | pub struct AnyMessage { 51 | pub topic: String, 52 | pub serialized: Vec, 53 | } 54 | 55 | enum ActionItem { 56 | BroadcastSend { 57 | message: AnyMessage, 58 | }, 59 | BroadcastListen { 60 | sender: mpsc::Sender<(PeerId, AnyMessage)>, 61 | topic: String, 62 | }, 63 | 64 | Error(RunError), 65 | } 66 | 67 | #[derive(Debug, Error)] 68 | pub enum FatalRunError {} 69 | 70 | #[derive(Debug, Error)] 71 | pub enum RunError { 72 | #[error("Normal run error")] 73 | Normal(Error), 74 | #[error("Critical error")] 75 | Fatal(#[from] FatalRunError), 76 | } 77 | 78 | impl From for RunError 79 | where 80 | Error: From, 81 | { 82 | fn from(value: T) -> RunError { 83 | RunError::Normal(Error::from(value)) 84 | } 85 | } 86 | 87 | #[derive(Debug, Error)] 88 | pub enum Error { 89 | #[error("Codec error")] 90 | Codec(String), // TODO: Change this to Box. 91 | #[error("Sender channel error")] 92 | ChannelSend(#[from] mpsc::SendError), 93 | #[error("Gossipsub subscription")] 94 | GossipsubSubscription(#[from] gossipsub::SubscriptionError), 95 | #[error("Gossipsub publish")] 96 | GossipsubPublish(#[from] gossipsub::PublishError), 97 | #[error("Noise error")] 98 | Noise(#[from] libp2p::noise::Error), 99 | #[error("Mutladdr error")] 100 | Multiaddr(#[from] libp2p::multiaddr::Error), 101 | #[error("Transport error")] 102 | Transport(#[from] libp2p::TransportError), 103 | #[error("Build error")] 104 | Build(Box), 105 | 106 | #[error("Broadcast message with an unknown source")] 107 | UnknownOriginBroadcast(AnyMessage), 108 | } 109 | 110 | #[derive(NetworkBehaviour)] 111 | struct Behaviour 112 | where 113 | PeerInfo: Debug + Clone + Serialize + DeserializeOwned + Send + 'static, 114 | { 115 | gossipsub: gossipsub::Behaviour, 116 | kademlia: kad::Behaviour, 117 | identify: identify::Behaviour, 118 | peer_info: peer_info::json::Behaviour>, 119 | mdns: mdns::tokio::Behaviour, 120 | request_response: request_response::json::Behaviour, 121 | } 122 | 123 | pub struct Worker 124 | where 125 | PeerInfo: Debug + Clone + Serialize + DeserializeOwned + Send + 'static, 126 | { 127 | swarm: Swarm>, 128 | peers: Arc>>>, 129 | local_info: Arc>>, 130 | broadcast_listen_senders: 131 | HashMap>)>, 132 | action_receiver: mpsc::Receiver, 133 | action_sender: mpsc::Sender, 134 | } 135 | 136 | impl Worker 137 | where 138 | PeerInfo: Debug + Clone + Serialize + DeserializeOwned + Send + 'static, 139 | { 140 | pub fn new(local_info: PeerInfo) -> Result { 141 | let mut swarm = libp2p::SwarmBuilder::with_new_identity() 142 | .with_tokio() 143 | .with_tcp( 144 | libp2p::tcp::Config::default(), 145 | libp2p::noise::Config::new, 146 | libp2p::yamux::Config::default, 147 | )? 148 | .with_quic() 149 | .with_behaviour(|key| { 150 | let peer_id = PeerId::from_public_key(&key.public()); 151 | 152 | let gossipsub = gossipsub::Behaviour::new( 153 | gossipsub::MessageAuthenticity::Signed(key.clone()), 154 | Default::default(), 155 | )?; 156 | 157 | let kademlia = kad::Behaviour::new(peer_id, kad::store::MemoryStore::new(peer_id)); 158 | 159 | let identify = identify::Behaviour::new(identify::Config::new( 160 | "/blocknet/v0.1".to_string(), 161 | key.public(), 162 | )); 163 | 164 | let peer_info = peer_info::json::Behaviour::new( 165 | peer_info::Config::new( 166 | "/blocknet/v0.1".to_string(), 167 | key.public(), 168 | StreamProtocol::new("/blocknet/peer_info/v0.1"), 169 | StreamProtocol::new("/blocknet/peer_info/push/v0.1"), 170 | ), 171 | PeerFullInfo { 172 | info: local_info.clone(), 173 | }, 174 | ); 175 | 176 | let mdns = mdns::Behaviour::new(mdns::Config::default(), peer_id.clone())?; 177 | 178 | let request_response = request_response::Behaviour::new( 179 | // TODO: At this moment we just allow all request/response types to be 180 | // communicated via a single protocol. It's reasonable to expect that 181 | // users will want to restrict this and negioate protocol types in 182 | // advance. In those situations, we will introduce a new `Metadata` type 183 | // that needs to be implemented by all request/response types, and default 184 | // it to `AnyMetadata`. The same is with notifications and broadcasts. 185 | vec![( 186 | StreamProtocol::new("/blocknet/request_response/v0.1"), 187 | request_response::ProtocolSupport::Full, 188 | )], 189 | request_response::Config::default(), 190 | ); 191 | 192 | Ok(Behaviour:: { 193 | gossipsub, 194 | kademlia, 195 | identify, 196 | peer_info, 197 | mdns, 198 | request_response, 199 | }) 200 | }) 201 | .map_err(|e| Error::Build(Box::new(e)))? 202 | .build(); 203 | 204 | swarm.listen_on("/ip4/0.0.0.0/udp/0/quic-v1".parse()?)?; 205 | swarm.listen_on("/ip4/0.0.0.0/tcp/0".parse()?)?; 206 | 207 | let (action_sender, action_receiver) = mpsc::channel(ACTION_CHANNEL_BUFFER_SIZE); 208 | 209 | Ok(Self { 210 | swarm, 211 | peers: Arc::new(RwLock::new(Default::default())), 212 | local_info: Arc::new(RwLock::new(PeerFullInfo { info: local_info })), 213 | broadcast_listen_senders: Default::default(), 214 | action_sender, 215 | action_receiver, 216 | }) 217 | } 218 | 219 | pub fn service(&self) -> Service { 220 | Service { 221 | peers: self.peers.clone(), 222 | local_info: self.local_info.clone(), 223 | action_sender: self.action_sender.clone(), 224 | } 225 | } 226 | 227 | pub async fn run(mut self) -> Result { 228 | loop { 229 | match self.step().await { 230 | Ok(()) => (), 231 | Err(RunError::Normal(e)) => { 232 | error!("Worker run normal error: {:?}", e) 233 | } 234 | Err(RunError::Fatal(e)) => return Err(e), 235 | } 236 | } 237 | } 238 | 239 | pub async fn step(&mut self) -> Result<(), RunError> { 240 | select! { 241 | action = self.action_receiver.select_next_some() => { 242 | match action { 243 | ActionItem::BroadcastSend { 244 | message 245 | } => { 246 | let topic = gossipsub::IdentTopic::new(message.topic); 247 | self.swarm.behaviour_mut().gossipsub 248 | .publish(topic, message.serialized)?; 249 | }, 250 | ActionItem::BroadcastListen { 251 | sender, topic, 252 | } => { 253 | let ident_topic = gossipsub::IdentTopic::new(topic.clone()); 254 | self.swarm.behaviour_mut().gossipsub 255 | .subscribe(&ident_topic)?; 256 | 257 | self.broadcast_listen_senders.entry(ident_topic.hash()) 258 | .or_insert((topic, Vec::new())) 259 | .1 260 | .push(sender); 261 | }, 262 | ActionItem::Error(err) => { 263 | return Err(err.into()) 264 | }, 265 | } 266 | }, 267 | event = self.swarm.select_next_some() => { 268 | match event { 269 | SwarmEvent::Behaviour(BehaviourEvent::Gossipsub(gossipsub::Event::Message { 270 | message, .. 271 | })) => { 272 | if let Some(entry) = self.broadcast_listen_senders.get_mut(&message.topic) { 273 | // TODO: Unsubscribe from topic when the entry becomes empty. 274 | entry.1.retain(|sender| sender.is_closed()); 275 | 276 | let topic = entry.0.clone(); 277 | let any_message = AnyMessage { 278 | topic, 279 | serialized: message.data, 280 | }; 281 | 282 | if let Some(source) = message.source { 283 | for sender in &mut entry.1 { 284 | sender.send((source, any_message.clone())).await?; 285 | } 286 | } else { 287 | return Err(Error::UnknownOriginBroadcast(any_message.clone()).into()) 288 | } 289 | } 290 | }, 291 | _ => (), 292 | } 293 | }, 294 | } 295 | 296 | Ok(()) 297 | } 298 | } 299 | 300 | #[derive(Clone, Debug, Serialize, Deserialize)] 301 | pub struct PeerFullInfo { 302 | info: PeerInfo, 303 | } 304 | 305 | impl peer_info::Info for PeerFullInfo 306 | where 307 | PeerInfo: Debug + Clone + Send + 'static, 308 | { 309 | type Push = Self; 310 | 311 | fn merge(&mut self, push: Self::Push) { 312 | *self = push; 313 | } 314 | } 315 | 316 | #[derive(Debug, Clone)] 317 | pub struct Service { 318 | peers: Arc>>>, 319 | local_info: Arc>>, 320 | action_sender: mpsc::Sender, 321 | } 322 | 323 | impl ServiceT for Service 324 | where 325 | PeerInfo: Clone + Send + Sync + 'static, 326 | { 327 | type PeerId = PeerId; 328 | type PeerInfo = PeerInfo; 329 | type Error = Error; 330 | 331 | fn local_info(&self) -> Self::PeerInfo { 332 | self.local_info.read_unwrap().clone().info 333 | } 334 | 335 | fn set_local_info(&mut self, info: Self::PeerInfo) { 336 | self.local_info.write_unwrap().info = info; 337 | } 338 | 339 | fn peers(&self) -> impl IntoIterator { 340 | self.peers 341 | .read_unwrap() 342 | .clone() 343 | .into_iter() 344 | .map(|(peer_id, info)| (peer_id, info.info)) 345 | } 346 | } 347 | 348 | #[derive(Debug)] 349 | pub struct Event { 350 | origin: PeerId, 351 | value: Value, 352 | } 353 | 354 | impl EventT for Event { 355 | type Origin = PeerId; 356 | type Value = Value; 357 | 358 | fn origin(&self) -> impl Deref { 359 | &self.origin 360 | } 361 | 362 | fn value(&self) -> impl Deref { 363 | &self.value 364 | } 365 | 366 | fn into_value(self) -> Value { 367 | self.value 368 | } 369 | } 370 | 371 | impl BroadcastServiceT for Service 372 | where 373 | PeerExtraInfo: Clone + Send + Sync + 'static, 374 | Msg: MessageT + Send + Clone + Serialize + DeserializeOwned + 'static, 375 | Msg::Topic: Send + Into + 'static, 376 | { 377 | type Event = Event; 378 | 379 | fn listen( 380 | &mut self, 381 | topic: Msg::Topic, 382 | ) -> impl Future + Send, Self::Error>> + Send 383 | { 384 | let (sender, receiver) = mpsc::channel(MESSAGE_CHANNEL_BUFFER_SIZE); 385 | 386 | async move { 387 | self.action_sender 388 | .send(ActionItem::BroadcastListen { 389 | topic: topic.into(), 390 | sender, 391 | }) 392 | .await?; 393 | 394 | let action_sender = self.action_sender.clone(); 395 | 396 | Ok(receiver 397 | .map(|v| Ok(v)) 398 | .and_then(|(origin, msg)| async move { 399 | Ok(Event { 400 | origin, 401 | value: serde_json::from_slice(&msg.serialized) 402 | .map_err(|e| Error::Codec(format!("{:?}", e)))?, 403 | }) 404 | }) 405 | .scan((), move |(), v| { 406 | let mut action_sender = action_sender.clone(); 407 | async move { 408 | match v { 409 | Ok(v) => Some(Ok(v)), 410 | Err(e) => match action_sender.send(ActionItem::Error(e)).await { 411 | Ok(()) => Some(Err(())), 412 | Err(e) => { 413 | tracing::info!( 414 | "Communicate with the worker service failed: {:?}", 415 | e 416 | ); 417 | 418 | // The action sender stops working. We close the stream. 419 | None 420 | } 421 | }, 422 | } 423 | } 424 | }) 425 | .filter_map(|v| async move { 426 | match v { 427 | Ok(v) => Some(v), 428 | Err(()) => None, 429 | } 430 | })) 431 | } 432 | } 433 | 434 | fn broadcast(&mut self, message: Msg) -> impl Future> + Send { 435 | async move { 436 | let item = ActionItem::BroadcastSend { 437 | message: AnyMessage { 438 | topic: message.topic().into(), 439 | serialized: serde_json::to_vec(&message) 440 | .map_err(|e| Error::Codec(format!("{:?}", e)))?, 441 | }, 442 | }; 443 | 444 | self.action_sender.send(item).await?; 445 | Ok(()) 446 | } 447 | } 448 | } 449 | --------------------------------------------------------------------------------