feat: Add constants for known miniprotocols

* Add constants for known miniprotocols

Now consumers of the crate don't have to memorize what channel number means what

* Add myself to the crate authors
This commit is contained in:
Pi Lanningham 2023-02-05 06:13:55 -05:00 committed by GitHub
parent f795948d2f
commit 4915d14cd5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 122 additions and 79 deletions

View file

@ -2,7 +2,7 @@ use pallas::network::{
miniprotocols::{
blockfetch,
handshake::{self, n2n::VersionTable},
Point, TESTNET_MAGIC,
Point, PROTOCOL_N2N_BLOCK_FETCH, PROTOCOL_N2N_HANDSHAKE, TESTNET_MAGIC,
},
multiplexer::{bearers::Bearer, StdPlexer},
};
@ -13,14 +13,14 @@ fn main() {
let bearer = Bearer::connect_tcp("relays-new.cardano-testnet.iohkdev.io:3001").unwrap();
let mut plexer = StdPlexer::new(bearer);
let channel0 = plexer.use_channel(0);
let channel3 = plexer.use_channel(3);
let handshake = plexer.use_channel(PROTOCOL_N2N_HANDSHAKE);
let blockfetch = plexer.use_channel(PROTOCOL_N2N_BLOCK_FETCH);
plexer.muxer.spawn();
plexer.demuxer.spawn();
let versions = VersionTable::v4_and_above(TESTNET_MAGIC);
let mut hs_client = handshake::N2NClient::new(channel0);
let mut hs_client = handshake::N2NClient::new(handshake);
let handshake = hs_client.handshake(versions).unwrap();
assert!(matches!(handshake, handshake::Confirmation::Accepted(..)));
@ -30,7 +30,7 @@ fn main() {
hex::decode("3f3d81c7b88f0fa28867541c5fea8794125cccf6d6c9ee0037a1dbb064130dfd").unwrap(),
);
let mut bf_client = blockfetch::Client::new(channel3);
let mut bf_client = blockfetch::Client::new(blockfetch);
let block = bf_client.fetch_single(point).unwrap();

View file

@ -1,5 +1,8 @@
use pallas::network::{
miniprotocols::{chainsync, handshake, localstate, Point, MAINNET_MAGIC},
miniprotocols::{
chainsync, handshake, localstate, Point, MAINNET_MAGIC, PROTOCOL_N2C_CHAIN_SYNC,
PROTOCOL_N2C_HANDSHAKE, PROTOCOL_N2C_STATE_QUERY,
},
multiplexer::{self, bearers::Bearer},
};
@ -75,19 +78,19 @@ fn main() {
// setup the multiplexer by specifying the bearer and the IDs of the
// miniprotocols to use
let mut plexer = multiplexer::StdPlexer::new(bearer);
let channel0 = plexer.use_channel(0);
let channel7 = plexer.use_channel(7);
let channel5 = plexer.use_channel(5);
let handshake = plexer.use_channel(PROTOCOL_N2C_HANDSHAKE);
let statequery = plexer.use_channel(PROTOCOL_N2C_STATE_QUERY);
let chainsync = plexer.use_channel(PROTOCOL_N2C_CHAIN_SYNC);
plexer.muxer.spawn();
plexer.demuxer.spawn();
// execute the required handshake against the relay
do_handshake(channel0);
do_handshake(handshake);
// execute an arbitrary "Local State" query against the node
do_localstate_query(channel7);
do_localstate_query(statequery);
// execute the chainsync flow from an arbitrary point in the chain
do_chainsync(channel5);
do_chainsync(chainsync);
}

View file

@ -1,5 +1,8 @@
use pallas::network::{
miniprotocols::{blockfetch, chainsync, handshake, Point, MAINNET_MAGIC},
miniprotocols::{
blockfetch, chainsync, handshake, Point, MAINNET_MAGIC, PROTOCOL_N2N_BLOCK_FETCH,
PROTOCOL_N2N_CHAIN_SYNC, PROTOCOL_N2N_HANDSHAKE,
},
multiplexer::{bearers::Bearer, StdChannel, StdPlexer},
};
@ -83,19 +86,19 @@ fn main() {
// setup the multiplexer by specifying the bearer and the IDs of the
// miniprotocols to use
let mut plexer = StdPlexer::new(bearer);
let channel0 = plexer.use_channel(0);
let channel3 = plexer.use_channel(3);
let channel2 = plexer.use_channel(2);
let handshake = plexer.use_channel(PROTOCOL_N2N_HANDSHAKE);
let blockfetch = plexer.use_channel(PROTOCOL_N2N_BLOCK_FETCH);
let chainsync = plexer.use_channel(PROTOCOL_N2N_CHAIN_SYNC);
plexer.muxer.spawn();
plexer.demuxer.spawn();
// execute the required handshake against the relay
do_handshake(channel0);
do_handshake(handshake);
// fetch an arbitrary batch of block
do_blockfetch(channel3);
do_blockfetch(blockfetch);
// execute the chainsync flow from an arbitrary point in the chain
do_chainsync(channel2);
do_chainsync(chainsync);
}

View file

@ -7,7 +7,6 @@
//!
//! However, only the [`SecretKeyExtended`] can be used for HD derivation
//! (using [ed25519_bip32] or otherwise).
//!
use crate::memsec::Scrubbed as _;
use cryptoxide::ed25519::{
@ -18,13 +17,13 @@ use std::{any::type_name, convert::TryFrom, fmt, str::FromStr};
use thiserror::Error;
/// Ed25519 Secret Key
///
#[derive(Clone)]
pub struct SecretKey([u8; Self::SIZE]);
/// Ed25519 Extended Secret Key
///
/// unlike [`SecretKey`], an extended key can be derived see [`pallas_crypto::derivation`]
/// unlike [`SecretKey`], an extended key can be derived see
/// [`pallas_crypto::derivation`]
#[derive(Clone)]
pub struct SecretKeyExtended([u8; Self::SIZE]);
@ -77,7 +76,6 @@ impl_size_zero!(Signature, SIGNATURE_LENGTH);
impl SecretKey {
/// generate a new [`SecretKey`] with the given random number generator
///
pub fn new<Rng>(mut rng: Rng) -> Self
where
Rng: RngCore + CryptoRng,
@ -106,8 +104,8 @@ impl SecretKey {
/// create a [`Signature`] for the given message with this [`SecretKey`].
///
/// The [`Signature`] can then be verified against the associated [`PublicKey`]
/// and the original message.
/// The [`Signature`] can then be verified against the associated
/// [`PublicKey`] and the original message.
pub fn sign<T>(&self, msg: T) -> Signature
where
T: AsRef<[u8]>,
@ -125,8 +123,8 @@ impl SecretKey {
}
impl SecretKeyExtended {
/// generate a new [`SecretKeyExtended`] with the given random number generator
///
/// generate a new [`SecretKeyExtended`] with the given random number
/// generator
pub fn new<Rng>(mut rng: Rng) -> Self
where
Rng: RngCore + CryptoRng,
@ -180,7 +178,6 @@ impl SecretKeyExtended {
impl PublicKey {
/// verify the cryptographic [`Signature`] against the `message` and the
/// [`PublicKey`] `self`.
///
#[inline]
pub fn verify<T>(&self, message: T, signature: &Signature) -> bool
where
@ -438,11 +435,12 @@ mod tests {
signature: Signature,
message: Vec<u8>,
) -> bool {
// NOTE: this test may fail but it is impossible to see this happening in normal condition.
// we are generating 32 random bytes of public key and 64 random bytes
// of signature with an randomly generated message of a random number
// of bytes in. If the message were empty, the probability to have
// a signature that matches the verify key would still be 1 out of 2^96.
// NOTE: this test may fail but it is impossible to see this happening in normal
// condition. we are generating 32 random bytes of public key and
// 64 random bytes of signature with an randomly generated message
// of a random number of bytes in. If the message were empty, the
// probability to have a signature that matches the verify key
// would still be 1 out of 2^96.
//
// if this test fails and it is not a bug, go buy a lottery ticket.
!public_key.verify(message, &signature)

View file

@ -8,7 +8,10 @@ homepage = "https://github.com/txpipe/pallas"
documentation = "https://docs.rs/pallas-machines"
license = "Apache-2.0"
readme = "README.md"
authors = ["Santiago Carmuega <santiago@carmuega.me>"]
authors = [
"Santiago Carmuega <santiago@carmuega.me>",
"Pi Lanningham <pi.lanningham@gmail.com>"
]
[dependencies]
pallas-codec = { version = "0.18.0", path = "../pallas-codec/" }

View file

@ -71,7 +71,7 @@ bearer.set_keepalive_ms(Some(30_000u32)).unwrap();
let mut muxer = Multiplexer::setup(bearer, &[0]).unwrap();
// get a handle for the handhsake mini-protocol handle
let mut channel = muxer.use_channel(0);
let mut channel = muxer.use_channel(pallas_miniprotocols::PROTOCOL_N2N_HANDSHAKE);
// create a handshake client agent with an initial state
let agent = handshake::Client::initial(VersionTable::v4_and_above(MAINNET_MAGIC));

View file

@ -14,6 +14,33 @@ pub const PREVIEW_MAGIC: u64 = 2;
/// Well-known magic for pre-production
pub const PRE_PRODUCTION_MAGIC: u64 = 1;
/// Protocol channel number for node-to-node handshakes
pub const PROTOCOL_N2N_HANDSHAKE: u16 = 0;
/// Protocol channel number for node-to-node chain-sync
pub const PROTOCOL_N2N_CHAIN_SYNC: u16 = 2;
/// Protocol channel number for node-to-node block-fetch
pub const PROTOCOL_N2N_BLOCK_FETCH: u16 = 3;
/// Protocol channel number for node-to-node tx-submission
pub const PROTOCOL_N2N_TX_SUBMISSION: u16 = 4;
/// Protocol channel number for node-to-node Keep-alive
pub const PROTOCOL_N2N_KEEP_ALIVE: u16 = 8;
/// Protocol channel number for node-to-client handshakes
pub const PROTOCOL_N2C_HANDSHAKE: u16 = 0;
/// Protocol channel number for node-to-client chain-sync
pub const PROTOCOL_N2C_CHAIN_SYNC: u16 = 5;
/// Protocol channel number for node-to-client tx-submission
pub const PROTOCOL_N2C_TX_SUBMISSION: u16 = 6;
// Protocol channel number for node-to-client state queries
pub const PROTOCOL_N2C_STATE_QUERY: u16 = 7;
/// A point within a chain
#[derive(Clone, Eq, PartialEq, Hash)]
pub enum Point {

View file

@ -31,7 +31,8 @@ where
self.0 == State::Done
}
// NOTE(pi): as of this writing, the network spec has a typo; this is the correct behavior
// NOTE(pi): as of this writing, the network spec has a typo; this is the
// correct behavior
fn has_agency(&self) -> bool {
!matches!(self.state(), State::Idle)
}

View file

@ -31,7 +31,8 @@ where
self.0 == State::Done
}
// NOTE(pi): as of this writing, the network spec has a typo; this is the correct behavior
// NOTE(pi): as of this writing, the network spec has a typo; this is the
// correct behavior
fn has_agency(&self) -> bool {
matches!(self.state(), State::Idle)
}

View file

@ -3,29 +3,30 @@ use pallas_miniprotocols::{
chainsync::{self, NextResponse},
handshake::{self, Confirmation},
txsubmission::{self, Reply},
Point,
Point, PROTOCOL_N2N_BLOCK_FETCH, PROTOCOL_N2N_CHAIN_SYNC, PROTOCOL_N2N_HANDSHAKE,
PROTOCOL_N2N_TX_SUBMISSION,
};
use pallas_multiplexer::{bearers::Bearer, StdChannel, StdPlexer};
struct N2NChannels {
channel2: StdChannel,
channel3: StdChannel,
channel4: StdChannel,
chainsync: StdChannel,
blockfetch: StdChannel,
txsubmission: StdChannel,
}
fn setup_n2n_client_connection() -> N2NChannels {
let bearer = Bearer::connect_tcp("preview-node.world.dev.cardano.org:30002").unwrap();
let mut plexer = StdPlexer::new(bearer);
let channel0 = plexer.use_channel(0);
let channel2 = plexer.use_channel(2);
let channel3 = plexer.use_channel(3);
let channel4 = plexer.use_channel(4);
let handshake = plexer.use_channel(PROTOCOL_N2N_HANDSHAKE);
let chainsync = plexer.use_channel(PROTOCOL_N2N_CHAIN_SYNC);
let blockfetch = plexer.use_channel(PROTOCOL_N2N_BLOCK_FETCH);
let txsubmission = plexer.use_channel(PROTOCOL_N2N_TX_SUBMISSION);
plexer.muxer.spawn();
plexer.demuxer.spawn();
let mut client = handshake::N2NClient::new(channel0);
let mut client = handshake::N2NClient::new(handshake);
let confirmation = client
.handshake(handshake::n2n::VersionTable::v7_and_above(2))
@ -38,23 +39,23 @@ fn setup_n2n_client_connection() -> N2NChannels {
}
N2NChannels {
channel2,
channel3,
channel4,
chainsync,
blockfetch,
txsubmission,
}
}
#[test]
#[ignore]
pub fn chainsync_history_happy_path() {
let N2NChannels { channel2, .. } = setup_n2n_client_connection();
let N2NChannels { chainsync, .. } = setup_n2n_client_connection();
let known_point = Point::Specific(
1654413,
hex::decode("7de1f036df5a133ce68a82877d14354d0ba6de7625ab918e75f3e2ecb29771c2").unwrap(),
);
let mut client = chainsync::N2NClient::new(channel2);
let mut client = chainsync::N2NClient::new(chainsync);
let (point, _) = client.find_intersect(vec![known_point.clone()]).unwrap();
@ -93,9 +94,9 @@ pub fn chainsync_history_happy_path() {
#[test]
#[ignore]
pub fn chainsync_tip_happy_path() {
let N2NChannels { channel2, .. } = setup_n2n_client_connection();
let N2NChannels { chainsync, .. } = setup_n2n_client_connection();
let mut client = chainsync::N2NClient::new(channel2);
let mut client = chainsync::N2NClient::new(chainsync);
client.intersect_tip().unwrap();
@ -132,14 +133,14 @@ pub fn chainsync_tip_happy_path() {
#[test]
#[ignore]
pub fn blockfetch_happy_path() {
let N2NChannels { channel3, .. } = setup_n2n_client_connection();
let N2NChannels { blockfetch, .. } = setup_n2n_client_connection();
let known_point = Point::Specific(
1654413,
hex::decode("7de1f036df5a133ce68a82877d14354d0ba6de7625ab918e75f3e2ecb29771c2").unwrap(),
);
let mut client = blockfetch::Client::new(channel3);
let mut client = blockfetch::Client::new(blockfetch);
let range_ok = client.request_range((known_point.clone(), known_point.clone()));
@ -170,13 +171,14 @@ pub fn blockfetch_happy_path() {
#[test]
#[ignore]
pub fn txsubmission_server_happy_path() {
// TODO(pi): Note that the below doesn't work; we need a node to connect *to us* during the integration test
// which seems awkward;
// Alternatively, we can just set up both a client and server connecting to themselves for testing!
// TODO(pi): Note that the below doesn't work; we need a node to connect *to us*
// during the integration test which seems awkward;
// Alternatively, we can just set up both a client and server connecting to
// themselves for testing!
let N2NChannels { channel4, .. } = setup_n2n_client_connection();
let N2NChannels { txsubmission, .. } = setup_n2n_client_connection();
let mut server = txsubmission::Server::new(channel4);
let mut server = txsubmission::Server::new(txsubmission);
assert!(matches!(server.wait_for_init(), Ok(_)));
@ -209,7 +211,8 @@ pub fn txsubmission_server_happy_path() {
assert!(matches!(reply, Ok(Reply::Txs(_))));
let Ok(Reply::Txs(second_txs)) = reply else { unreachable!() };
// Make sure we receive the second and third tx again, indicating we sent the `acknowledge 1` bit correctly
// Make sure we receive the second and third tx again, indicating we sent the
// `acknowledge 1` bit correctly
assert_eq!(second_txs[0], first_txs[1]);
assert_eq!(second_txs[1], first_txs[2]);

View file

@ -34,13 +34,13 @@ let bearer = UnixStream::connect("/tmp/pallas").unwrap();
let muxer = Multiplexer::setup(tcp, &[0, 2])
// Ask the multiplexer to provide us with the channel for the miniprotocol #0.
let mut channel_0 = muxer.use_channel(0);
let mut handshake = muxer.use_channel(PROTOCOL_N2N_HANDSHAKE);
// Spawn a thread and pass the ownership of the channel.
thread::spawn(move || {
// Deconstruct the channel to get a handle for sending data into the muxer
// ingress and a handle to receive data from the demuxer egress.
let Channel(mux_tx, demux_rx) = channel_0;
let Channel(mux_tx, demux_rx) = handshake;
// Do something with the channel. In this case, we just keep sending
// dumb data every 50 millis.
@ -51,14 +51,14 @@ thread::spawn(move || {
}
});
// Ask the multiplexer to provide us with the channel for the miniprotocol #2.
let mut channel_2 = muxer.use_channel(2);
// Ask the multiplexer to provide us with the channel for the chainsync miniprotocol.
let mut chainsync = muxer.use_channel(PROTOCOL_N2N_CHAINSYNC);
// Spawn a different thread and pass the ownership of the 2nd channel.
thread::spawn(move || {
// Deconstruct the channel to get a handle for sending data into the muxer
// ingress and a handle to receive data from the demuxer egress.
let Channel(mux_tx, demux_rx) = channel_2;
let Channel(mux_tx, demux_rx) = chainsync;
// Do something with the channel. In this case, we just print in stdout
// whatever get received for this mini-protocol.

View file

@ -844,7 +844,8 @@ impl AsRef<[u8]> for PlutusScript {
}
}
/// Defined to encode PlutusData bytestring as it is done in the canonical plutus implementation
/// Defined to encode PlutusData bytestring as it is done in the canonical
/// plutus implementation
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[serde(into = "String")]
#[serde(try_from = "String")]
@ -899,7 +900,8 @@ impl<C> Encode<C> for BoundedBytes {
e: &mut minicbor::Encoder<W>,
_: &mut C,
) -> Result<(), minicbor::encode::Error<W::Error>> {
// we match the haskell implementation by encoding bytestrings longer than 64 bytes as indefinite lists of bytes
// we match the haskell implementation by encoding bytestrings longer than 64
// bytes as indefinite lists of bytes
const CHUNK_SIZE: usize = 64;
let bs: &Vec<u8> = self.deref();
if bs.len() <= 64 {
@ -1086,8 +1088,8 @@ impl<C> minicbor::encode::Encode<C> for PlutusData {
e.encode_with(a, ctx)?;
}
Self::Map(a) => {
// we use definite array to match the approach used by haskell's plutus implementation
// https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L152
// we use definite array to match the approach used by haskell's plutus
// implementation https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L152
e.map(a.len().try_into().unwrap())?;
for (k, v) in a.iter() {
k.encode(e, ctx)?;
@ -1101,8 +1103,8 @@ impl<C> minicbor::encode::Encode<C> for PlutusData {
e.encode_with(a, ctx)?;
}
Self::Array(a) => {
// we use definite array for empty array or indef array otherwise to match haskell implementation
// https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L153
// we use definite array for empty array or indef array otherwise to match
// haskell implementation https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L153
// default encoder for a list:
// https://github.com/well-typed/cborg/blob/4bdc818a1f0b35f38bc118a87944630043b58384/serialise/src/Codec/Serialise/Class.hs#L181
encode_list(a, e, ctx)?;
@ -1172,15 +1174,16 @@ where
e.array(2)?;
e.encode_with(self.any_constructor.unwrap_or_default(), ctx)?;
// we use definite array for empty array or indef array otherwise to match haskell implementation
// https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L144
// we use definite array for empty array or indef array otherwise to match
// haskell implementation https://github.com/input-output-hk/plutus/blob/9538fc9829426b2ecb0628d352e2d7af96ec8204/plutus-core/plutus-core/src/PlutusCore/Data.hs#L144
// default encoder for a list:
// https://github.com/well-typed/cborg/blob/4bdc818a1f0b35f38bc118a87944630043b58384/serialise/src/Codec/Serialise/Class.hs#L181
encode_list(&self.fields, e, ctx)?;
Ok(())
}
_ => {
// we use definite array for empty array or indef array otherwise to match haskell implementation. See above reference.
// we use definite array for empty array or indef array otherwise to match
// haskell implementation. See above reference.
encode_list(&self.fields, e, ctx)?;
Ok(())
}

View file

@ -232,14 +232,15 @@ impl<'b> MultiEraTx<'b> {
}
}
/// Returns a list of tuples of the outputs produced by the Tx with their indexes
/// Returns a list of tuples of the outputs produced by the Tx with their
/// indexes
///
/// Helper method to abstract the logic of which outputs are produced
/// depending on the validity of the Tx. If the Tx is valid, this method
/// will return the list of outputs. If the Tx is invalid it will return the
/// collateral return if one is present or an empty list if not. Note that the
/// collateral return output index is defined as the next available index after
/// the txouts (Babbage spec, ch 4).
/// collateral return if one is present or an empty list if not. Note that
/// the collateral return output index is defined as the next available
/// index after the txouts (Babbage spec, ch 4).
pub fn produces(&self) -> Vec<(usize, MultiEraOutput)> {
match self.is_valid() {
true => self.outputs().into_iter().enumerate().collect(),