From 9d9ca12d2ed2f16abe8fa1af1c771130a571e31c Mon Sep 17 00:00:00 2001 From: Michael McGee Date: Mon, 21 Aug 2023 16:42:36 +0000 Subject: [PATCH] bank: tile --- config/with-optimization.mk | 8 +- ffi/rust/Cargo.toml | 2 +- ffi/rust/firedancer-sys/src/tango/cnc.rs | 2 + ffi/rust/firedancer-sys/src/tango/fctl.rs | 6 + ffi/rust/firedancer-sys/src/tango/fseq.rs | 1 + ffi/rust/firedancer-sys/src/tango/mod.rs | 3 + ffi/rust/firedancer-sys/src/util/mod.rs | 4 + ffi/rust/firedancer-sys/src/util/pod.rs | 2 + ffi/rust/firedancer-sys/src/util/rng.rs | 19 +++ ffi/rust/firedancer-sys/src/util/tempo.rs | 6 + ffi/rust/firedancer-sys/src/util/wksp.rs | 1 + ffi/rust/firedancer-sys/wrapper_util.h | 1 + ffi/rust/firedancer/Cargo.toml | 11 ++ ffi/rust/firedancer/src/bits.rs | 42 +++++ ffi/rust/firedancer/src/cnc.rs | 78 +++++++++ ffi/rust/firedancer/src/dcache.rs | 39 +++++ ffi/rust/firedancer/src/fctl.rs | 99 +++++++++++ ffi/rust/firedancer/src/fseq.rs | 67 ++++++++ ffi/rust/firedancer/src/gaddr.rs | 41 +++++ ffi/rust/firedancer/src/lib.rs | 21 +++ ffi/rust/firedancer/src/mcache.rs | 118 +++++++++++++ ffi/rust/firedancer/src/pod.rs | 156 +++++++++++++++++ ffi/rust/firedancer/src/rng.rs | 44 +++++ ffi/rust/firedancer/src/workspace.rs | 37 +++++ ffi/rust/firedancer/tests/linker.rs | 7 + solana | 2 +- src/app/fdctl/config.c | 3 + src/app/fdctl/config.h | 3 + src/app/fdctl/config/default.toml | 22 ++- src/app/fdctl/fdctl.h | 10 ++ src/app/fdctl/main1.c | 8 + src/app/fdctl/run.c | 16 +- src/app/fddev/Local.mk | 2 +- src/app/fddev/configure/cluster.c | 65 +++++--- src/app/fddev/dev.c | 3 - src/app/fddev/fddev.h | 14 ++ src/app/fddev/main.c | 7 + src/app/fddev/txn.c | 193 ++++++++++++++++++++++ src/util/log/fd_log.c | 20 +-- 39 files changed, 1135 insertions(+), 48 deletions(-) create mode 100644 ffi/rust/firedancer-sys/src/util/rng.rs create mode 100644 ffi/rust/firedancer-sys/src/util/tempo.rs create mode 100644 ffi/rust/firedancer/Cargo.toml create mode 100644 ffi/rust/firedancer/src/bits.rs create mode 100644 ffi/rust/firedancer/src/cnc.rs create mode 100644 ffi/rust/firedancer/src/dcache.rs create mode 100644 ffi/rust/firedancer/src/fctl.rs create mode 100644 ffi/rust/firedancer/src/fseq.rs create mode 100644 ffi/rust/firedancer/src/gaddr.rs create mode 100644 ffi/rust/firedancer/src/lib.rs create mode 100644 ffi/rust/firedancer/src/mcache.rs create mode 100644 ffi/rust/firedancer/src/pod.rs create mode 100644 ffi/rust/firedancer/src/rng.rs create mode 100644 ffi/rust/firedancer/src/workspace.rs create mode 100644 ffi/rust/firedancer/tests/linker.rs create mode 100644 src/app/fddev/txn.c diff --git a/config/with-optimization.mk b/config/with-optimization.mk index 70444fa0f1..5ca04eab71 100644 --- a/config/with-optimization.mk +++ b/config/with-optimization.mk @@ -1,4 +1,4 @@ -CPPFLAGS+=-O3 -ffast-math -fno-associative-math -fno-reciprocal-math -CPPFLAGS+=-DFD_HAS_OPTIMIZATION=1 -FD_HAS_OPTIMIZATION:=1 -RUST_PROFILE:=release +# CPPFLAGS+=-O3 -ffast-math -fno-associative-math -fno-reciprocal-math +# CPPFLAGS+=-DFD_HAS_OPTIMIZATION=1 +# FD_HAS_OPTIMIZATION:=1 +# RUST_PROFILE:=release diff --git a/ffi/rust/Cargo.toml b/ffi/rust/Cargo.toml index 6fe709b2a3..9cf1e2e908 100644 --- a/ffi/rust/Cargo.toml +++ b/ffi/rust/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["ffi/rust/firedancer-sys", "ffi/rust/firedancer-diff"] +members = ["ffi/rust/firedancer", "ffi/rust/firedancer-sys", "ffi/rust/firedancer-diff"] resolver = "2" [profile.dev] diff --git a/ffi/rust/firedancer-sys/src/tango/cnc.rs b/ffi/rust/firedancer-sys/src/tango/cnc.rs index fe4bdfa29a..17d19c1033 100644 --- a/ffi/rust/firedancer-sys/src/tango/cnc.rs +++ b/ffi/rust/firedancer-sys/src/tango/cnc.rs @@ -13,7 +13,9 @@ pub use crate::generated::{ fd_cnc_leave, fd_cnc_new, fd_cnc_open, + fd_cnc_signal, fd_cnc_signal_cstr, + fd_cnc_signal_query, fd_cnc_strerror, fd_cnc_t, fd_cnc_type, diff --git a/ffi/rust/firedancer-sys/src/tango/fctl.rs b/ffi/rust/firedancer-sys/src/tango/fctl.rs index eb32ef5094..654a6b4719 100644 --- a/ffi/rust/firedancer-sys/src/tango/fctl.rs +++ b/ffi/rust/firedancer-sys/src/tango/fctl.rs @@ -1,8 +1,14 @@ pub use crate::generated::{ fd_fctl_cfg_done, fd_fctl_cfg_rx_add, + fd_fctl_delete, + fd_fctl_join, + fd_fctl_leave, fd_fctl_new, + fd_fctl_private_rx_t, + fd_fctl_rx_cr_return, fd_fctl_t, + fd_fctl_tx_cr_update, FD_FCTL_ALIGN, FD_FCTL_RX_MAX_MAX, }; diff --git a/ffi/rust/firedancer-sys/src/tango/fseq.rs b/ffi/rust/firedancer-sys/src/tango/fseq.rs index fbdbc34290..605d620b94 100644 --- a/ffi/rust/firedancer-sys/src/tango/fseq.rs +++ b/ffi/rust/firedancer-sys/src/tango/fseq.rs @@ -1,5 +1,6 @@ pub use crate::generated::{ fd_fseq_align, + fd_fseq_app_laddr, fd_fseq_app_laddr_const, fd_fseq_delete, fd_fseq_footprint, diff --git a/ffi/rust/firedancer-sys/src/tango/mod.rs b/ffi/rust/firedancer-sys/src/tango/mod.rs index 77d451c0a7..5a9762ef9f 100644 --- a/ffi/rust/firedancer-sys/src/tango/mod.rs +++ b/ffi/rust/firedancer-sys/src/tango/mod.rs @@ -13,3 +13,6 @@ pub use fseq::*; pub use mcache::*; pub use tcache::*; pub use xdp::*; + +pub use crate::generated::fd_chunk_to_laddr; +pub use crate::generated::fd_chunk_to_laddr_const; diff --git a/ffi/rust/firedancer-sys/src/util/mod.rs b/ffi/rust/firedancer-sys/src/util/mod.rs index 3d99fc0fbb..dbfd9e5ee7 100644 --- a/ffi/rust/firedancer-sys/src/util/mod.rs +++ b/ffi/rust/firedancer-sys/src/util/mod.rs @@ -1,14 +1,18 @@ mod bits; mod log; mod pod; +mod rng; mod shmem; +mod tempo; mod tile; mod wksp; pub use bits::*; pub use log::*; pub use pod::*; +pub use rng::*; pub use shmem::*; +pub use tempo::*; pub use tile::*; pub use wksp::*; diff --git a/ffi/rust/firedancer-sys/src/util/pod.rs b/ffi/rust/firedancer-sys/src/util/pod.rs index 16687f7bf8..44a9150bf2 100644 --- a/ffi/rust/firedancer-sys/src/util/pod.rs +++ b/ffi/rust/firedancer-sys/src/util/pod.rs @@ -1,6 +1,8 @@ pub use crate::generated::{ fd_pod_cnt_subpod, fd_pod_info_t, + fd_pod_join, + fd_pod_leave, fd_pod_query, fd_pod_query_buf, fd_pod_query_char, diff --git a/ffi/rust/firedancer-sys/src/util/rng.rs b/ffi/rust/firedancer-sys/src/util/rng.rs new file mode 100644 index 0000000000..3648ddb24a --- /dev/null +++ b/ffi/rust/firedancer-sys/src/util/rng.rs @@ -0,0 +1,19 @@ +pub use crate::generated::{ + fd_rng_delete, + fd_rng_idx, + fd_rng_idx_set, + fd_rng_int, + fd_rng_join, + fd_rng_leave, + fd_rng_long, + fd_rng_new, + fd_rng_schar, + fd_rng_seq, + fd_rng_seq_set, + fd_rng_short, + fd_rng_t, + fd_rng_uchar, + fd_rng_uint, + fd_rng_ulong, + fd_rng_ushort, +}; diff --git a/ffi/rust/firedancer-sys/src/util/tempo.rs b/ffi/rust/firedancer-sys/src/util/tempo.rs new file mode 100644 index 0000000000..1bc091dd7e --- /dev/null +++ b/ffi/rust/firedancer-sys/src/util/tempo.rs @@ -0,0 +1,6 @@ +pub use crate::generated::{ + fd_tempo_async_min, + fd_tempo_async_reload, + fd_tempo_lazy_default, + fd_tempo_tick_per_ns, +}; diff --git a/ffi/rust/firedancer-sys/src/util/wksp.rs b/ffi/rust/firedancer-sys/src/util/wksp.rs index f43cad4483..83da7cc153 100644 --- a/ffi/rust/firedancer-sys/src/util/wksp.rs +++ b/ffi/rust/firedancer-sys/src/util/wksp.rs @@ -27,5 +27,6 @@ pub use crate::generated::{ fd_wksp_pod_map, fd_wksp_pod_unmap, fd_wksp_t, + fd_wksp_private, fd_wksp_unmap, }; diff --git a/ffi/rust/firedancer-sys/wrapper_util.h b/ffi/rust/firedancer-sys/wrapper_util.h index 01c6b72bc2..948fcfea45 100644 --- a/ffi/rust/firedancer-sys/wrapper_util.h +++ b/ffi/rust/firedancer-sys/wrapper_util.h @@ -1,4 +1,5 @@ #include "src/util/fd_util.h" +#include "src/util/wksp/fd_wksp_private.h" #if FD_MCACHE_LG_INTERLEAVE #error "FD_MCACHE_LG_INTERLEAVE unsupported" diff --git a/ffi/rust/firedancer/Cargo.toml b/ffi/rust/firedancer/Cargo.toml new file mode 100644 index 0000000000..2656000b33 --- /dev/null +++ b/ffi/rust/firedancer/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "firedancer" +version = "0.1.0" +edition = "2021" +authors = ["Firedancer Contributors "] +description = "Safe high level wrappers for Firedancer" +license = "Apache-2.0" + +[dependencies] +paste = "1.0.12" +firedancer-sys = { path = "../firedancer-sys" } diff --git a/ffi/rust/firedancer/src/bits.rs b/ffi/rust/firedancer/src/bits.rs new file mode 100644 index 0000000000..587dae92ec --- /dev/null +++ b/ffi/rust/firedancer/src/bits.rs @@ -0,0 +1,42 @@ +macro_rules! align_up { + ( $x:expr, $a:expr ) => { + ($x + ($a - 1)) & !($a - 1) + }; +} + +macro_rules! layout { + ( value=$value:expr, ) => { $value }; + ( value=$value:expr, ($align:expr, $size:expr), $($tail:tt)*) => { + layout!( value=align_up!($value, $align) + $size, $($tail)*) + }; + ( align=$align:expr, [ $($tail:tt)* ]) => { + align_up!(layout!(value = 0, $($tail)*), $align) + }; +} + +pub(crate) use { + align_up, + layout, +}; + +#[cfg(test)] +mod tests { + #[test] + fn test_align_up() { + let zeros = 0u64; + let ones = u64::MAX; + + for i in 0..64 { + let align = 1u64 << i; + let lo = (1u64 << i) - 1; + let hi = !lo; + + assert_eq!(align_up!(zeros, align), zeros); + assert_eq!(align_up!(ones, align), if i == 0 { ones } else { zeros }); + for j in 0..64 { + let x = 1u64 << j; + assert_eq!(align_up!(x, align), (x + lo) & hi); + } + } + } +} diff --git a/ffi/rust/firedancer/src/cnc.rs b/ffi/rust/firedancer/src/cnc.rs new file mode 100644 index 0000000000..e7586b0fbc --- /dev/null +++ b/ffi/rust/firedancer/src/cnc.rs @@ -0,0 +1,78 @@ +use std::ffi::c_ulong; +use std::ptr::{ + read_volatile, + write_volatile, +}; +use firedancer_sys::*; + +use crate::*; + +pub struct Cnc { + laddr: *mut tango::fd_cnc_t, + diagnostic: *mut c_ulong, + _workspace: Workspace, +} + +impl Drop for Cnc { + fn drop(&mut self) { + unsafe { tango::fd_cnc_leave(self.laddr) }; + } +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum CncSignal { + Run = tango::FD_CNC_SIGNAL_RUN, + Boot = tango::FD_CNC_SIGNAL_BOOT, + Fail = tango::FD_CNC_SIGNAL_FAIL, + Halt = tango::FD_CNC_SIGNAL_HALT, +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum CncDiag { + InBackpressure = tango::FD_CNC_DIAG_IN_BACKP, + BackpressureCount = tango::FD_CNC_DIAG_BACKP_CNT, +} + +impl Cnc { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = tango::fd_cnc_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + Err(()) + } else { + let diagnostic = tango::fd_cnc_app_laddr(laddr) as *mut c_ulong; + if diagnostic.is_null() { + Err(()) + } else { + Ok(Self { + laddr, + diagnostic, + _workspace: workspace, + }) + } + } + } + + pub fn query(&self) -> u64 { + unsafe { tango::fd_cnc_signal_query(self.laddr) } + } + + pub fn signal(&self, signal: u64) { + unsafe { tango::fd_cnc_signal(self.laddr, signal) } + } + + pub unsafe fn set(&self, diag: u64, value: u64) { + write_volatile(self.diagnostic.offset(diag as isize), value) + } + + pub unsafe fn increment(&self, diag: u64, value: u64) { + let offset = self.diagnostic.offset(diag as isize); + write_volatile(offset, read_volatile(offset) + value) + } + + pub fn heartbeat(&self, now: i64) { + unsafe { tango::fd_cnc_heartbeat(self.laddr, now) } + } +} diff --git a/ffi/rust/firedancer/src/dcache.rs b/ffi/rust/firedancer/src/dcache.rs new file mode 100644 index 0000000000..8510e34f08 --- /dev/null +++ b/ffi/rust/firedancer/src/dcache.rs @@ -0,0 +1,39 @@ +use firedancer_sys::*; + +use std::ffi::c_void; + +use crate::*; + +pub struct DCache { + laddr: *mut u8, + wksp: *mut util::fd_wksp_t, + _workspace: Workspace, +} + +impl Drop for DCache { + fn drop(&mut self) { + unsafe { tango::fd_dcache_leave(self.laddr) }; + } +} + +impl DCache { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = tango::fd_dcache_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + return Err(()); + } + + let wksp = util::fd_wksp_containing(laddr as *const c_void); + Ok(Self { + laddr, + wksp, + _workspace: workspace, + }) + } + + pub unsafe fn slice<'a>(&self, chunk: u64, offset: u64, len: u64) -> &'a[u8] { + let laddr = tango::fd_chunk_to_laddr_const(self.wksp as *const c_void, chunk); + std::slice::from_raw_parts(laddr.offset(offset as isize) as *const u8, len as usize) + } +} diff --git a/ffi/rust/firedancer/src/fctl.rs b/ffi/rust/firedancer/src/fctl.rs new file mode 100644 index 0000000000..a308206041 --- /dev/null +++ b/ffi/rust/firedancer/src/fctl.rs @@ -0,0 +1,99 @@ +use std::ffi::c_void; +use std::marker::PhantomData; +use std::mem::{ + align_of, + size_of, +}; +use std::ptr::null_mut; + +use firedancer_sys::*; + +use crate::*; + +macro_rules! footprint { + ( $rx_max:expr ) => { + layout!( + align = tango::FD_FCTL_ALIGN as usize, + [ + ( + align_of::(), + size_of::() + ), + ( + align_of::(), + $rx_max * size_of::() + ), + ] + ) + }; +} + +pub struct FCtl<'a, 'b> { + _stack: Vec, + shmem: *mut c_void, + fctl: *mut tango::fd_fctl_t, + _seq: PhantomData<&'a u64>, + _slow: PhantomData<&'b mut u64>, +} + +impl<'a, 'b> Drop for FCtl<'a, 'b> { + fn drop(&mut self) { + unsafe { tango::fd_fctl_leave(self.fctl) }; + unsafe { tango::fd_fctl_delete(self.shmem) }; + } +} + +impl<'a, 'b> FCtl<'a, 'b> { + pub fn new( + cr_burst: u64, + cr_max: u64, + cr_resume: u64, + cr_refill: u64, + fseq: &FSeq, + ) -> Result { + let mut stack = vec![0; footprint!(1usize)]; + let shmem = unsafe { tango::fd_fctl_new(stack.as_mut_ptr() as *mut _, 1) }; + if shmem.is_null() { + return Err(()); + } + + let fctl = unsafe { tango::fd_fctl_join(shmem) }; + if fctl.is_null() { + return Err(()); + } + + let fctl = unsafe { + tango::fd_fctl_cfg_rx_add( + fctl, + cr_max, + fseq.laddr, + fseq.diagnostic.offset(FSeqDiag::SlowCount as isize), + ) + }; + if fctl.is_null() { + return Err(()); + } + + let fctl = unsafe { tango::fd_fctl_cfg_done(fctl, cr_burst, cr_max, cr_resume, cr_refill) }; + + Ok(FCtl { + _stack: stack, + shmem, + fctl, + _seq: PhantomData, + _slow: PhantomData, + }) + } + + pub fn tx_cr_update(&self, cr_avail: u64, mcache: &MCache) -> u64 { + unsafe { tango::fd_fctl_tx_cr_update(self.fctl, cr_avail, mcache.sequence_number) } + } +} + +pub fn housekeeping_default_interval_nanos(cr_max: u64) -> i64 { + unsafe { util::fd_tempo_lazy_default(cr_max) } +} + +pub fn minimum_housekeeping_tick_interval(lazy: i64) -> u64 { + unsafe { util::fd_tempo_async_min(lazy, 1, util::fd_tempo_tick_per_ns(null_mut()) as f32) } +} diff --git a/ffi/rust/firedancer/src/fseq.rs b/ffi/rust/firedancer/src/fseq.rs new file mode 100644 index 0000000000..8792a8b57d --- /dev/null +++ b/ffi/rust/firedancer/src/fseq.rs @@ -0,0 +1,67 @@ +use std::ffi::c_ulong; +use std::ptr::{ + read_volatile, + write_volatile, +}; + +use firedancer_sys::*; + +use crate::*; + +pub struct FSeq { + pub(crate) laddr: *mut c_ulong, + pub(crate) diagnostic: *mut c_ulong, + _workspace: Workspace, +} + +impl Drop for FSeq { + fn drop(&mut self) { + unsafe { tango::fd_fseq_leave(self.laddr) }; + } +} + +#[repr(u32)] +#[derive(Copy, Clone, Debug)] +pub enum FSeqDiag { + PublishedCount = tango::FD_FSEQ_DIAG_PUB_CNT, + PublishedSize = tango::FD_FSEQ_DIAG_PUB_SZ, + FilteredCount = tango::FD_FSEQ_DIAG_FILT_CNT, + FilteredSize = tango::FD_FSEQ_DIAG_FILT_SZ, + OverrunPollingCount = tango::FD_FSEQ_DIAG_OVRNP_CNT, + OverrunReadingCount = tango::FD_FSEQ_DIAG_OVRNR_CNT, + SlowCount = tango::FD_FSEQ_DIAG_SLOW_CNT, +} + +impl FSeq { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = tango::fd_fseq_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + Err(()) + } else { + let diagnostic = tango::fd_fseq_app_laddr(laddr) as *mut c_ulong; + if diagnostic.is_null() { + Err(()) + } else { + Ok(Self { + laddr, + diagnostic, + _workspace: workspace, + }) + } + } + } + + pub unsafe fn set(&self, diag: u64, value: u64) { + write_volatile(self.diagnostic.offset(diag as isize), value) + } + + pub unsafe fn increment(&self, diag: u64, value: u64) { + let offset = self.diagnostic.offset(diag as isize); + write_volatile(offset, read_volatile(offset) + value) + } + + pub fn rx_cr_return(&self, mcache: &MCache) { + unsafe { tango::fd_fctl_rx_cr_return(self.laddr, mcache.sequence_number) } + } +} \ No newline at end of file diff --git a/ffi/rust/firedancer/src/gaddr.rs b/ffi/rust/firedancer/src/gaddr.rs new file mode 100644 index 0000000000..427d90ea35 --- /dev/null +++ b/ffi/rust/firedancer/src/gaddr.rs @@ -0,0 +1,41 @@ +use std::ffi::{ + c_char, + CString, +}; + +pub struct GlobalAddress { + gaddr: CString, + _offset: u64, +} + +impl GlobalAddress { + pub(crate) fn as_ptr(&self) -> *const c_char { + self.gaddr.as_ptr() + } +} + +impl TryFrom for GlobalAddress { + type Error = (); + + fn try_from(value: String) -> Result { + let (_workspace, offset) = match value.split_once(':') { + None => return Err(()), + Some(parts) => parts, + }; + + let offset = match offset.parse() { + Err(_) => return Err(()), + Ok(offset) => offset, + }; + + let gaddr = match CString::new(value) { + Err(_) => return Err(()), + Ok(path) => path, + }; + + Ok(GlobalAddress { + gaddr, + _offset: offset, + }) + } +} diff --git a/ffi/rust/firedancer/src/lib.rs b/ffi/rust/firedancer/src/lib.rs new file mode 100644 index 0000000000..7adcde9139 --- /dev/null +++ b/ffi/rust/firedancer/src/lib.rs @@ -0,0 +1,21 @@ +mod bits; +mod cnc; +mod dcache; +mod fctl; +mod fseq; +mod gaddr; +mod mcache; +mod pod; +mod rng; +mod workspace; + +use bits::*; +pub use cnc::*; +pub use dcache::*; +pub use fctl::*; +pub use fseq::*; +pub use gaddr::*; +pub use mcache::*; +pub use pod::*; +pub use rng::*; +pub use workspace::*; diff --git a/ffi/rust/firedancer/src/mcache.rs b/ffi/rust/firedancer/src/mcache.rs new file mode 100644 index 0000000000..8214976a6a --- /dev/null +++ b/ffi/rust/firedancer/src/mcache.rs @@ -0,0 +1,118 @@ +use std::sync::atomic::{ + compiler_fence, + Ordering, +}; + +use firedancer_sys::*; + +use crate::*; + +pub struct MCache { + laddr: *mut tango::fd_frag_meta_t, + mline: *mut tango::fd_frag_meta_t, + pub(crate) sequence_number: u64, + depth: u64, + + sync: *mut u64, + _workspace: Workspace, +} + +#[derive(Copy, Clone)] +pub enum MCacheCtl { + None +} + +impl MCacheCtl { + fn ctl(&self) -> u64 { + match self { + MCacheCtl::None => 0, + } + } +} + +#[derive(Copy, Clone)] +pub enum Poll { + CaughtUp, + Overrun, + Ready, +} + +#[derive(Copy, Clone)] + pub enum Advance { + Overrun, + Normal, +} + +impl MCache { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = tango::fd_mcache_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + return Err(()); + } + + let depth = tango::fd_mcache_depth(laddr); + let sync = tango::fd_mcache_seq_laddr(laddr); + let sequence_number = tango::fd_mcache_seq_query(sync); + let mline = laddr.offset(tango::fd_mcache_line_idx(sequence_number, depth) as isize); + + Ok(Self { + laddr, + mline, + sequence_number, + depth, + sync, + _workspace: workspace, + }) + } + + pub fn depth(&self) -> u64 { + self.depth + } + + pub fn chunk(&self) -> u32 { + unsafe { (*self.mline).__bindgen_anon_1.chunk } + } + + pub fn housekeep(&self) { + unsafe { tango::fd_mcache_seq_update(self.sync, self.sequence_number) } + } + + pub fn poll(&self) -> Poll { + compiler_fence(Ordering::AcqRel); + let sequence_number_found = unsafe { (*self.mline).__bindgen_anon_1.seq }; + compiler_fence(Ordering::AcqRel); + + match sequence_number_found.cmp(&self.sequence_number) { + std::cmp::Ordering::Less => Poll::Overrun, + std::cmp::Ordering::Equal => Poll::Ready, + std::cmp::Ordering::Greater => Poll::CaughtUp, + } + } + + pub fn size(&self) -> u16 { + unsafe { (*self.mline).__bindgen_anon_1.sz } + } + + pub fn advance(&mut self) -> Advance { + compiler_fence(Ordering::AcqRel); + let sequence_number_found = unsafe { (*self.mline).__bindgen_anon_1.seq }; + compiler_fence(Ordering::AcqRel); + + if sequence_number_found != self.sequence_number { + self.sequence_number = sequence_number_found; + Advance::Overrun + } else { + self.sequence_number += 1; + self.mline = unsafe { + self.laddr + .offset(tango::fd_mcache_line_idx(self.sequence_number, self.depth) as isize) + }; + Advance::Normal + } + } + + pub fn publish(&mut self, sig: u64, chunk: u64, sz: u64, ctl: MCacheCtl, tsorig: u64, tspub: u64) { + unsafe { tango::fd_mcache_publish(self.mline, self.depth, self.sequence_number, sig, chunk, sz, ctl.ctl(), tsorig, tspub) } + } +} diff --git a/ffi/rust/firedancer/src/pod.rs b/ffi/rust/firedancer/src/pod.rs new file mode 100644 index 0000000000..2fb1cd9155 --- /dev/null +++ b/ffi/rust/firedancer/src/pod.rs @@ -0,0 +1,156 @@ +use std::ffi::{ + c_char, + CStr, + CString, c_void, +}; +use std::marker::PhantomData; +use std::ptr::{null, NonNull}; +use std::sync::Arc; + +use firedancer_sys::*; +use paste::paste; + +use crate::*; + +#[derive(Clone)] +pub struct Pod { + laddr: *const u8, + workspace: Arc, +} + +unsafe impl Send for Pod {} +unsafe impl Sync for Pod {} + +impl Drop for Pod { + fn drop(&mut self) { + unsafe { util::fd_pod_leave(self.laddr) }; + } +} + +impl Pod { + pub unsafe fn join>(gaddr: T) -> Result { + let workspace = Workspace::map(gaddr)?; + let laddr = util::fd_pod_join(workspace.laddr.as_ptr()); + if laddr.is_null() { + Err(()) + } else { + Ok(Self { + laddr, + workspace: Arc::new(workspace), + }) + } + } + + pub unsafe fn join_default>(wksp: T) -> Result { + let wksp_str = CString::new(wksp.as_ref()).unwrap(); + let wksp = util::fd_wksp_attach(wksp_str.as_ptr()); + if wksp.is_null() { + return Err(()); + } + + let pod = util::fd_wksp_laddr( wksp, (*wksp).gaddr_lo ); + if pod.is_null() { + return Err(()); + } + + let laddr = util::fd_pod_join(pod); + + if laddr.is_null() { + Err(()) + } else { + Ok(Self { + laddr, + workspace: Arc::new(Workspace { + laddr: NonNull::new(laddr as *mut c_void).unwrap(), + _marker: PhantomData, + }), + }) + } + } + + pub fn try_query>(&self, key: S) -> Option { + let key = match CString::new(key.as_ref()) { + Ok(key) => key, + _ => return None, + }; + + T::try_query(self, key.as_ptr()) + } + + pub fn query>(&self, key: S) -> T { + let key = match CString::new(key.as_ref()) { + Ok(key) => key, + _ => return T::default(), + }; + + T::query(self, key.as_ptr()) + } +} + +pub trait FromPod: Sized { + fn try_query(pod: &Pod, key: *const c_char) -> Option; + + fn query(pod: &Pod, key: *const c_char) -> Self { + FromPod::try_query(pod, key).unwrap() + } +} + +impl FromPod for String { + fn try_query(pod: &Pod, key: *const c_char) -> Option { + let value = unsafe { util::fd_pod_query_cstr(pod.laddr, key, null()) } as *const i8; + + if value.is_null() { + return None; + } + + match unsafe { CStr::from_ptr(value).to_str() } { + Ok(str) => Some(str.to_owned()), + _ => None, + } + } +} + +impl FromPod for Pod { + fn try_query(pod: &Pod, key: *const c_char) -> Option { + let laddr = unsafe { util::fd_pod_query_subpod(pod.laddr, key) }; + + if laddr.is_null() { + return None; + } + + Some(Pod { + laddr, + workspace: pod.workspace.clone(), + }) + } +} + +impl FromPod for GlobalAddress { + fn try_query(pod: &Pod, key: *const c_char) -> Option { + let string: String = FromPod::try_query(pod, key)?; + string.try_into().ok() + } +} + +macro_rules! impl_from_pod { + ($ty:ty, $id:ident) => { + impl FromPod for $ty { + fn try_query(pod: &Pod, key: *const c_char) -> Option { + paste! { + unsafe { + Some(util::[](pod.laddr, key, $ty::default())) + } + } + } + } + }; +} + +impl_from_pod!(i8, char); +impl_from_pod!(i16, short); +impl_from_pod!(i32, int); +impl_from_pod!(i64, long); +impl_from_pod!(u8, uchar); +impl_from_pod!(u16, ushort); +impl_from_pod!(u32, uint); +impl_from_pod!(u64, ulong); diff --git a/ffi/rust/firedancer/src/rng.rs b/ffi/rust/firedancer/src/rng.rs new file mode 100644 index 0000000000..09a0f3a9e8 --- /dev/null +++ b/ffi/rust/firedancer/src/rng.rs @@ -0,0 +1,44 @@ +use std::ffi::c_void; + +use firedancer_sys::*; + +pub struct Rng { + inner: util::fd_rng_t, + shmem: *mut c_void, +} + +impl Drop for Rng { + fn drop(&mut self) { + unsafe { util::fd_rng_leave(&mut self.inner) }; + unsafe { util::fd_rng_delete(self.shmem) }; + } +} + +impl Rng { + pub fn new(seed: u32, id: u64) -> Result { + let mut inner = util::fd_rng_t { seq: 0, idx: 0 }; + + let shmem = unsafe { util::fd_rng_new(&mut inner as *mut _ as *mut c_void, seed, id) }; + if shmem.is_null() { + return Err(()); + } + + let rng = unsafe { util::fd_rng_join(shmem) }; + if rng.is_null() { + return Err(()); + } + + Ok(Rng { inner, shmem }) + } + + pub fn async_reload(&mut self, min: u64) -> u64 { + unsafe { util::fd_tempo_async_reload(&mut self.inner, min) } + } +} + +pub fn boot() { + let mut argc: i32 = 0; + unsafe { + util::fd_boot(&mut argc as *mut _, std::ptr::null_mut()); + } +} diff --git a/ffi/rust/firedancer/src/workspace.rs b/ffi/rust/firedancer/src/workspace.rs new file mode 100644 index 0000000000..f1cefd18e3 --- /dev/null +++ b/ffi/rust/firedancer/src/workspace.rs @@ -0,0 +1,37 @@ +use std::cell::Cell; +use std::ffi::c_void; +use std::marker::PhantomData; +use std::ptr::NonNull; + +use firedancer_sys::*; + +use crate::*; + +pub struct Workspace { + pub(crate) laddr: NonNull, + pub(crate) _marker: PhantomData>, // Not covariant +} + +impl Drop for Workspace { + fn drop(&mut self) { + unsafe { firedancer_sys::util::fd_wksp_unmap(self.laddr.as_ptr()) } + } +} + +impl Workspace { + pub(crate) unsafe fn map>(gaddr: G) -> Result { + let addr: GlobalAddress = match gaddr.try_into() { + Ok(addr) => addr, + _ => return Err(()), + }; + let laddr = unsafe { util::fd_wksp_map(addr.as_ptr()) }; + if laddr.is_null() { + Err(()) + } else { + Ok(Self { + laddr: NonNull::new(laddr).unwrap(), + _marker: PhantomData, + }) + } + } +} diff --git a/ffi/rust/firedancer/tests/linker.rs b/ffi/rust/firedancer/tests/linker.rs new file mode 100644 index 0000000000..c7a80d077d --- /dev/null +++ b/ffi/rust/firedancer/tests/linker.rs @@ -0,0 +1,7 @@ +use firedancer::*; + +#[test] +fn links_properly() { + let gaddr = GlobalAddress::try_from("".to_string()).unwrap(); + assert!(unsafe { MCache::join(gaddr) }.is_err()); +} diff --git a/solana b/solana index 354d6262d6..29bcd55ea3 160000 --- a/solana +++ b/solana @@ -1 +1 @@ -Subproject commit 354d6262d62e95df27aefa1b8e5d24f1e5f415e2 +Subproject commit 29bcd55ea39db8722fe66793c318fcc171624a5e diff --git a/src/app/fdctl/config.c b/src/app/fdctl/config.c index 65dd7cba71..07946c7bfc 100644 --- a/src/app/fdctl/config.c +++ b/src/app/fdctl/config.c @@ -227,6 +227,8 @@ static void parse_key_value( config_t * config, ENTRY_BOOL ( ., ledger, bigtable_storage ); ENTRY_VSTR ( ., ledger, account_indexes ); ENTRY_VSTR ( ., ledger, account_index_exclude_keys ); + ENTRY_STR ( ., ledger, snapshot_compression ); + ENTRY_BOOL ( ., ledger, require_tower ); ENTRY_VSTR ( ., gossip, entrypoints ); ENTRY_BOOL ( ., gossip, port_check ); @@ -245,6 +247,7 @@ static void parse_key_value( config_t * config, ENTRY_BOOL ( ., consensus, wait_for_vote_to_start_leader ); ENTRY_VUINT ( ., consensus, hard_fork_at_slots ); ENTRY_VSTR ( ., consensus, known_validators ); + ENTRY_BOOL ( ., consensus, os_network_limits_test ); ENTRY_USHORT( ., rpc, port ); ENTRY_BOOL ( ., rpc, full_api ); diff --git a/src/app/fdctl/config.h b/src/app/fdctl/config.h index 4ba40671b2..a006b94e07 100644 --- a/src/app/fdctl/config.h +++ b/src/app/fdctl/config.h @@ -57,6 +57,8 @@ typedef struct { char account_indexes[ 4 ][ 32 ]; ulong account_index_exclude_keys_cnt; char account_index_exclude_keys[ 32 ][ 32 ]; + int require_tower; + char snapshot_compression[ 10 ]; } ledger; struct { @@ -82,6 +84,7 @@ typedef struct { uint hard_fork_at_slots[ 32 ]; ulong known_validators_cnt; char known_validators[ 16 ][ 256 ]; + int os_network_limits_test; } consensus; struct { diff --git a/src/app/fdctl/config/default.toml b/src/app/fdctl/config/default.toml index 83cc82da4b..74e7a0baaa 100644 --- a/src/app/fdctl/config/default.toml +++ b/src/app/fdctl/config/default.toml @@ -29,7 +29,7 @@ scratch_directory = "/home/{user}/.firedancer/{name}" # [min, max). Ports are used for receiving transactions and votes from clients # and other validators. This option is passed to the Solana Labs client with the # `--dynamic-port-range` argument. -dynamic_port_range = "8000-10000" +dynamic_port_range = "" # TODO: Describe ledger. [ledger] @@ -63,7 +63,7 @@ dynamic_port_range = "8000-10000" # # This option is passed to the Solana Labs client with the # `--limit-ledger-size` argument. - limit_size = 200_000_000 + limit_size = 0 # If enabled, fetch historical transaction info from a BigTable instance as # a fallback to local ledger data. The `GOOGLE_APPLICATION_CREDENTIALS` @@ -83,6 +83,14 @@ dynamic_port_range = "8000-10000" # `--account-index-exclude-key` argument. account_index_exclude_keys = [] + # Whether to use compression when storing snapshots. This option is passed + # to the Solana Labs client with the `--snapshot-compression` argument. + snapshot_compression = "none" + + # Refuse to start if saved tower state is not found. This option is passed + # to the Solana Labs client with the `--require-tower` argument. + require_tower = true + [gossip] # Routable DNS name or IP address and port number to use to rendezvous with # the gossip cluster. These entrypoints are passed to the Solana Labs client @@ -123,7 +131,7 @@ dynamic_port_range = "8000-10000" # If the RPC is private, the valdiator's open RPC port is not published in # the `solana gossip` command for use by others. This option is passed to # the Solana Labs client with the `--private-rpc` argument. - private = true + private = false # Enable historical transaction info over JSON RPC, including the # `getConfirmedBlock` API. This will cause an increase in disk usage and @@ -134,7 +142,7 @@ dynamic_port_range = "8000-10000" # If enabled, include CPI inner instructions, logs, and return data in the # historical transaction info stored. This option is passed to the Solana # Labs client with the `--enable-extended-tx-metadata-storage` argument. - extended_tx_metadata_storage = false + extended_tx_metadata_storage = true # If true, use the RPC service of known validators only. This option is # passed to the Solana Labs client with the `--only-known-rpc` argument. @@ -225,6 +233,12 @@ dynamic_port_range = "8000-10000" # `--no-wait-for-vote-to-start-leader` argument. wait_for_vote_to_start_leader = true + # Don't perform a network speed test on starting up the validator. If this + # is not disabled, and the speed test fails, the validator will refuse to + # start. This option is passed to the Solana Labs client (inverted) with the + # `--no-os-network-limits-test` argument. + os_network_limits_test = false + # If nonempty, add a hard fork at each of the provided slots. These options # are passed to the Solana Labs client with the `--hard-fork` argument. hard_fork_at_slots = [] diff --git a/src/app/fdctl/fdctl.h b/src/app/fdctl/fdctl.h index cc91437ea2..589e8a25c3 100644 --- a/src/app/fdctl/fdctl.h +++ b/src/app/fdctl/fdctl.h @@ -33,6 +33,16 @@ typedef union { struct { int monitor; } dev; + struct { + const char * payload_base64; + ulong count; + const char * dst_ip; + ushort dst_port; + } txn; + struct { + int setcap; + int withcap; + } gdb; } args_t; typedef struct security security_t; diff --git a/src/app/fdctl/main1.c b/src/app/fdctl/main1.c index 16aa41059a..31d61713d6 100644 --- a/src/app/fdctl/main1.c +++ b/src/app/fdctl/main1.c @@ -1,5 +1,8 @@ +#define _GNU_SOURCE #include "fdctl.h" +#include + action_t ACTIONS[ 4 ] = { { .name = "run", .args = NULL, .fn = run_cmd_fn, .perm = run_cmd_perm }, { .name = "configure", .args = configure_cmd_args, .fn = configure_cmd_fn, .perm = configure_cmd_perm }, @@ -7,9 +10,14 @@ action_t ACTIONS[ 4 ] = { { .name = "keygen", .args = NULL, .fn = keygen_cmd_fn, .perm = NULL }, }; +extern int * fd_log_private_shared_lock; + int main1( int argc, char ** _argv ) { + fd_log_private_shared_lock = (int*)mmap( NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, (off_t)0 ); + if( FD_UNLIKELY( !fd_log_private_shared_lock ) ) exit(1); + fd_boot( &argc, &_argv ); fd_log_thread_set( "main" ); diff --git a/src/app/fdctl/run.c b/src/app/fdctl/run.c index 3bc39bc8e6..1f638f956a 100644 --- a/src/app/fdctl/run.c +++ b/src/app/fdctl/run.c @@ -241,13 +241,18 @@ solana_labs_main( void * args ) { ADD1( "fdctl" ); ADD( "--log", "-" ); + ADD( "--firedancer-app-name", config->name ); - ADD( "--dynamic-port-range", config->dynamic_port_range ); + ADD1("--no-poh-speed-test" ); // TODO: Only if we built debug? + if( FD_UNLIKELY( strcmp( config->dynamic_port_range, "" ) ) ) + ADD( "--dynamic-port-range", config->dynamic_port_range ); /* consensus */ + (void)identity_path; ADD( "--identity", identity_path ); if( strcmp( config->consensus.vote_account_path, "" ) ) ADD( "--vote-account", config->consensus.vote_account_path ); + if( !config->consensus.snapshot_fetch ) ADD1( "--no-snapshot-fetch" ); if( !config->consensus.genesis_fetch ) ADD1( "--no-genesis-fetch" ); if( !config->consensus.poh_speed_test ) ADD1( "--no-poh-speed-test" ); @@ -266,9 +271,14 @@ solana_labs_main( void * args ) { for( ulong i=0; iconsensus.known_validators_cnt; i++ ) ADD( "--known_validator", config->consensus.known_validators[ i ] ); + ADD( "--snapshot-compression", config->ledger.snapshot_compression ); + if( FD_UNLIKELY( config->ledger.require_tower ) ) ADD1( "--require-tower" ); + + if( FD_UNLIKELY( !config->consensus.os_network_limits_test ) ) ADD1( "--no-os-network-limits-test" ); + /* ledger */ ADD( "--ledger", config->ledger.path ); - ADDU( "--limit-ledger-size", config->ledger.limit_size ); + if( FD_LIKELY( config->ledger.limit_size ) ) ADDU( "--limit-ledger-size", config->ledger.limit_size ); if( config->ledger.bigtable_storage ) ADD1( "--enable-rpc-bigtable-ledger-storage" ); for( ulong i=0; iledger.account_indexes_cnt; i++ ) ADD( "--account-index", config->ledger.account_indexes[ i ] ); @@ -282,7 +292,7 @@ solana_labs_main( void * args ) { if( strcmp( config->gossip.host, "" ) ) ADD( "--gossip-host", config->gossip.host ); - /* rpc */ + // /* rpc */ if( config->rpc.port ) ADDH( "--rpc-port", config->rpc.port ); if( config->rpc.full_api ) ADD1( "--full-rpc-api" ); if( config->rpc.private ) ADD1( "--private-rpc" ); diff --git a/src/app/fddev/Local.mk b/src/app/fddev/Local.mk index 5a1ea0ded0..71a211e427 100644 --- a/src/app/fddev/Local.mk +++ b/src/app/fddev/Local.mk @@ -4,7 +4,7 @@ ifdef FD_HAS_X86 ifdef FD_HAS_DOUBLE .PHONY: fddev -$(call make-bin-rust,fddev,main dev dev1 configure/netns configure/cluster,fd_fdctl fd_frank fd_disco fd_ballet fd_tango fd_util fd_quic solana_validator_fd) +$(call make-bin-rust,fddev,main dev dev1 txn configure/netns configure/cluster,fd_fdctl fd_frank fd_disco fd_ballet fd_tango fd_util fd_quic solana_validator_fd) ifeq (run,$(firstword $(MAKECMDGOALS))) RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS)) diff --git a/src/app/fddev/configure/cluster.c b/src/app/fddev/configure/cluster.c index 1d75e90231..27501982f3 100644 --- a/src/app/fddev/configure/cluster.c +++ b/src/app/fddev/configure/cluster.c @@ -2,6 +2,7 @@ #include "../../fdctl/configure/configure.h" #include +#include #include #define NAME "cluster" @@ -48,17 +49,11 @@ init( config_t * const config ) { ADD1( "fddev" ); - ADDU( "--max-genesis-archive-unpacked-size", 1073741824 ); - ADD1( "--enable-warmup-epochs" ); - ADD( "--bootstrap-validator", config->consensus.identity_path ); - ADD1( vote ); - ADD1( stake ); - ADD( "--bootstrap-stake-authorized-pubkey", config->consensus.identity_path ); - - ADD( "--ledger", config->ledger.path ); ADD( "--faucet-pubkey", faucet ); - ADDU( "--faucet-lamports", 500000000000000000 ); - ADD( "--hashes-per-tick", "auto" ); + ADD( "--hashes-per-tick", "sleep" ); + ADDU( "--faucet-lamports", 500000000000000000UL ); + ADD( "--bootstrap-validator", config->consensus.identity_path ); ADD1( vote ); ADD1( stake ); + ADD( "--ledger", config->ledger.path ); ADD( "--cluster-type", "development" ); /* these are copied out of the output of `solana/fetch-spl.sh` ... need to @@ -108,6 +103,42 @@ init( config_t * const config ) { if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) )); } +static void +rmtree( char * path ) { + DIR * dir = opendir( path ); + if( FD_UNLIKELY( !dir ) ) { + if( errno == ENOENT ) return; + FD_LOG_ERR(( "opendir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) )); + } + + struct dirent * entry; + errno = 0; + while(( entry = readdir( dir ) )) { + if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue; + + char path1[ PATH_MAX ]; + snprintf1( path1, PATH_MAX, "%s/%s", path, entry->d_name ); + + struct stat st; + if( FD_UNLIKELY( stat( path1, &st ) ) ) { + if( FD_LIKELY( errno == ENOENT ) ) continue; + FD_LOG_ERR(( "stat `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) )); + } + + if( FD_UNLIKELY( S_ISDIR( st.st_mode ) ) ) { + rmtree( path1 ); + } else { + if( FD_UNLIKELY( unlink( path1 ) && errno != ENOENT ) ) + FD_LOG_ERR(( "unlink `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) )); + } + } + + if( FD_UNLIKELY( errno ) ) FD_LOG_ERR(( "readdir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) )); + + if( FD_UNLIKELY( rmdir( path ) ) ) FD_LOG_ERR(( "rmdir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) )); + if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", path, errno, fd_io_strerror( errno ) )); +} + static void fini( config_t * const config ) { char path[ PATH_MAX ]; @@ -120,26 +151,23 @@ fini( config_t * const config ) { snprintf1( path, PATH_MAX, "%s/vote-account.json", config->scratch_directory ); if( FD_UNLIKELY( unlink( path ) && errno != ENOENT ) ) FD_LOG_ERR(( "could not remove cluster file `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) )); - snprintf1( path, PATH_MAX, "%s/genesis.bin", config->ledger.path ); - if( FD_UNLIKELY( unlink( path ) && errno != ENOENT ) ) - FD_LOG_ERR(( "could not remove cluster file `%s` (%i-%s)", path, errno, fd_io_strerror( errno ) )); + rmtree( config->ledger.path ); } static configure_result_t check( config_t * const config ) { - char faucet[ PATH_MAX ], stake[ PATH_MAX ], vote[ PATH_MAX ], genesis[ PATH_MAX ]; + char faucet[ PATH_MAX ], stake[ PATH_MAX ], vote[ PATH_MAX ]; snprintf1( faucet, PATH_MAX, "%s/faucet.json", config->scratch_directory ); snprintf1( stake, PATH_MAX, "%s/stake-account.json", config->scratch_directory ); snprintf1( vote, PATH_MAX, "%s/vote-account.json", config->scratch_directory ); - snprintf1( genesis, PATH_MAX, "%s/genesis.bin", config->ledger.path ); struct stat st; if( FD_UNLIKELY( stat( faucet, &st ) && errno == ENOENT && stat( stake, &st ) && errno == ENOENT && stat( vote, &st ) && errno == ENOENT && - stat( genesis, &st ) && errno == ENOENT ) ) - NOT_CONFIGURED( "faucet.json, stake-account.json, vote-account.json, and genesis.bin do not exist" ); + stat( config->ledger.path, &st ) && errno == ENOENT ) ) + NOT_CONFIGURED( "faucet.json, stake-account.json, vote-account.json, and `%s` do not exist", config->ledger.path ); CHECK( check_dir( config->ledger.path, config->uid, config->gid, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR ) ); CHECK( check_dir( config->scratch_directory, config->uid, config->gid, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR ) ); @@ -147,9 +175,8 @@ check( config_t * const config ) { CHECK( check_file( faucet, config->uid, config->gid, S_IFREG | S_IRUSR | S_IWUSR ) ); CHECK( check_file( stake, config->uid, config->gid, S_IFREG | S_IRUSR | S_IWUSR ) ); CHECK( check_file( vote, config->uid, config->gid, S_IFREG | S_IRUSR | S_IWUSR ) ); - CHECK( check_file( genesis, config->uid, config->gid, S_IFREG | S_IRUSR | S_IWUSR ) ); - CONFIGURE_OK(); + PARTIALLY_CONFIGURED( "genesis directory exists at `%s`", config->ledger.path ); } configure_stage_t cluster = { diff --git a/src/app/fddev/dev.c b/src/app/fddev/dev.c index e290d81770..905c981641 100644 --- a/src/app/fddev/dev.c +++ b/src/app/fddev/dev.c @@ -74,9 +74,6 @@ dev_cmd_fn( args_t * args, validator will get stuck forever. */ config->consensus.wait_for_vote_to_start_leader = 0; - config->consensus.genesis_fetch = 0; - config->consensus.snapshot_fetch = 0; - if( FD_LIKELY( !strcmp( config->consensus.vote_account_path, "" ) ) ) snprintf1( config->consensus.vote_account_path, sizeof( config->consensus.vote_account_path ), diff --git a/src/app/fddev/fddev.h b/src/app/fddev/fddev.h index a8858ee0a7..a8a2ccb840 100644 --- a/src/app/fddev/fddev.h +++ b/src/app/fddev/fddev.h @@ -26,4 +26,18 @@ void dev1_cmd_fn( args_t * args, config_t * const config ); +void +txn_cmd_perm( args_t * args, + security_t * security, + config_t * const config ); + +void +txn_cmd_args( int * pargc, + char *** pargv, + args_t * args); + +void +txn_cmd_fn( args_t * args, + config_t * const config ); + #endif /* HEADER_fd_src_app_fddev_fddev_h */ diff --git a/src/app/fddev/main.c b/src/app/fddev/main.c index e485c34360..d5d855c9ff 100644 --- a/src/app/fddev/main.c +++ b/src/app/fddev/main.c @@ -8,6 +8,7 @@ #include #include #include +#include configure_stage_t * STAGES[ CONFIGURE_STAGE_COUNT ] = { &netns, @@ -26,6 +27,7 @@ configure_stage_t * STAGES[ CONFIGURE_STAGE_COUNT ] = { static action_t DEV_ACTIONS[] = { { .name = "dev", .args = dev_cmd_args, .fn = dev_cmd_fn, .perm = dev_cmd_perm }, { .name = "dev1", .args = dev1_cmd_args, .fn = dev1_cmd_fn, .perm = dev_cmd_perm }, + { .name = "txn", .args = txn_cmd_args, .fn = txn_cmd_fn, .perm = txn_cmd_perm }, }; #define MAX_ARGC 32 @@ -67,9 +69,14 @@ execve_as_root( int argc, FD_LOG_ERR(( "execve(sudo) failed (%i-%s)", errno, fd_io_strerror( errno ) )); } +extern int * fd_log_private_shared_lock; + int main( int argc, char ** _argv ) { + fd_log_private_shared_lock = (int*)mmap( NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, (off_t)0 ); + if( FD_UNLIKELY( !fd_log_private_shared_lock ) ) exit(1); + /* save original arguments list in case we need to respawn the process as privileged */ int orig_argc = argc; diff --git a/src/app/fddev/txn.c b/src/app/fddev/txn.c new file mode 100644 index 0000000000..0bb8851d2e --- /dev/null +++ b/src/app/fddev/txn.c @@ -0,0 +1,193 @@ +#include "fddev.h" + +#include "../fdctl/configure/configure.h" +#include "../../ballet/base64/fd_base64.h" +#include "../../tango/quic/fd_quic.h" +#include "../../tango/quic/tests/fd_quic_test_helpers.h" + +#include + +FD_IMPORT_BINARY(sample_transaction, "src/tango/quic/tests/quic_txn.bin"); + +static int g_conn_hs_complete = 0; +static int g_conn_final = 0; +static int g_stream_notify = 0; + +#define MAX_TXN_COUNT 128 + +void +txn_cmd_perm( args_t * args, + security_t * security, + config_t * const config ) { + (void)args; + + if( FD_UNLIKELY( config->development.netns.enabled ) ) + check_cap( security, "txn", CAP_SYS_ADMIN, "enter a network namespace by calling `setns(2)`" ); +} + +void +txn_cmd_args( int * pargc, + char *** pargv, + args_t * args ) { + args->txn.payload_base64 = fd_env_strip_cmdline_cstr( pargc, pargv, "--payload-base64-encoded", NULL, NULL ); + args->txn.count = fd_env_strip_cmdline_ulong( pargc, pargv, "--count", NULL, 1 ); + if( FD_UNLIKELY( !args->txn.count || args->txn.count > MAX_TXN_COUNT ) ) + FD_LOG_ERR(( "count must be between 1 and %d", MAX_TXN_COUNT )); + + args->txn.dst_ip = fd_env_strip_cmdline_cstr( pargc, pargv, "--dst-ip", NULL, 0 ); + args->txn.dst_port = fd_env_strip_cmdline_ushort( pargc, pargv, "--dst-port", NULL, 0 ); +} + +static ulong +cb_now( void * context ) { + (void)context; + return (ulong)fd_log_wallclock(); +} + +static void +cb_conn_hs_complete( fd_quic_conn_t * conn, + void * quic_ctx ) { + (void)conn; + (void)quic_ctx; + g_conn_hs_complete = 1; +} + +static void +cb_conn_final( fd_quic_conn_t * conn, + void * quic_ctx ) { + (void)conn; + (void)quic_ctx; + g_conn_final = 1; +} + +static void +cb_stream_notify( fd_quic_stream_t * stream, + void * stream_ctx, + int notify_type ) { + (void)stream; + (void)stream_ctx; + (void)notify_type; + g_stream_notify = 1; +} + +static void +send_quic_transactions( fd_quic_t * quic, + fd_quic_udpsock_t * udpsock, + ulong count, + uint dst_ip, + ushort dst_port, + fd_aio_pkt_info_t * pkt ) { + fd_quic_set_aio_net_tx( quic, udpsock->aio ); + FD_TEST( fd_quic_init( quic ) ); + + quic->cb.now = cb_now; + quic->cb.conn_final = cb_conn_final; + quic->cb.conn_hs_complete = cb_conn_hs_complete; + quic->cb.stream_notify = cb_stream_notify; + + fd_quic_conn_t * conn = fd_quic_connect( quic, dst_ip, dst_port, NULL ); + while ( FD_LIKELY( !( g_conn_hs_complete || g_conn_final ) ) ) { + fd_quic_service( quic ); + fd_quic_udpsock_service( udpsock ); + } + FD_TEST( conn ); + if( FD_UNLIKELY( conn->state != FD_QUIC_CONN_STATE_ACTIVE ) ) + FD_LOG_ERR(( "unable to connect to QUIC endpoint on port %hu, is it running? state is %d", dst_port, conn->state )); + + fd_quic_stream_t * stream = fd_quic_conn_new_stream( conn, FD_QUIC_TYPE_UNIDIR ); + FD_TEST( stream ); + + ulong sent = 0; + while( sent < count ) { + int res = fd_quic_stream_send( stream, pkt + sent, count - sent, 1 ); + if( FD_UNLIKELY( res < 0 ) ) FD_LOG_ERR(( "fd_quic_stream_send failed (%d)", res )); + sent += (ulong)res; + + fd_quic_service( quic ); + fd_quic_udpsock_service( udpsock ); + } + + while ( FD_UNLIKELY( !( g_stream_notify || g_conn_final ) ) ) { + fd_quic_service( quic ); + fd_quic_udpsock_service( udpsock ); + } + + fd_quic_conn_close( conn, 0 ); + fd_quic_fini( quic ); +} + +void +txn_cmd_fn( args_t * args, + config_t * const config ) { + if( FD_UNLIKELY( config->development.netns.enabled ) ) + enter_network_namespace( config->development.netns.interface1 ); + + fd_quic_limits_t quic_limits = { + .conn_cnt = 1UL, + .handshake_cnt = 1UL, + .conn_id_cnt = 4UL, + .conn_id_sparsity = 4.0, + .stream_cnt = { 0UL, // FD_QUIC_STREAM_TYPE_BIDI_CLIENT + 0UL, // FD_QUIC_STREAM_TYPE_BIDI_SERVER + 1UL, // FD_QUIC_STREAM_TYPE_UNI_CLIENT + 0UL }, // FD_QUIC_STREAM_TYPE_UNI_SERVER + .stream_sparsity = 4.0, + .inflight_pkt_cnt = 64UL, + .tx_buf_sz = 1UL<<15UL + }; + ulong quic_footprint = fd_quic_footprint( &quic_limits ); + FD_TEST( quic_footprint ); + + fd_wksp_t * wksp = fd_wksp_new_anonymous( fd_cstr_to_shmem_page_sz("normal"), + 1UL << 10, + fd_shmem_cpu_idx( 0 ), + "wksp", + 0UL ); + FD_TEST( wksp ); + void * mem = fd_wksp_alloc_laddr( wksp, fd_quic_align(), quic_footprint, 1UL ); + fd_quic_t * quic = fd_quic_new( mem, &quic_limits ); + FD_TEST( quic ); + + fd_quic_udpsock_t _udpsock; + fd_quic_udpsock_t * udpsock = fd_quic_client_create_udpsock( &_udpsock, wksp, fd_quic_get_aio_net_rx( quic ), 0 ); + FD_TEST( udpsock == &_udpsock ); + + fd_quic_config_t * client_cfg = &quic->config; + client_cfg->role = FD_QUIC_ROLE_CLIENT; + memcpy( client_cfg->alpns, "\xasolana-tpu", 11UL ); + client_cfg->alpns_sz = 11U; + memcpy( client_cfg->link.dst_mac_addr, config->tiles.quic.mac_addr, 6UL ); + client_cfg->net.ip_addr = udpsock->listen_ip; + client_cfg->net.ephem_udp_port.lo = (ushort)udpsock->listen_port; + client_cfg->net.ephem_udp_port.hi = (ushort)(udpsock->listen_port + 1); + client_cfg->initial_rx_max_stream_data = 1<<15; + client_cfg->idle_timeout = 100; + client_cfg->initial_rx_max_stream_data = FD_QUIC_DEFAULT_INITIAL_RX_MAX_STREAM_DATA; + + fd_aio_pkt_info_t pkt[ MAX_TXN_COUNT ]; + + if( FD_LIKELY( !args->txn.payload_base64 ) ) { + for( ulong i=0; itxn.count; i++ ) { + pkt[ i ].buf = (void * )sample_transaction; + pkt[ i ].buf_sz = (ushort )sample_transaction_sz; + } + } else { + uchar buf[1300]; + int buf_sz = fd_base64_decode( args->txn.payload_base64, buf ); + if( FD_UNLIKELY( buf_sz == -1 ) ) FD_LOG_ERR(( "bad payload input `%s`", args->txn.payload_base64 )); + for( ulong i=0; itxn.count; i++ ) { + pkt[ i ].buf = (void * )buf; + pkt[ i ].buf_sz = (ushort )buf_sz; + } + } + + uint dst_ip = config->tiles.quic.ip_addr; + if( FD_UNLIKELY( args->txn.dst_ip ) ) + if( FD_UNLIKELY( !fd_cstr_to_ip4_addr( args->txn.dst_ip, &dst_ip ) ) ) FD_LOG_ERR(( "invalid --dst-ip" )); + + ushort dst_port = config->tiles.quic.listen_port; + if( FD_UNLIKELY( args->txn.dst_port ) ) dst_port = args->txn.dst_port; + + send_quic_transactions( quic, udpsock, args->txn.count, dst_ip, dst_port, pkt ); + exit_group( 0 ); +} diff --git a/src/util/log/fd_log.c b/src/util/log/fd_log.c index dc61eaa63f..ac34e19e0e 100644 --- a/src/util/log/fd_log.c +++ b/src/util/log/fd_log.c @@ -516,9 +516,10 @@ fd_log_private_0( char const * fmt, ... ) { return fd_log_private_log_msg; } -/* Lock to sequence writes between different threads */ +/* Lock to sequence fprintf writes between different threads / processes */ -static int * fd_log_private_shared_lock = NULL; +static int fd_log_private_shared_lock_local[1] __attribute__((aligned(128))); /* 0 at start */ +int * fd_log_private_shared_lock; /* NULL at start */ void fd_log_private_fprintf_0( int fd, char const * fmt, ... ) { @@ -923,16 +924,11 @@ fd_log_private_boot( int * pargc, char buf[ FD_LOG_NAME_MAX ]; - fd_log_private_shared_lock = (int*)mmap( NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, (off_t)0 ); - - if( FD_UNLIKELY( !fd_log_private_shared_lock ) ) { - int lock = 0; - fd_log_private_shared_lock = &lock; - fd_log_private_fprintf_0( STDERR_FILENO, - "open( \"/dev/null\", O_WRONLY | O_APPEND ) failed (%i-%s); attempting to continue\n", - errno, fd_io_strerror( errno ) ); - exit(1); - } + /* If the user hasn't provided an alternate location for the IPC + locking, use a local location. Note at program start, the shared + lock local should already be zero. */ + + if( FD_LIKELY( !fd_log_private_shared_lock ) ) fd_log_private_shared_lock = fd_log_private_shared_lock_local; /* Init our our application logical ids */ /* FIXME: CONSIDER EXPLICIT SPECIFICATION OF RANGE OF THREADS