Implement multiplexer and mini-protocols PoC
This commit is contained in:
parent
89edee6793
commit
c2d537b83f
28 changed files with 1398 additions and 0 deletions
2
pallas-blockfetch/.gitignore
vendored
Normal file
2
pallas-blockfetch/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
Cargo.lock
|
||||
24
pallas-blockfetch/Cargo.toml
Normal file
24
pallas-blockfetch/Cargo.toml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "pallas-blockfetch"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/txpipe/pallas"
|
||||
license = "Apache-2.0"
|
||||
authors = [
|
||||
"Santiago Carmuega <santiago@carmuega.me>"
|
||||
]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
pallas-multiplexer = { path = "../pallas-multiplexer/" }
|
||||
pallas-machines = { path = "../pallas-machines/" }
|
||||
minicbor = { version="0.11.4", features=["half"] }
|
||||
minicbor-io = "0.6.0"
|
||||
log = "0.4.14"
|
||||
|
||||
[dev-dependencies]
|
||||
net2 = "0.2.37"
|
||||
env_logger = "0.9.0"
|
||||
pallas-handshake = { path = "../pallas-handshake/" }
|
||||
hex = "0.4.3"
|
||||
47
pallas-blockfetch/examples/client.rs
Normal file
47
pallas-blockfetch/examples/client.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use net2::TcpStreamExt;
|
||||
use std::net::TcpStream;
|
||||
|
||||
use pallas_blockfetch::{BlockFetchClient, Point};
|
||||
use pallas_handshake::n2n::{Client, VersionTable};
|
||||
use pallas_handshake::MAINNET_MAGIC;
|
||||
use pallas_machines::run_agent;
|
||||
use pallas_multiplexer::Multiplexer;
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
//let bearer = TcpStream::connect("localhost:6000").unwrap();
|
||||
let bearer =
|
||||
TcpStream::connect("relays-new.cardano-mainnet.iohk.io:3001").unwrap();
|
||||
|
||||
bearer.set_nodelay(true).unwrap();
|
||||
bearer.set_keepalive_ms(Some(30_000u32)).unwrap();
|
||||
|
||||
let mut handles = Multiplexer::new(bearer, &vec![0, 3]).unwrap();
|
||||
let (_, rx, tx) = handles.remove(0);
|
||||
|
||||
let versions = VersionTable::v4_and_above(MAINNET_MAGIC);
|
||||
let last = run_agent(Client::initial(versions), rx, &tx).unwrap();
|
||||
println!("{:?}", last);
|
||||
|
||||
let range = (
|
||||
Point(
|
||||
43847831u64,
|
||||
hex::decode("15b9eeee849dd6386d3770b0745e0450190f7560e5159b1b3ab13b14b2684a45")
|
||||
.unwrap(),
|
||||
),
|
||||
Point(
|
||||
43847831u64,
|
||||
hex::decode("15b9eeee849dd6386d3770b0745e0450190f7560e5159b1b3ab13b14b2684a45")
|
||||
.unwrap(),
|
||||
),
|
||||
);
|
||||
|
||||
let (_, bf_rx, bf_tx) = handles.remove(0);
|
||||
|
||||
let bf = BlockFetchClient::initial(range);
|
||||
|
||||
let bf_last = run_agent(bf, bf_rx, &bf_tx);
|
||||
|
||||
println!("{:?}", bf_last);
|
||||
}
|
||||
190
pallas-blockfetch/src/lib.rs
Normal file
190
pallas-blockfetch/src/lib.rs
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
use log::info;
|
||||
use pallas_machines::{
|
||||
Agent, DecodePayload, EncodePayload, MachineError, MachineOutput,
|
||||
PayloadDecoder, PayloadEncoder, Transition,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Point(pub u64, pub Vec<u8>);
|
||||
|
||||
impl EncodePayload for Point {
|
||||
fn encode_payload(
|
||||
&self,
|
||||
e: &mut PayloadEncoder,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
e.array(2)?.u64(self.0)?.bytes(&self.1)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DecodePayload for Point {
|
||||
fn decode_payload(
|
||||
d: &mut PayloadDecoder,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
d.array()?;
|
||||
let slot = d.u64()?;
|
||||
let hash = d.bytes()?;
|
||||
|
||||
Ok(Point(slot, Vec::from(hash)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub enum State {
|
||||
Idle,
|
||||
Busy,
|
||||
Streaming,
|
||||
Done,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
RequestRange { range: (Point, Point) },
|
||||
ClientDone,
|
||||
StartBatch,
|
||||
NoBlocks,
|
||||
Block { body: Vec<u8> },
|
||||
BatchDone,
|
||||
}
|
||||
|
||||
impl EncodePayload for Message {
|
||||
fn encode_payload(
|
||||
&self,
|
||||
e: &mut PayloadEncoder,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
match self {
|
||||
Message::RequestRange { range } => {
|
||||
e.array(3)?.u16(0)?;
|
||||
range.0.encode_payload(e)?;
|
||||
range.1.encode_payload(e)?;
|
||||
Ok(())
|
||||
}
|
||||
Message::ClientDone => {
|
||||
e.array(1)?.u16(1)?;
|
||||
Ok(())
|
||||
}
|
||||
Message::StartBatch => {
|
||||
e.array(1)?.u16(2)?;
|
||||
Ok(())
|
||||
}
|
||||
Message::NoBlocks => {
|
||||
e.array(1)?.u16(3)?;
|
||||
Ok(())
|
||||
}
|
||||
Message::Block { body } => {
|
||||
e.array(2)?.u16(4)?;
|
||||
e.bytes(&body)?;
|
||||
Ok(())
|
||||
}
|
||||
Message::BatchDone => {
|
||||
e.array(1)?.u16(5)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DecodePayload for Message {
|
||||
fn decode_payload(
|
||||
d: &mut PayloadDecoder,
|
||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||
d.array()?;
|
||||
let label = d.u16()?;
|
||||
|
||||
match label {
|
||||
0 => {
|
||||
let point1 = Point::decode_payload(d)?;
|
||||
let point2 = Point::decode_payload(d)?;
|
||||
Ok(Message::RequestRange {
|
||||
range: (point1, point2),
|
||||
})
|
||||
}
|
||||
1 => Ok(Message::ClientDone),
|
||||
2 => Ok(Message::StartBatch),
|
||||
3 => Ok(Message::NoBlocks),
|
||||
4 => {
|
||||
d.tag()?;
|
||||
let body = d.bytes()?;
|
||||
Ok(Message::Block {
|
||||
body: Vec::from(body),
|
||||
})
|
||||
}
|
||||
5 => Ok(Message::BatchDone),
|
||||
x => Err(Box::new(MachineError::BadLabel(x))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BlockFetchClient {
|
||||
pub state: State,
|
||||
pub range: (Point, Point),
|
||||
}
|
||||
|
||||
impl BlockFetchClient {
|
||||
pub fn initial(range: (Point, Point)) -> Self {
|
||||
Self {
|
||||
state: State::Idle,
|
||||
range,
|
||||
}
|
||||
}
|
||||
|
||||
fn send_request_range(self, tx: &impl MachineOutput) -> Transition<Self> {
|
||||
let msg = Message::RequestRange {
|
||||
range: self.range.clone(),
|
||||
};
|
||||
|
||||
tx.send_msg(&msg)?;
|
||||
|
||||
Ok(Self {
|
||||
state: State::Busy,
|
||||
..self
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Agent for BlockFetchClient {
|
||||
type Message = Message;
|
||||
|
||||
fn is_done(&self) -> bool {
|
||||
self.state == State::Done
|
||||
}
|
||||
|
||||
fn has_agency(&self) -> bool {
|
||||
match self.state {
|
||||
State::Idle => true,
|
||||
State::Busy => false,
|
||||
State::Streaming => false,
|
||||
State::Done => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn send_next(self, tx: &impl MachineOutput) -> Transition<Self> {
|
||||
match self.state {
|
||||
State::Idle => self.send_request_range(tx),
|
||||
_ => panic!("I don't have agency, don't know what to do"),
|
||||
}
|
||||
}
|
||||
|
||||
fn receive_next(self, msg: Self::Message) -> Transition<Self> {
|
||||
match (&self.state, msg) {
|
||||
(State::Busy, Message::StartBatch) => Ok(Self {
|
||||
state: State::Streaming,
|
||||
..self
|
||||
}),
|
||||
(State::Busy, Message::NoBlocks) => Ok(Self {
|
||||
state: State::Done,
|
||||
..self
|
||||
}),
|
||||
(State::Streaming, Message::Block { body }) => {
|
||||
info!("received block body of size {}", body.len());
|
||||
Ok(self)
|
||||
}
|
||||
(State::Streaming, Message::BatchDone) => Ok(Self {
|
||||
state: State::Done,
|
||||
..self
|
||||
}),
|
||||
_ => panic!("I have agency, I don't expect messages"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue