2023-03-07 23:36:48 +00:00
|
|
|
// Rust Levin Library
|
|
|
|
// Written in 2023 by
|
|
|
|
// Cuprate Contributors
|
|
|
|
//
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
//
|
|
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
|
|
// copies or substantial portions of the Software.
|
|
|
|
//
|
|
|
|
|
2023-03-07 22:37:55 +00:00
|
|
|
//! # Rust Levin
|
|
|
|
//!
|
|
|
|
//! A crate for working with the Levin protocol in Rust.
|
|
|
|
//!
|
|
|
|
//! The Levin protocol is a network protocol used in the Monero cryptocurrency. It is used for
|
|
|
|
//! peer-to-peer communication between nodes. This crate provides a Rust implementation of the Levin
|
|
|
|
//! header serialization and allows developers to define their own bucket bodies so this is not a
|
|
|
|
//! complete Monero networking crate.
|
|
|
|
//!
|
|
|
|
//! ## License
|
|
|
|
//!
|
|
|
|
//! This project is licensed under the MIT License.
|
|
|
|
|
|
|
|
// Coding conventions
|
|
|
|
#![forbid(unsafe_code)]
|
|
|
|
#![deny(non_upper_case_globals)]
|
|
|
|
#![deny(non_camel_case_types)]
|
|
|
|
#![deny(unused_mut)]
|
|
|
|
#![deny(missing_docs)]
|
|
|
|
|
|
|
|
pub mod bucket_sink;
|
|
|
|
pub mod bucket_stream;
|
|
|
|
pub mod header;
|
|
|
|
pub mod message_sink;
|
|
|
|
pub mod message_stream;
|
|
|
|
|
|
|
|
pub use header::BucketHead;
|
|
|
|
|
|
|
|
use std::fmt::Debug;
|
|
|
|
|
|
|
|
use bytes::Bytes;
|
|
|
|
use thiserror::Error;
|
|
|
|
|
|
|
|
/// Possible Errors when working with levin buckets
|
|
|
|
#[derive(Error, Debug)]
|
|
|
|
pub enum BucketError {
|
|
|
|
/// Unsupported p2p command.
|
|
|
|
#[error("Unsupported p2p command: {0}")]
|
|
|
|
UnsupportedP2pCommand(u32),
|
|
|
|
/// Revived header with incorrect signature.
|
|
|
|
#[error("Revived header with incorrect signature: {0}")]
|
|
|
|
IncorrectSignature(u64),
|
|
|
|
/// Header contains unknown flags.
|
|
|
|
#[error("Header contains unknown flags")]
|
|
|
|
UnknownFlags,
|
|
|
|
/// Revived header with unknown protocol version.
|
|
|
|
#[error("Revived header with unknown protocol version: {0}")]
|
|
|
|
UnknownProtocolVersion(u32),
|
|
|
|
/// More bytes needed to parse data.
|
|
|
|
#[error("More bytes needed to parse data")]
|
|
|
|
NotEnoughBytes,
|
|
|
|
/// Failed to decode bucket body.
|
|
|
|
#[error("Failed to decode bucket body: {0}")]
|
|
|
|
FailedToDecodeBucketBody(String),
|
|
|
|
/// Failed to encode bucket body.
|
|
|
|
#[error("Failed to encode bucket body: {0}")]
|
|
|
|
FailedToEncodeBucketBody(String),
|
|
|
|
/// IO Error.
|
|
|
|
#[error("IO Error: {0}")]
|
|
|
|
IO(#[from] std::io::Error),
|
|
|
|
/// Peer sent an error response code.
|
|
|
|
#[error("Peer sent an error response code: {0}")]
|
|
|
|
Error(i32),
|
|
|
|
}
|
|
|
|
|
|
|
|
const PROTOCOL_VERSION: u32 = 1;
|
|
|
|
const LEVIN_SIGNATURE: u64 = 0x0101010101012101;
|
|
|
|
|
|
|
|
/// A levin Bucket
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Bucket {
|
|
|
|
header: BucketHead,
|
|
|
|
body: Bytes,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Bucket {
|
|
|
|
fn to_bytes(&self) -> Bytes {
|
|
|
|
let mut buf = self.header.to_bytes();
|
|
|
|
buf.extend(self.body.iter());
|
|
|
|
buf.into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An enum representing if the message is a request or response
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum MessageType {
|
|
|
|
/// Request
|
|
|
|
Request,
|
|
|
|
/// Response
|
|
|
|
Response,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<MessageType> for header::Flags {
|
|
|
|
fn from(val: MessageType) -> Self {
|
|
|
|
match val {
|
|
|
|
MessageType::Request => header::REQUEST,
|
|
|
|
MessageType::Response => header::RESPONSE,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryInto<MessageType> for header::Flags {
|
|
|
|
type Error = BucketError;
|
|
|
|
fn try_into(self) -> Result<MessageType, Self::Error> {
|
|
|
|
if self.is_request() {
|
|
|
|
Ok(MessageType::Request)
|
|
|
|
} else if self.is_response() {
|
|
|
|
Ok(MessageType::Response)
|
|
|
|
} else {
|
|
|
|
Err(BucketError::UnknownFlags)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A levin body
|
|
|
|
pub trait LevinBody: Sized {
|
|
|
|
/// Decodes the message from the data in the header
|
initial database code (#6)
* commit to start the draft pull request.
added a space
* Please don't look to close.It might hurt your eyes
* impl associated types
* errors, docs & divided ro/rw tx
Added some more errors to DB_FAILURES, rewrited crates docs, and specified
WriteTransaction subtype which implement write mode method.
* more changes see description
changed blockchain_db folder by database. Implemented (just for test) get_block_hash, open, from to Interface.
Also rewrited a declarative macro for tables. Will have to add Dummy Tables later.
* small changes
* Organized modules & implemented get_block_hash
* write prototype & error
Added prototype functions for clear(), put() & delete() in mdbx implementation. They still don't
consider table flags. Also added a temporary DB_FAILURES::EncodingError for monero-rs consensus_encode
errors. Still have to rethink about it to resend a reference to the data that can't be encoded.
* Multiple changes
- hse.rs
Added hse.rs that will contain db implementations for HSE. Since the codebase can't welcome unsafe
code, the wrapper will be written outside of the project.
- lib.rs
Added a specific FailedToCommit error. (will investigate if really necessary).
Added DupTable trait, which is a Table with DUPSORT/DUPFIXED support and its declarative macro.
Added two other tables, blockheaders that give block's header with specified hash & blockbody that give block's body with specified hash
Added Cursor methods, that are likely to be deprecated if I found a way to implemen Iterator on top of it.
Added WriteCursor trait & methods, which is basically put & del.
Added mandatory type for Cursors in Transaction & WriteTransactions
Refactored get_block_hash interface method.
- mdbx.rs
Added partial implementation of Cursor & WriteCursor trait for libmdbx::Cursor. Only the first() & get() methods are implemented
Added implementation of get & commit for Transaction
* put mdbx as features with its dependency
* save
* refactored some method with macros
* more mdbx errors, docs correction, moved to error.rs
* finish nodup mdbx impl, errors.rs, macros, tables
Finished the initial implementation of Cursor, WriteCursor, Transaction and WriteTransaction in mdbx.rs. Corrected some macros in mdbx.rs to simplify the implementations. There is certainly rooms to more flexible macros. Also added 3 other tables. I started to divide errors into category to more easily handle them at higher-level. Due to the large number of errors i just moved them into another file. There is know DB_SERIAL enum for errors relating of decoding/encoding error & DB_FULL enum for every errors relating a component being overeaching its capacity.
* bye bye match statement in mdbx.rs
* defined all blockchain tables (not txpool)
* dupsort/fixed support, dupcursor, basic block interface
* tables, types, encoding and documentations
Redefined all the database types from @Boog900's monero-rs db branch and added the needed
implementations. The database now use bincode2 for encoding and decoding. We observe that bincode was
5 times faster at serializing than monero::consensus_encode. Since we still use monero-rs types but can't implement
foreign trait to them, the encoding module contain a compatibility layer, the time we switch from monero-rs to properly
implement it. All the tables are now defined. (can be subject to change if there is good reason for). added documentations
to modules and types.
* replaced macros and added hfversion table
* save
* multiple changes
* modified database schema. deprecated output global index and splited up pre-rct from rct output.
* Fixed DupCursor function to return subkey (thx to rust turbofish inference).
* Added some output functions
* Added two new DB_FAILURES, one to handle a prohibited None case and one for undefined case where a dev msg is needed.
* fixed TxOutputIdx, previously used global index, now is a tuple of amount/amount_index.
* i hate lifetimes
* read-only method now use read-only tx
* initial output fn
* some tx functions. Yes I'll refactor them
* moved interface in a module
* redefined errors, more tx fn, None->error
* corrected a table + started blk fns
* save
* fixed TxOutputIdx + pop_block
* IIRC I finished initial interface fns
* fixed table name const + db build/check/open fn
* switched important tables to dummy keys + rm blockhfversion
* minor docs correction
* fixed mentioned issues
* make a test bin, just for fun
* fixed issues + cargo fmt
* removed monerod part
* fixed a comment
2023-04-20 17:20:32 +00:00
|
|
|
fn decode_message(
|
|
|
|
buf: &[u8],
|
|
|
|
typ: MessageType,
|
|
|
|
have_to_return: bool,
|
|
|
|
command: u32,
|
|
|
|
) -> Result<Self, BucketError>;
|
2023-03-07 22:37:55 +00:00
|
|
|
|
|
|
|
/// Encodes the message
|
|
|
|
///
|
|
|
|
/// returns:
|
|
|
|
/// return_code: i32,
|
|
|
|
/// command: u32,
|
|
|
|
/// have_to_return: bool,
|
|
|
|
/// message_type: MessageType
|
|
|
|
/// bytes: Bytes
|
|
|
|
fn encode(&self) -> Result<(i32, u32, bool, MessageType, Bytes), BucketError>;
|
|
|
|
}
|