feat: Vendor pezkuwi-subxt and pezkuwi-zombienet-sdk into monorepo

- Add pezkuwi-subxt crates to vendor/pezkuwi-subxt
- Add pezkuwi-zombienet-sdk crates to vendor/pezkuwi-zombienet-sdk
- Convert git dependencies to path dependencies
- Add vendor crates to workspace members
- Remove test/example crates from vendor (not needed for SDK)
- Fix feature propagation issues detected by zepter
- Fix workspace inheritance for internal dependencies
- All 606 crates now in workspace
- All 6919 internal dependency links verified correct
- No git dependencies remaining
This commit is contained in:
2025-12-22 23:31:24 +03:00
parent 4c8f281051
commit 70ddb6516f
386 changed files with 76759 additions and 36 deletions
+37
View File
@@ -0,0 +1,37 @@
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Default platform for WASM environments.
#[cfg(feature = "web")]
mod wasm_helpers;
#[cfg(feature = "web")]
mod wasm_platform;
#[cfg(feature = "web")]
mod wasm_socket;
pub use helpers::{DefaultPlatform, build_platform};
#[cfg(feature = "native")]
mod helpers {
use smoldot_light::platform::default::DefaultPlatform as Platform;
use std::sync::Arc;
pub type DefaultPlatform = Arc<Platform>;
pub fn build_platform() -> DefaultPlatform {
Platform::new("subxt-light-client".into(), env!("CARGO_PKG_VERSION").into())
}
}
#[cfg(feature = "web")]
mod helpers {
use super::wasm_platform::SubxtPlatform as Platform;
pub type DefaultPlatform = Platform;
pub fn build_platform() -> DefaultPlatform {
Platform::new()
}
}
@@ -0,0 +1,42 @@
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
//! Wasm implementation for the light client's platform using
//! custom websockets.
use super::wasm_socket::WasmSocket;
use core::time::Duration;
use futures_util::{FutureExt, future};
pub fn now_from_unix_epoch() -> Duration {
web_time::SystemTime::now()
.duration_since(web_time::SystemTime::UNIX_EPOCH)
.unwrap_or_else(|_| {
panic!("Invalid systime cannot be configured earlier than `UNIX_EPOCH`")
})
}
pub type Instant = web_time::Instant;
pub fn now() -> Instant {
web_time::Instant::now()
}
pub type Delay = future::BoxFuture<'static, ()>;
pub fn sleep(duration: Duration) -> Delay {
futures_timer::Delay::new(duration).boxed()
}
/// Implementation detail of a stream from the `SubxtPlatform`.
#[pin_project::pin_project]
pub struct Stream(
#[pin]
pub smoldot::libp2p::with_buffers::WithBuffers<
future::BoxFuture<'static, Result<WasmSocket, std::io::Error>>,
WasmSocket,
Instant,
>,
);
@@ -0,0 +1,212 @@
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use super::wasm_socket::WasmSocket;
use core::{
fmt::{self, Write as _},
net::IpAddr,
time::Duration,
};
use futures::prelude::*;
use smoldot::libp2p::with_buffers;
use smoldot_light::platform::{
Address, ConnectionType, LogLevel, MultiStreamAddress, MultiStreamWebRtcConnection,
PlatformRef, SubstreamDirection,
};
use std::{io, net::SocketAddr, pin::Pin};
const LOG_TARGET: &str = "subxt-platform-wasm";
/// Subxt platform implementation for wasm.
///
/// This implementation is a conversion of the implementation from the smoldot:
/// https://github.com/smol-dot/smoldot/blob/6401d4df90569e23073d646b14a8fbf9f7e6bdd3/light-base/src/platform/default.rs#L83.
///
/// This platform will evolve over time and we'll need to keep this code in sync.
#[derive(Clone)]
pub struct SubxtPlatform {}
impl SubxtPlatform {
pub fn new() -> Self {
SubxtPlatform {}
}
}
impl PlatformRef for SubxtPlatform {
type Delay = super::wasm_helpers::Delay;
type Instant = super::wasm_helpers::Instant;
type MultiStream = std::convert::Infallible;
type Stream = super::wasm_helpers::Stream;
type StreamConnectFuture = future::Ready<Self::Stream>;
type MultiStreamConnectFuture = future::Pending<MultiStreamWebRtcConnection<Self::MultiStream>>;
type ReadWriteAccess<'a> = with_buffers::ReadWriteAccess<'a, Self::Instant>;
type StreamUpdateFuture<'a> = future::BoxFuture<'a, ()>;
type StreamErrorRef<'a> = &'a std::io::Error;
type NextSubstreamFuture<'a> = future::Pending<Option<(Self::Stream, SubstreamDirection)>>;
fn now_from_unix_epoch(&self) -> Duration {
super::wasm_helpers::now_from_unix_epoch()
}
fn now(&self) -> Self::Instant {
super::wasm_helpers::now()
}
fn fill_random_bytes(&self, buffer: &mut [u8]) {
// This could fail if the system does not have access to a good source of entropy.
// Note: `rand::RngCore::fill_bytes` also panics on errors and `rand::OsCore` calls
// identically into `getrandom::getrandom`.
getrandom::getrandom(buffer).expect("Cannot fill random bytes");
}
fn sleep(&self, duration: Duration) -> Self::Delay {
super::wasm_helpers::sleep(duration)
}
fn sleep_until(&self, when: Self::Instant) -> Self::Delay {
self.sleep(when.saturating_duration_since(self.now()))
}
fn spawn_task(
&self,
_task_name: std::borrow::Cow<'_, str>,
task: impl future::Future<Output = ()> + Send + 'static,
) {
wasm_bindgen_futures::spawn_local(task);
}
fn client_name(&self) -> std::borrow::Cow<'_, str> {
"subxt-light-client".into()
}
fn client_version(&self) -> std::borrow::Cow<'_, str> {
env!("CARGO_PKG_VERSION").into()
}
fn supports_connection_type(&self, connection_type: ConnectionType) -> bool {
let result = matches!(
connection_type,
ConnectionType::WebSocketIpv4 { .. } |
ConnectionType::WebSocketIpv6 { .. } |
ConnectionType::WebSocketDns { .. }
);
tracing::trace!(
target: LOG_TARGET,
"Supports connection type={:?} result={}",
connection_type, result
);
result
}
fn connect_stream(&self, multiaddr: Address) -> Self::StreamConnectFuture {
tracing::trace!(target: LOG_TARGET, "Connect stream to multiaddr={:?}", multiaddr);
// `PlatformRef` trait guarantees that `connect_stream` is only called with addresses
// stated in `supports_connection_type`.
let addr = match multiaddr {
Address::WebSocketDns { hostname, port, secure: true } => {
format!("wss://{hostname}:{port}")
},
Address::WebSocketDns { hostname, port, secure: false } => {
format!("ws://{hostname}:{port}")
},
Address::WebSocketIp { ip: IpAddr::V4(ip), port } => {
let addr = SocketAddr::from((ip, port));
format!("ws://{addr}")
},
Address::WebSocketIp { ip: IpAddr::V6(ip), port } => {
let addr = SocketAddr::from((ip, port));
format!("ws://{addr}")
},
// The API user of the `PlatformRef` trait is never supposed to open connections of
// a type that isn't supported.
_ => {
unreachable!(
"Connecting to an address not supported. This code path indicates a bug in smoldot. Please raise an issue at https://github.com/smol-dot/smoldot/issues"
)
},
};
let socket_future = async move {
tracing::debug!(target: LOG_TARGET, "Connecting to addr={addr}");
WasmSocket::new(addr.as_str()).map_err(|err| std::io::Error::other(err.to_string()))
};
future::ready(super::wasm_helpers::Stream(with_buffers::WithBuffers::new(Box::pin(
socket_future,
))))
}
fn connect_multistream(&self, _address: MultiStreamAddress) -> Self::MultiStreamConnectFuture {
panic!(
"Multistreams are not currently supported. This code path indicates a bug in smoldot. Please raise an issue at https://github.com/smol-dot/smoldot/issues"
)
}
fn open_out_substream(&self, c: &mut Self::MultiStream) {
// This function can only be called with so-called "multi-stream" connections. We never
// open such connection.
match *c {}
}
fn next_substream(&self, c: &'_ mut Self::MultiStream) -> Self::NextSubstreamFuture<'_> {
// This function can only be called with so-called "multi-stream" connections. We never
// open such connection.
match *c {}
}
fn read_write_access<'a>(
&self,
stream: Pin<&'a mut Self::Stream>,
) -> Result<Self::ReadWriteAccess<'a>, &'a io::Error> {
let stream = stream.project();
stream.0.read_write_access(Self::Instant::now())
}
fn wait_read_write_again<'a>(
&self,
stream: Pin<&'a mut Self::Stream>,
) -> Self::StreamUpdateFuture<'a> {
let stream = stream.project();
Box::pin(stream.0.wait_read_write_again(|when| async move {
let now = super::wasm_helpers::now();
let duration = when.saturating_duration_since(now);
super::wasm_helpers::sleep(duration).await;
}))
}
fn log<'a>(
&self,
log_level: LogLevel,
log_target: &'a str,
message: &'a str,
key_values: impl Iterator<Item = (&'a str, &'a dyn fmt::Display)>,
) {
let mut message_build = String::with_capacity(128);
message_build.push_str(message);
let mut first = true;
for (key, value) in key_values {
if first {
let _ = write!(message_build, "; ");
first = false;
} else {
let _ = write!(message_build, ", ");
}
let _ = write!(message_build, "{key}={value}");
}
match log_level {
LogLevel::Error => tracing::error!("target={log_target} {message_build}"),
LogLevel::Warn => tracing::warn!("target={log_target} {message_build}"),
LogLevel::Info => tracing::info!("target={log_target} {message_build}"),
LogLevel::Debug => tracing::debug!("target={log_target} {message_build}"),
LogLevel::Trace => tracing::trace!("target={log_target} {message_build}"),
};
}
}
@@ -0,0 +1,240 @@
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
// see LICENSE for license details.
use futures::{io, prelude::*};
use send_wrapper::SendWrapper;
use wasm_bindgen::{JsCast, prelude::*};
use std::{
collections::VecDeque,
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll, Waker},
};
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Failed to connect {0}")]
ConnectionError(String),
}
/// Websocket for WASM environments.
///
/// This is a rust-based wrapper around browser's WebSocket API.
// Warning: It is not safe to have `Clone` on this structure.
pub struct WasmSocket {
/// Inner data shared between `poll` and web_sys callbacks.
inner: Arc<Mutex<InnerWasmSocket>>,
/// This implements `Send` and panics if the value is accessed
/// or dropped from another thread.
///
/// This is safe in wasm environments.
socket: SendWrapper<web_sys::WebSocket>,
/// In memory callbacks to handle messages from the browser socket.
_callbacks: SendWrapper<Callbacks>,
}
/// The state of the [`WasmSocket`].
#[derive(PartialEq, Eq, Clone, Copy)]
enum ConnectionState {
/// Initial state of the socket.
Connecting,
/// Socket is fully opened.
Opened,
/// Socket is closed.
Closed,
/// Error reported by callbacks.
Error,
}
struct InnerWasmSocket {
/// The state of the connection.
state: ConnectionState,
/// Data buffer for the socket.
data: VecDeque<u8>,
/// Waker from `poll_read` / `poll_write`.
waker: Option<Waker>,
}
/// Registered callbacks of the [`WasmSocket`].
///
/// These need to be kept around until the socket is dropped.
type Callbacks = (
Closure<dyn FnMut()>,
Closure<dyn FnMut(web_sys::MessageEvent)>,
Closure<dyn FnMut(web_sys::Event)>,
Closure<dyn FnMut(web_sys::CloseEvent)>,
);
impl WasmSocket {
/// Establish a WebSocket connection.
///
/// The error is a string representing the browser error.
/// Visit [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#exceptions_thrown)
/// for more info.
pub fn new(addr: &str) -> Result<Self, Error> {
let socket = match web_sys::WebSocket::new(addr) {
Ok(socket) => socket,
Err(err) => return Err(Error::ConnectionError(format!("{err:?}"))),
};
socket.set_binary_type(web_sys::BinaryType::Arraybuffer);
let inner = Arc::new(Mutex::new(InnerWasmSocket {
state: ConnectionState::Connecting,
data: VecDeque::with_capacity(16384),
waker: None,
}));
let open_callback = Closure::<dyn FnMut()>::new({
let inner = inner.clone();
move || {
let mut inner = inner.lock().expect("Mutex is poised; qed");
inner.state = ConnectionState::Opened;
if let Some(waker) = inner.waker.take() {
waker.wake();
}
}
});
socket.set_onopen(Some(open_callback.as_ref().unchecked_ref()));
let message_callback = Closure::<dyn FnMut(_)>::new({
let inner = inner.clone();
move |event: web_sys::MessageEvent| {
let Ok(buffer) = event.data().dyn_into::<js_sys::ArrayBuffer>() else {
panic!("Unexpected data format {:?}", event.data());
};
let mut inner = inner.lock().expect("Mutex is poised; qed");
let bytes = js_sys::Uint8Array::new(&buffer).to_vec();
inner.data.extend(bytes);
if let Some(waker) = inner.waker.take() {
waker.wake();
}
}
});
socket.set_onmessage(Some(message_callback.as_ref().unchecked_ref()));
let error_callback = Closure::<dyn FnMut(_)>::new({
let inner = inner.clone();
move |_event: web_sys::Event| {
// Callback does not provide useful information, signal it back to the stream.
let mut inner = inner.lock().expect("Mutex is poised; qed");
inner.state = ConnectionState::Error;
if let Some(waker) = inner.waker.take() {
waker.wake();
}
}
});
socket.set_onerror(Some(error_callback.as_ref().unchecked_ref()));
let close_callback = Closure::<dyn FnMut(_)>::new({
let inner = inner.clone();
move |_event: web_sys::CloseEvent| {
let mut inner = inner.lock().expect("Mutex is poised; qed");
inner.state = ConnectionState::Closed;
if let Some(waker) = inner.waker.take() {
waker.wake();
}
}
});
socket.set_onclose(Some(close_callback.as_ref().unchecked_ref()));
let callbacks = (open_callback, message_callback, error_callback, close_callback);
Ok(Self {
inner,
socket: SendWrapper::new(socket),
_callbacks: SendWrapper::new(callbacks),
})
}
}
impl AsyncRead for WasmSocket {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<Result<usize, io::Error>> {
let mut inner = self.inner.lock().expect("Mutex is poised; qed");
inner.waker = Some(cx.waker().clone());
if self.socket.ready_state() == web_sys::WebSocket::CONNECTING {
return Poll::Pending;
}
match inner.state {
ConnectionState::Error => Poll::Ready(Err(io::Error::other("Socket error"))),
ConnectionState::Closed => Poll::Ready(Err(io::ErrorKind::BrokenPipe.into())),
ConnectionState::Connecting => Poll::Pending,
ConnectionState::Opened => {
if inner.data.is_empty() {
return Poll::Pending;
}
let n = inner.data.len().min(buf.len());
for k in buf.iter_mut().take(n) {
*k = inner.data.pop_front().expect("Buffer non empty; qed");
}
Poll::Ready(Ok(n))
},
}
}
}
impl AsyncWrite for WasmSocket {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<Result<usize, io::Error>> {
let mut inner = self.inner.lock().expect("Mutex is poised; qed");
inner.waker = Some(cx.waker().clone());
match inner.state {
ConnectionState::Error => Poll::Ready(Err(io::Error::other("Socket error"))),
ConnectionState::Closed => Poll::Ready(Err(io::ErrorKind::BrokenPipe.into())),
ConnectionState::Connecting => Poll::Pending,
ConnectionState::Opened => match self.socket.send_with_u8_array(buf) {
Ok(()) => Poll::Ready(Ok(buf.len())),
Err(err) => Poll::Ready(Err(io::Error::other(format!("Write error: {err:?}")))),
},
}
}
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
if self.socket.ready_state() == web_sys::WebSocket::CLOSED {
return Poll::Ready(Ok(()));
}
if self.socket.ready_state() != web_sys::WebSocket::CLOSING {
let _ = self.socket.close();
}
let mut inner = self.inner.lock().expect("Mutex is poised; qed");
inner.waker = Some(cx.waker().clone());
Poll::Pending
}
}
impl Drop for WasmSocket {
fn drop(&mut self) {
if self.socket.ready_state() != web_sys::WebSocket::CLOSING {
let _ = self.socket.close();
}
self.socket.set_onopen(None);
self.socket.set_onmessage(None);
self.socket.set_onerror(None);
self.socket.set_onclose(None);
}
}