mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 14:01:06 +00:00
Backup platform impl
Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
Generated
+30
-26
@@ -3195,12 +3195,12 @@ checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
|
||||
[[package]]
|
||||
name = "ruzstd"
|
||||
version = "0.4.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3ffab8f9715a0d455df4bbb9d21e91135aab3cd3ca187af0cd0c3c3f868fdc"
|
||||
checksum = "9a15e661f0f9dac21f3494fe5d23a6338c0ac116a2d22c2b63010acd89467ffe"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"thiserror-core",
|
||||
"thiserror",
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
@@ -3594,7 +3594,7 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "smoldot"
|
||||
version = "0.7.0"
|
||||
version = "0.6.0"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"async-lock",
|
||||
@@ -3646,7 +3646,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smoldot-light"
|
||||
version = "0.5.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"async-lock",
|
||||
"async-std",
|
||||
@@ -3666,11 +3666,30 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"siphasher",
|
||||
"slab",
|
||||
"smoldot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smoldot-light-wasm"
|
||||
version = "1.0.6"
|
||||
dependencies = [
|
||||
"async-executor",
|
||||
"async-task",
|
||||
"event-listener",
|
||||
"fnv",
|
||||
"futures-util",
|
||||
"hashbrown 0.13.2",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"nom",
|
||||
"pin-project",
|
||||
"rand 0.8.5",
|
||||
"slab",
|
||||
"smoldot",
|
||||
"smoldot-light",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "snow"
|
||||
version = "0.9.2"
|
||||
@@ -4201,8 +4220,10 @@ dependencies = [
|
||||
"either",
|
||||
"frame-metadata",
|
||||
"futures",
|
||||
"futures-timer",
|
||||
"futures-util",
|
||||
"getrandom 0.2.9",
|
||||
"gloo-net",
|
||||
"hex",
|
||||
"impl-serde",
|
||||
"jsonrpsee",
|
||||
@@ -4215,7 +4236,9 @@ dependencies = [
|
||||
"scale-value",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smoldot",
|
||||
"smoldot-light",
|
||||
"smoldot-light-wasm",
|
||||
"sp-core",
|
||||
"sp-core-hashing",
|
||||
"sp-keyring",
|
||||
@@ -4227,6 +4250,7 @@ dependencies = [
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
"wasm-bindgen-futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -4379,26 +4403,6 @@ dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-core"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d97345f6437bb2004cd58819d8a9ef8e36cdd7661c2abc4bbde0a7c40d9f497"
|
||||
dependencies = [
|
||||
"thiserror-core-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-core-impl"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10ac1c5050e43014d16b2f94d0d2ce79e65ffdd8b38d8048f9c8f6a8a6da62ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
|
||||
+8
-1
@@ -73,7 +73,14 @@ wasm-bindgen-test = "0.3.24"
|
||||
which = "4.4.0"
|
||||
|
||||
# Light client support:
|
||||
smoldot-light = { path = "/home/lexnv/workspace/smoldot/light-base", default-features = false, opt-level = 2 }
|
||||
smoldot = { path = "/home/lexnv/workspace/smoldot/lib", default-features = false }
|
||||
smoldot-light = { path = "/home/lexnv/workspace/smoldot/light-base", default-features = false }
|
||||
smoldot-light-wasm = { path = "/home/lexnv/workspace/smoldot/wasm-node/rust", default-features = false }
|
||||
wasm-bindgen-futures = { version = "0.4.19" }
|
||||
futures-timer = { version = "3" }
|
||||
gloo-net = { version = "0.2.6", default-features = false, features = ["json", "websocket"] }
|
||||
|
||||
|
||||
tokio-stream = "0.1.14"
|
||||
futures-util = "0.3.28"
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use futures::StreamExt;
|
||||
use sp_keyring::AccountKeyring;
|
||||
use std::sync::Arc;
|
||||
use subxt::{rpc::LightClient, tx::PairSigner, OnlineClient, PolkadotConfig};
|
||||
|
||||
// Generate an interface that we can use from the node's metadata.
|
||||
#[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")]
|
||||
pub mod polkadot {}
|
||||
// // Generate an interface that we can use from the node's metadata.
|
||||
// #[subxt::subxt(runtime_metadata_path = "../artifacts/polkadot_metadata_small.scale")]
|
||||
// pub mod polkadot {}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -48,25 +49,41 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let light_client = LightClient::new(include_str!("../../artifacts/dev_spec.json"))?;
|
||||
let api = OnlineClient::<PolkadotConfig>::from_rpc_client(Arc::new(light_client)).await?;
|
||||
|
||||
// Build a balance transfer extrinsic.
|
||||
let dest = AccountKeyring::Bob.to_account_id().into();
|
||||
let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
|
||||
{
|
||||
println!("Subscribe to latest finalized blocks: ");
|
||||
|
||||
// Submit the balance transfer extrinsic from Alice, and wait for it to be successful
|
||||
// and in a finalized block. We get back the extrinsic events if all is well.
|
||||
let from = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
let events = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&balance_transfer_tx, &from)
|
||||
.await?
|
||||
.wait_for_finalized_success()
|
||||
.await?;
|
||||
let mut blocks_sub = api.blocks().subscribe_finalized().await?.take(3);
|
||||
// For each block, print a bunch of information about it:
|
||||
while let Some(block) = blocks_sub.next().await {
|
||||
let block = block?;
|
||||
|
||||
// Find a Transfer event and print it.
|
||||
let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
|
||||
if let Some(event) = transfer_event {
|
||||
println!("Balance transfer success: {event:?}");
|
||||
let block_number = block.header().number;
|
||||
let block_hash = block.hash();
|
||||
|
||||
println!("Block #{block_number}:");
|
||||
println!(" Hash: {block_hash}");
|
||||
}
|
||||
}
|
||||
|
||||
// Build a balance transfer extrinsic.
|
||||
// let dest = AccountKeyring::Bob.to_account_id().into();
|
||||
// let balance_transfer_tx = polkadot::tx().balances().transfer(dest, 10_000);
|
||||
|
||||
// // Submit the balance transfer extrinsic from Alice, and wait for it to be successful
|
||||
// // and in a finalized block. We get back the extrinsic events if all is well.
|
||||
// let from = PairSigner::new(AccountKeyring::Alice.pair());
|
||||
// let events = api
|
||||
// .tx()
|
||||
// .sign_and_submit_then_watch_default(&balance_transfer_tx, &from)
|
||||
// .await?
|
||||
// .wait_for_finalized_success()
|
||||
// .await?;
|
||||
|
||||
// // Find a Transfer event and print it.
|
||||
// let transfer_event = events.find_first::<polkadot::balances::events::Transfer>()?;
|
||||
// if let Some(event) = transfer_event {
|
||||
// println!("Balance transfer success: {event:?}");
|
||||
// }
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -43,11 +43,16 @@ unstable-metadata = []
|
||||
# Activate this to expose the Light Client functionality.
|
||||
# Note that this feature is experimental and things may break or not work as expected.
|
||||
unstable-light-client = [
|
||||
"smoldot",
|
||||
"smoldot-light/std",
|
||||
"smoldot-light-wasm",
|
||||
"tokio-stream",
|
||||
"tokio/sync",
|
||||
"tokio/rt",
|
||||
"futures-util",
|
||||
"wasm-bindgen-futures",
|
||||
"futures-timer/wasm-bindgen",
|
||||
"gloo-net",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
@@ -88,7 +93,14 @@ subxt-macro = { workspace = true }
|
||||
subxt-metadata = { workspace = true }
|
||||
|
||||
# Light client support:
|
||||
smoldot = { workspace = true, optional = true }
|
||||
smoldot-light = { workspace = true, optional = true }
|
||||
smoldot-light-wasm = { workspace = true, optional = true }
|
||||
wasm-bindgen-futures = { workspace = true, optional = true }
|
||||
futures-timer = { workspace = true, optional = true }
|
||||
gloo-net = { workspace = true, optional = true }
|
||||
|
||||
|
||||
tokio = { workspace = true, optional = true }
|
||||
tokio-stream = { workspace = true, optional = true }
|
||||
futures-util = { workspace = true, optional = true }
|
||||
|
||||
@@ -32,6 +32,8 @@ use std::{
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
|
||||
use smoldot_light_wasm::platform::Platform as WasmPlatform;
|
||||
|
||||
const LOG_TARGET: &str = "light-client";
|
||||
|
||||
/// Inner structure to work with light clients.
|
||||
@@ -39,7 +41,7 @@ struct LightClientInner {
|
||||
/// Smoldot light client implementation that leverages the `AsyncStdTcpWebSocket`.
|
||||
///
|
||||
/// Note: `AsyncStdTcpWebSocket` is not wasm compatible.
|
||||
client: smoldot_light::Client<AsyncStdTcpWebSocket>,
|
||||
client: smoldot_light::Client<WasmPlatform>,
|
||||
/// The ID of the chain used to identify the chain protocol (ie. substrate).
|
||||
///
|
||||
/// Note: A single chain is supported for a client. This aligns with the subxt's
|
||||
@@ -129,6 +131,261 @@ impl LightClientInner {
|
||||
}
|
||||
}
|
||||
|
||||
/// Must stop the execution immediately. The message is a UTF-8 string found in the memory of
|
||||
/// the WebAssembly at offset `message_ptr` and with length `message_len`.
|
||||
///
|
||||
/// > **Note**: This function is typically implemented using `throw`.
|
||||
///
|
||||
/// After this function has been called, no further Wasm functions must be called again on
|
||||
/// this Wasm virtual machine. Explanation below.
|
||||
///
|
||||
/// # About throwing and safety
|
||||
///
|
||||
/// Rust programs can be configured in two panicking modes: `abort`, or `unwind`. Safe or
|
||||
/// unsafe Rust code must be written by keeping in mind that the execution of a function can
|
||||
/// be suddenly interrupted by a panic, but can rely on the fact that this panic will either
|
||||
/// completely abort the program, or unwind the stack. In the latter case, they can rely on
|
||||
/// the fact that `std::panic::catch_unwind` will catch this unwinding and let them perform
|
||||
/// some additional clean-ups.
|
||||
///
|
||||
/// This function is typically implemented using `throw`. However, "just" throwing a JavaScript
|
||||
/// exception from within the implementation of this function is neither `abort`, because the
|
||||
/// JavaScript could call into the Wasm again later, nor `unwind`, because it isn't caught by
|
||||
/// `std::panic::catch_unwind`. By being neither of the two, it breaks the assumptions that
|
||||
/// some Rust codes might rely on for either correctness or safety.
|
||||
/// In order to solve this problem, we enforce that `panic` must behave like `abort`, and
|
||||
/// forbid calling into the Wasm virtual machine again.
|
||||
///
|
||||
/// Beyond the `panic` function itself, any other FFI function that throws must similarly
|
||||
/// behave like `abort` and prevent any further execution.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn panic(message_ptr: u32, message_len: u32) {
|
||||
let slice =
|
||||
unsafe { std::slice::from_raw_parts(message_ptr as *const u8, message_len as usize) };
|
||||
if let Ok(message) = std::str::from_utf8(slice) {
|
||||
panic!("{message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Copies the entire content of the buffer with the given index to the memory of the
|
||||
/// WebAssembly at offset `target_pointer`.
|
||||
///
|
||||
/// In situations where a buffer must be provided from the JavaScript to the Rust code, the
|
||||
/// JavaScript must (prior to calling the Rust function that requires the buffer) assign a
|
||||
/// "buffer index" to the buffer it wants to provide. The Rust code then calls the
|
||||
/// [`buffer_size`] and [`buffer_copy`] functions in order to obtain the length and content
|
||||
/// of the buffer.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn buffer_copy(buffer_index: u32, target_pointer: u32) {}
|
||||
|
||||
/// Returns the size (in bytes) of the buffer with the given index.
|
||||
///
|
||||
/// See the documentation of [`buffer_copy`] for context.
|
||||
#[no_mangle]
|
||||
|
||||
pub extern "C" fn buffer_size(buffer_index: u32) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
/// The queue of JSON-RPC responses of the given chain is no longer empty.
|
||||
///
|
||||
/// This function is only ever called after [`json_rpc_responses_peek`] has returned a `len`
|
||||
/// of 0.
|
||||
///
|
||||
/// This function might be called spuriously, however this behavior must not be relied upon.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn json_rpc_responses_non_empty(chain_id: u32) {}
|
||||
|
||||
/// Client is emitting a log entry.
|
||||
///
|
||||
/// Each log entry is made of a log level (`1 = Error, 2 = Warn, 3 = Info, 4 = Debug,
|
||||
/// 5 = Trace`), a log target (e.g. "network"), and a log message.
|
||||
///
|
||||
/// The log target and message is a UTF-8 string found in the memory of the WebAssembly
|
||||
/// virtual machine at offset `ptr` and with length `len`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn log(
|
||||
level: u32,
|
||||
target_ptr: u32,
|
||||
target_len: u32,
|
||||
message_ptr: u32,
|
||||
message_len: u32,
|
||||
) {
|
||||
let target_slice =
|
||||
unsafe { std::slice::from_raw_parts(target_ptr as *const u8, target_len as usize) };
|
||||
let target = std::str::from_utf8(target_slice).unwrap_or_else(|_| "cannot decode target");
|
||||
|
||||
let msg_slice =
|
||||
unsafe { std::slice::from_raw_parts(message_ptr as *const u8, message_len as usize) };
|
||||
let message = std::str::from_utf8(msg_slice).unwrap_or_else(|_| "cannot decode message");
|
||||
|
||||
println!("log level={level} target={target} message={message}");
|
||||
}
|
||||
|
||||
/// Called when [`advance_execution`] should be executed again.
|
||||
///
|
||||
/// This function might be called from within [`advance_execution`], in which case
|
||||
/// [`advance_execution`] should be called again immediately after it returns.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn advance_execution_ready() {}
|
||||
|
||||
/// After at least `milliseconds` milliseconds have passed, [`timer_finished`] must be called.
|
||||
///
|
||||
/// It is not a logic error to call [`timer_finished`] *before* `milliseconds` milliseconds
|
||||
/// have passed, and this will likely cause smoldot to restart a new timer for the remainder
|
||||
/// of the duration.
|
||||
///
|
||||
/// When [`timer_finished`] is called, the value of the monotonic clock (in the WASI bindings)
|
||||
/// must have increased by at least the given number of `milliseconds`.
|
||||
///
|
||||
/// If `milliseconds` is 0, [`timer_finished`] should be called as soon as possible.
|
||||
///
|
||||
/// `milliseconds` never contains a negative number, `NaN` or infinite.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn start_timer(milliseconds: f64) {}
|
||||
|
||||
/// Must initialize a new connection that tries to connect to the given multiaddress.
|
||||
///
|
||||
/// The multiaddress is a UTF-8 string found in the WebAssembly memory at offset `addr_ptr`
|
||||
/// and with `addr_len` bytes. The string is a multiaddress such as `/ip4/1.2.3.4/tcp/5/ws`.
|
||||
///
|
||||
/// The `id` parameter is an identifier for this connection, as chosen by the Rust code. It
|
||||
/// must be passed on every interaction with this connection.
|
||||
///
|
||||
/// Returns 0 to indicate success, or 1 to indicate that an error happened. If an error is
|
||||
/// returned, the `id` doesn't correspond to anything.
|
||||
///
|
||||
/// > **Note**: If you implement this function using for example `new WebSocket()`, please
|
||||
/// > keep in mind that exceptions should be caught and turned into an error code.
|
||||
///
|
||||
/// If an error happened, assign a so-called "buffer index" (a `u32`) representing the buffer
|
||||
/// containing the UTF-8 error message, then write this buffer index as little-endian to the
|
||||
/// memory of the WebAssembly indicated by `error_buffer_index_ptr`. The Rust code will call
|
||||
/// [`buffer_size`] and [`buffer_copy`] in order to obtain the content of this buffer. The
|
||||
/// buffer index should remain assigned and buffer alive until the next time the JavaScript
|
||||
/// code retains control. Then, write at location `error_buffer_index_ptr + 4` a `1` if the
|
||||
/// error is caused by the address being forbidden or unsupported, and `0` otherwise. If no
|
||||
/// error happens, nothing should be written to `error_buffer_index_ptr`.
|
||||
///
|
||||
/// At any time, a connection can be in one of the three following states:
|
||||
///
|
||||
/// - `Opening` (initial state)
|
||||
/// - `Open`
|
||||
/// - `Reset`
|
||||
///
|
||||
/// When in the `Opening` or `Open` state, the connection can transition to the `Reset` state
|
||||
/// if the remote closes the connection or refuses the connection altogether. When that
|
||||
/// happens, [`connection_reset`] must be called. Once in the `Reset` state, the connection
|
||||
/// cannot transition back to another state.
|
||||
///
|
||||
/// Initially in the `Opening` state, the connection can transition to the `Open` state if the
|
||||
/// remote accepts the connection. When that happens, [`connection_open_single_stream`] or
|
||||
/// [`connection_open_multi_stream`] must be called.
|
||||
///
|
||||
/// There exists two kind of connections: single-stream and multi-stream. Single-stream
|
||||
/// connections are assumed to have a single stream open at all time and the encryption and
|
||||
/// multiplexing are handled internally by smoldot. Multi-stream connections open and close
|
||||
/// streams over time using [`connection_stream_opened`] and [`stream_reset`], and the
|
||||
/// encryption and multiplexing are handled by the user of these bindings.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn connection_new(
|
||||
id: u32,
|
||||
addr_ptr: u32,
|
||||
addr_len: u32,
|
||||
error_buffer_index_ptr: u32,
|
||||
) -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
/// Abruptly close a connection previously initialized with [`connection_new`].
|
||||
///
|
||||
/// This destroys the identifier passed as parameter. This identifier must never be passed
|
||||
/// through the FFI boundary, unless the same identifier is later allocated again with
|
||||
/// [`connection_new`].
|
||||
///
|
||||
/// Must never be called if [`connection_reset`] has been called on that object in the past.
|
||||
///
|
||||
/// The connection must be closed in the background. The Rust code isn't interested in incoming
|
||||
/// messages from this connection anymore.
|
||||
///
|
||||
/// > **Note**: In JavaScript, remember to unregister event handlers before calling for
|
||||
/// > example `WebSocket.close()`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn reset_connection(id: u32) {}
|
||||
|
||||
/// Queues a new outbound substream opening. The [`connection_stream_opened`] function must
|
||||
/// later be called when the substream has been successfully opened.
|
||||
///
|
||||
/// This function will only be called for multi-stream connections. The connection must
|
||||
/// currently be in the `Open` state. See the documentation of [`connection_new`] for details.
|
||||
///
|
||||
/// > **Note**: No mechanism exists in this API to handle the situation where a substream fails
|
||||
/// > to open, as this is not supposed to happen. If you need to handle such a
|
||||
/// > situation, either try again opening a substream again or reset the entire
|
||||
/// > connection.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn connection_stream_open(connection_id: u32) {}
|
||||
|
||||
/// Abruptly closes an existing substream of a multi-stream connection. The substream must
|
||||
/// currently be in the `Open` state.
|
||||
///
|
||||
/// Must never be called if [`stream_reset`] has been called on that object in the past.
|
||||
///
|
||||
/// This function will only be called for multi-stream connections. The connection must
|
||||
/// currently be in the `Open` state. See the documentation of [`connection_new`] for details.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn connection_stream_reset(connection_id: u32, stream_id: u32) {}
|
||||
|
||||
/// Queues data on the given stream. The data is found in the memory of the WebAssembly
|
||||
/// virtual machine, at the given pointer.
|
||||
///
|
||||
/// If `connection_id` is a single-stream connection, then the value of `stream_id` should
|
||||
/// be ignored. If `connection_id` is a multi-stream connection, then the value of `stream_id`
|
||||
/// contains the identifier of the stream on which to send the data, as was provided to
|
||||
/// [`connection_stream_opened`].
|
||||
///
|
||||
/// The connection associated with that stream (and, in the case of a multi-stream connection,
|
||||
/// the stream itself must currently be in the `Open` state. See the documentation of
|
||||
/// [`connection_new`] for details.
|
||||
///
|
||||
/// The size of the buffer must not exceed the number of writable bytes of the given stream.
|
||||
/// Use [`stream_writable_bytes`] to notify that more data can be sent on the stream.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn stream_send(connection_id: u32, stream_id: u32, ptr: u32, len: u32) {}
|
||||
|
||||
/// Close the sending side of the given stream of the given connection.
|
||||
///
|
||||
/// Never called for connection types where this isn't possible to implement (i.e. WebSocket
|
||||
/// and WebRTC at the moment).
|
||||
///
|
||||
/// If `connection_id` is a single-stream connection, then the value of `stream_id` should
|
||||
/// be ignored. If `connection_id` is a multi-stream connection, then the value of `stream_id`
|
||||
/// contains the identifier of the stream whose sending side should be closed, as was provided
|
||||
/// to [`connection_stream_opened`].
|
||||
///
|
||||
/// The connection associated with that stream (and, in the case of a multi-stream connection,
|
||||
/// the stream itself must currently be in the `Open` state. See the documentation of
|
||||
/// [`connection_new`] for details.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn stream_send_close(connection_id: u32, stream_id: u32) {}
|
||||
|
||||
/// Called when the Wasm execution enters the context of a certain task. This is useful for
|
||||
/// debugging purposes.
|
||||
///
|
||||
/// Only one task can be currently executing at any time.
|
||||
///
|
||||
/// The name of the task is a UTF-8 string found in the memory of the WebAssembly virtual
|
||||
/// machine at offset `ptr` and with length `len`.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn current_task_entered(ptr: u32, len: u32) {}
|
||||
|
||||
/// Called when the Wasm execution leave the context of a certain task. This is useful for
|
||||
/// debugging purposes.
|
||||
///
|
||||
/// Only one task can be currently executing at any time.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn current_task_exit() {}
|
||||
|
||||
/// The LightClient RPC offers a slightly different RPC methods than the
|
||||
/// substrate based chains. This is because the light client only exposes
|
||||
/// a small subset of the RPCs needed for basic functionality.
|
||||
@@ -181,10 +438,14 @@ impl LightClient {
|
||||
pub fn new(chain_spec: &str) -> Result<LightClient, Error> {
|
||||
tracing::trace!(target: LOG_TARGET, "Create light client");
|
||||
|
||||
let mut client = smoldot_light::Client::new(AsyncStdTcpWebSocket::new(
|
||||
env!("CARGO_PKG_NAME").into(),
|
||||
env!("CARGO_PKG_VERSION").into(),
|
||||
));
|
||||
// let platform = AsyncStdTcpWebSocket::new(
|
||||
// env!("CARGO_PKG_NAME").into(),
|
||||
// env!("CARGO_PKG_VERSION").into(),
|
||||
// );
|
||||
|
||||
let platform = WasmPlatform::new();
|
||||
|
||||
let mut client = smoldot_light::Client::new(platform);
|
||||
|
||||
let smoldot_light::AddChainSuccess {
|
||||
chain_id,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
mod background;
|
||||
mod client;
|
||||
mod platform;
|
||||
|
||||
pub use client::LightClient;
|
||||
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
use futures_timer::Delay;
|
||||
use futures_util::{future, FutureExt};
|
||||
|
||||
use gloo_net::websocket::{futures::WebSocket, Message, WebSocketError};
|
||||
use smoldot::libp2p::multiaddr::{Multiaddr, ProtocolRef};
|
||||
|
||||
use futures_util::stream::{SplitSink, SplitStream, StreamExt};
|
||||
|
||||
use futures_util::lock::Mutex;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
io::IoSlice,
|
||||
net::{IpAddr, SocketAddr},
|
||||
};
|
||||
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Platform {}
|
||||
|
||||
impl Platform {
|
||||
pub const fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl smoldot_light::platform::PlatformRef for Platform {
|
||||
type Delay = future::BoxFuture<'static, ()>;
|
||||
|
||||
// No-op yielding.
|
||||
type Yield = future::Ready<()>;
|
||||
|
||||
type Instant = std::time::Instant;
|
||||
|
||||
type Connection = std::convert::Infallible;
|
||||
|
||||
type Stream = ConnectionSocket;
|
||||
|
||||
type ConnectFuture = future::BoxFuture<'static, Result<ConnectionSocket, ConnectError>>;
|
||||
|
||||
type StreamUpdateFuture<'a> = future::BoxFuture<'a, ()>;
|
||||
|
||||
type NextSubstreamFuture<'a> = future::Pending<()>;
|
||||
|
||||
fn now_from_unix_epoch(&self) -> std::time::Duration {
|
||||
std::time::UNIX_EPOCH
|
||||
.elapsed()
|
||||
.expect("Invalid systime cannot be configured earlier than `UNIX_EPOCH`")
|
||||
}
|
||||
|
||||
fn now(&self) -> Self::Instant {
|
||||
std::time::Instant::now()
|
||||
}
|
||||
|
||||
fn sleep(&self, duration: std::time::Duration) -> Self::Delay {
|
||||
futures_timer::Delay::new(duration).boxed()
|
||||
}
|
||||
|
||||
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: futures_util::future::BoxFuture<'static, ()>,
|
||||
) {
|
||||
println!("Spawning {task_name}");
|
||||
wasm_bindgen_futures::spawn_local(task)
|
||||
}
|
||||
|
||||
fn client_name(&self) -> std::borrow::Cow<str> {
|
||||
"subxt".into()
|
||||
}
|
||||
|
||||
fn client_version(&self) -> std::borrow::Cow<str> {
|
||||
env!("CARGO_PKG_VERSION").into()
|
||||
}
|
||||
|
||||
fn yield_after_cpu_intensive(&self) -> Self::Yield {
|
||||
future::ready(())
|
||||
}
|
||||
|
||||
fn connect(&self, url: &str) -> Self::ConnectFuture {
|
||||
Box::pin(async move {
|
||||
let multiaddr = url.parse::<Multiaddr>().map_err(|_| ConnectError {
|
||||
message: format!("Address {url} is not a valid multiaddress"),
|
||||
is_bad_addr: true,
|
||||
})?;
|
||||
|
||||
// First two protocals must be valid, the third one is optional.
|
||||
let mut proto_iter = multiaddr.iter().fuse();
|
||||
|
||||
let addr = match (
|
||||
proto_iter.next().ok_or(ConnectError {
|
||||
message: format!("Unknown protocol combination"),
|
||||
is_bad_addr: true,
|
||||
})?,
|
||||
proto_iter.next().ok_or(ConnectError {
|
||||
message: format!("Unknown protocol combination"),
|
||||
is_bad_addr: true,
|
||||
})?,
|
||||
proto_iter.next(),
|
||||
) {
|
||||
(ProtocolRef::Ip4(ip), ProtocolRef::Tcp(port), None) => {
|
||||
SocketAddr::new(IpAddr::V4((ip).into()), port)
|
||||
}
|
||||
(ProtocolRef::Ip6(ip), ProtocolRef::Tcp(port), None) => {
|
||||
SocketAddr::new(IpAddr::V6((ip).into()), port)
|
||||
}
|
||||
(ProtocolRef::Ip4(ip), ProtocolRef::Tcp(port), Some(ProtocolRef::Ws)) => {
|
||||
SocketAddr::new(IpAddr::V4((ip).into()), port)
|
||||
}
|
||||
(ProtocolRef::Ip6(ip), ProtocolRef::Tcp(port), Some(ProtocolRef::Ws)) => {
|
||||
SocketAddr::new(IpAddr::V6((ip).into()), port)
|
||||
}
|
||||
// // TODO: we don't care about the differences between Dns, Dns4, and Dns6
|
||||
// (
|
||||
// ProtocolRef::Dns(addr) | ProtocolRef::Dns4(addr) | ProtocolRef::Dns6(addr),
|
||||
// ProtocolRef::Tcp(port),
|
||||
// None,
|
||||
// ) => (either::Right((addr.to_string(), *port)), None),
|
||||
// (
|
||||
// ProtocolRef::Dns(addr) | ProtocolRef::Dns4(addr) | ProtocolRef::Dns6(addr),
|
||||
// ProtocolRef::Tcp(port),
|
||||
// Some(ProtocolRef::Ws),
|
||||
// ) => (
|
||||
// either::Right((addr.to_string(), *port)),
|
||||
// Some(format!("{}:{}", addr, *port)),
|
||||
// ),
|
||||
_ => {
|
||||
return Err(ConnectError {
|
||||
is_bad_addr: true,
|
||||
message: "Unknown protocols combination".to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: use `addr` instead.
|
||||
let websocket = WebSocket::open(url.as_ref()).map_err(|e| ConnectError {
|
||||
is_bad_addr: false,
|
||||
message: "Cannot stablish WebSocket connection".to_string(),
|
||||
})?;
|
||||
|
||||
let (to_sender, from_sender) = mpsc::channel(1024);
|
||||
let (to_receiver, from_receiver) = mpsc::channel(1024);
|
||||
|
||||
// Note: WebSocket is not `Send`, work around that with a spawned task.
|
||||
|
||||
wasm_bindgen_futures::spawn_local(async move {
|
||||
let backend_event = tokio_stream::wrappers::ReceiverStream::new(from_sender);
|
||||
|
||||
let rpc_responses_event =
|
||||
futures_util::stream::unfold(rpc_responses, |mut rpc_responses| async {
|
||||
rpc_responses
|
||||
.next()
|
||||
.await
|
||||
.map(|result| (result, rpc_responses))
|
||||
});
|
||||
|
||||
tokio::pin!(backend_event, rpc_responses_event);
|
||||
|
||||
let mut backend_event_fut = backend_event.next();
|
||||
let mut rpc_responses_fut = rpc_responses_event.next();
|
||||
|
||||
loop {
|
||||
match future::select(backend_event_fut, rpc_responses_fut).await {
|
||||
// Message received from the backend: user registered.
|
||||
Either::Left((backend_value, previous_fut)) => {
|
||||
let Some(message) = backend_value else {
|
||||
tracing::trace!(target: LOG_TARGET, "Frontend channel closed");
|
||||
break;
|
||||
};
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
"Received register message {:?}",
|
||||
message
|
||||
);
|
||||
|
||||
self.handle_register(message).await;
|
||||
|
||||
backend_event_fut = backend_event.next();
|
||||
rpc_responses_fut = previous_fut;
|
||||
}
|
||||
// Message received from rpc handler: lightclient response.
|
||||
Either::Right((response, previous_fut)) => {
|
||||
// Smoldot returns `None` if the chain has been removed (which subxt does not remove).
|
||||
let Some(response) = response else {
|
||||
tracing::trace!(target: LOG_TARGET, "Smoldot RPC responses channel closed");
|
||||
break;
|
||||
};
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
"Received smoldot RPC result {:?}",
|
||||
response
|
||||
);
|
||||
|
||||
self.handle_rpc_response(response).await;
|
||||
|
||||
// Advance backend, save frontend.
|
||||
backend_event_fut = previous_fut;
|
||||
rpc_responses_fut = rpc_responses_event.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let (sender, receiver) = websocket.split();
|
||||
Ok(ConnectionSocket { sender, receiver })
|
||||
})
|
||||
}
|
||||
|
||||
fn open_out_substream(&self, _connection: &mut Self::Connection) {
|
||||
// Called from MultiStream connections that are not supported.
|
||||
}
|
||||
|
||||
fn next_substream<'a>(
|
||||
&self,
|
||||
connection: &'a mut Self::Connection,
|
||||
) -> Self::NextSubstreamFuture<'a> {
|
||||
// Called from MultiStream connections that are not supported.
|
||||
}
|
||||
|
||||
fn update_stream<'a>(&self, stream: &'a mut Self::Stream) -> Self::StreamUpdateFuture<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn read_buffer<'a>(
|
||||
&self,
|
||||
stream: &'a mut Self::Stream,
|
||||
) -> smoldot_light::platform::ReadBuffer<'a> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn advance_read_cursor(&self, stream: &mut Self::Stream, bytes: usize) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn writable_bytes(&self, stream: &mut Self::Stream) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn send(&self, stream: &mut Self::Stream, data: &[u8]) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn close_send(&self, stream: &mut Self::Stream) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Error potentially returned by [`PlatformRef::connect`].
|
||||
pub struct ConnectError {
|
||||
/// Human-readable error message.
|
||||
pub message: String,
|
||||
/// `true` if the error is caused by the address to connect to being forbidden or unsupported.
|
||||
pub is_bad_addr: bool,
|
||||
}
|
||||
|
||||
pub struct ConnectionSocket {
|
||||
sender: SplitSink<WebSocket, Message>,
|
||||
receiver: SSplitStream<WebSocket>,
|
||||
}
|
||||
Generated
+8
-8
@@ -988,18 +988,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.58"
|
||||
version = "1.0.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
|
||||
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.27"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
|
||||
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1379,9 +1379,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sp-core-hashing"
|
||||
version = "8.0.0"
|
||||
version = "9.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27449abdfbe41b473e625bce8113745e81d65777dd1d5a8462cf24137930dad8"
|
||||
checksum = "2ee599a8399448e65197f9a6cee338ad192e9023e35e31f22382964c3c174c68"
|
||||
dependencies = [
|
||||
"blake2b_simd",
|
||||
"byteorder",
|
||||
@@ -1394,9 +1394,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sp-std"
|
||||
version = "7.0.0"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1de8eef39962b5b97478719c493bed2926cf70cb621005bbf68ebe58252ff986"
|
||||
checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
|
||||
@@ -12,4 +12,4 @@ wasm-bindgen-test = "0.3.24"
|
||||
tracing-wasm = "0.2.1"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
serde_json = "1"
|
||||
subxt = { path = "../../subxt", default-features = false, features = ["jsonrpsee-web"] }
|
||||
subxt = { path = "../../subxt", default-features = false, features = ["jsonrpsee-web", "unstable-light-client"] }
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
use subxt::config::PolkadotConfig;
|
||||
use subxt::rpc::LightClient;
|
||||
use wasm_bindgen_test::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
@@ -16,3 +18,12 @@ async fn wasm_ws_transport_works() {
|
||||
let chain = client.rpc().system_chain().await.unwrap();
|
||||
assert_eq!(&chain, "Development");
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
async fn light_client_transport_works() {
|
||||
let light_client = LightClient::new(include_str!("../../artifacts/dev_spec.json")).unwrap();
|
||||
let client = subxt::client::OnlineClient::<PolkadotConfig>::from_rpc_client(Arc::new(light_client)).await.unwrap();
|
||||
|
||||
let chain = client.rpc().system_chain().await.unwrap();
|
||||
assert_eq!(&chain, "Development");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user