Skip to content

Commit

Permalink
Remove Chain.databaseContent function (#2791)
Browse files Browse the repository at this point in the history
Close #2456

This PR removes the `Chain.databaseContent` function from the public
API. Users are supposed to use the
`chainHead_unstable_finalizedDatabase` JSON-RPC function that was added
in #2749 instead.

This is an API breaking change. Since
#2778 has been merged and is a
breaking change, let's do other breaking changes at the same time.

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
tomaka and mergify[bot] authored Sep 28, 2022
1 parent 62ff6b7 commit 313eee2
Show file tree
Hide file tree
Showing 10 changed files with 20 additions and 246 deletions.
46 changes: 1 addition & 45 deletions bin/light-base/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ pub struct AddChainConfig<'a, TChain, TRelays> {
pub specification: &'a str,

/// Opaque data containing the database content that was retrieved by calling
/// [`Client::database_content`] in the past.
/// the `chainHead_unstable_finalizedDatabase` JSON-RPC function in the past.
///
/// Pass an empty string if no database content exists or is known.
///
Expand Down Expand Up @@ -883,50 +883,6 @@ impl<TPlat: platform::Platform, TChain> Client<TPlat, TChain> {

json_rpc_sender.queue_rpc_request(json_rpc_request)
}

/// Returns opaque data that can later by passing back through
/// [`AddChainConfig::database_content`].
///
/// Note that the `Future` being returned doesn't borrow `self`. Even if the chain is later
/// removed, this `Future` will still return a value.
///
/// If the database content can't be obtained because not enough information is known about
/// the chain, a dummy value is intentionally returned.
///
/// `max_size` can be passed force the output of the function to be smaller than the given
/// value.
///
/// # Panic
///
/// Panics if the [`ChainId`] is invalid.
///
pub fn database_content(
&self,
chain_id: ChainId,
max_size: usize,
) -> impl Future<Output = String> {
let key = &self.public_api_chains.get(chain_id.0).unwrap().key;

// Clone the services initialization future.
let mut services = match &self.chains_by_key.get(key).unwrap().services {
future::MaybeDone::Done(d) => future::MaybeDone::Done(d.clone()),
future::MaybeDone::Future(d) => future::MaybeDone::Future(d.clone()),
future::MaybeDone::Gone => unreachable!(),
};

async move {
// Wait for the chain to finish initializing before we can obtain the database.
(&mut services).await;
let services = Pin::new(&mut services).take_output().unwrap();
encode_database(
&services.network_service,
&services.sync_service,
services.block_number_bytes,
max_size,
)
.await
}
}
}

/// Starts all the services of the client.
Expand Down
4 changes: 4 additions & 0 deletions bin/wasm-node/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Removed

- Removed `Chain.databaseContent` function. Use the `chainHead_unstable_finalizedDatabase` JSON-RPC function to obtain the database content instead. ([#2791](https://github.com/paritytech/smoldot/pull/2791))

### Changed

- `Chain.sendJsonRpc` now throws a `MalformedJsonRpcError` exception if the JSON-RPC request is too large or malformed, or a `QueueFullError` if the queue of JSON-RPC requests of the chain is full. ([#2778](https://github.com/paritytech/smoldot/pull/2778))
Expand Down
26 changes: 2 additions & 24 deletions bin/wasm-node/javascript/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,22 +140,6 @@ export interface Chain {
*/
nextJsonRpcResponse(): Promise<string>;

/**
* Serializes the important information about the state of the chain so that it can be provided
* back in the {AddChainOptions.databaseContent} when the chain is recreated.
*
* The content of the string is opaque and shouldn't be decoded.
*
* A parameter can be passed to indicate the maximum length of the returned value (in number
* of bytes this string would occupy in the UTF-8 encoding). The higher this limit is the more
* information can be included. This parameter is optional, and not passing any value means
* "unbounded".
*
* @throws {AlreadyDestroyedError} If the chain has been removed or the client has been terminated.
* @throws {CrashError} If the background client has crashed.
*/
databaseContent(maxUtf8BytesSize?: number): Promise<string>;

/**
* Disconnects from the blockchain.
*
Expand Down Expand Up @@ -295,7 +279,8 @@ export interface AddChainOptions {
chainSpec: string;

/**
* Content of the database of this chain. Can be obtained with {Client.databaseContent}.
* Content of the database of this chain. Can be obtained by using the
* `chainHead_unstable_finalizedDatabase` JSON-RPC function.
*
* Smoldot reserves the right to change its database format, making previous databases
* incompatible. For this reason, no error is generated if the content of the database is invalid
Expand Down Expand Up @@ -448,13 +433,6 @@ export function start(options: ClientOptions, platformBindings: PlatformBindings
instance.nextJsonRpcResponse(chainId, resolve, reject)
});
},
databaseContent: (maxUtf8BytesSize) => {
if (alreadyDestroyedError)
return Promise.reject(alreadyDestroyedError);
if (wasDestroyed.destroyed)
return Promise.reject(new AlreadyDestroyedError);
return instance.databaseContent(chainId, maxUtf8BytesSize);
},
remove: () => {
if (alreadyDestroyedError)
throw alreadyDestroyedError;
Expand Down
16 changes: 0 additions & 16 deletions bin/wasm-node/javascript/src/instance/bindings-smoldot-light.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ export interface Config {

logCallback: (level: number, target: string, message: string) => void,
jsonRpcResponsesNonEmptyCallback: (chainId: number) => void,
databaseContentCallback: (data: string, chainId: number) => void,
currentTaskCallback?: (taskName: string | null) => void,
}

Expand Down Expand Up @@ -222,21 +221,6 @@ export default function (config: Config): { imports: WebAssembly.ModuleImports,
config.jsonRpcResponsesNonEmptyCallback(chainId);
},

// Used by the Rust side in response to asking for the database content of a chain.
database_content_ready: (ptr: number, len: number, chainId: number) => {
if (killedTracked.killed) return;

const instance = config.instance!;

ptr >>>= 0;
len >>>= 0;

let content = buffer.utf8BytesToString(new Uint8Array(instance.exports.memory.buffer), ptr, len);
if (config.databaseContentCallback) {
config.databaseContentCallback(content, chainId);
}
},

// Used by the Rust side to emit a log entry.
// See also the `max_log_level` parameter in the configuration.
log: (level: number, targetPtr: number, targetLen: number, messagePtr: number, messageLen: number) => {
Expand Down
1 change: 0 additions & 1 deletion bin/wasm-node/javascript/src/instance/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export interface SmoldotWasmExports extends WebAssembly.Exports {
json_rpc_send: (textPtr: number, textLen: number, chainId: number) => number,
json_rpc_responses_peek: (chainId: number) => number,
json_rpc_responses_pop: (chainId: number) => void,
database_content: (chainId: number, maxSize: number) => void,
timer_finished: (timerId: number) => void,
connection_open_single_stream: (connectionId: number, handshakeTy: number) => void,
connection_open_multi_stream: (connectionId: number, handshakeTyPtr: number, handshakeTyLen: number) => void,
Expand Down
59 changes: 3 additions & 56 deletions bin/wasm-node/javascript/src/instance/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export interface Instance {
nextJsonRpcResponse: (chainId: number, resolve: (response: string) => void, reject: (error: Error) => void) => void
addChain: (chainSpec: string, databaseContent: string, potentialRelayChains: number[], disableJsonRpc: boolean) => Promise<{ success: true, chainId: number } | { success: false, error: string }>
removeChain: (chainId: number) => void
databaseContent: (chainId: number, maxUtf8BytesSize?: number) => Promise<string>
startShutdown: () => void
}

Expand All @@ -87,8 +86,7 @@ export function start(configMessage: Config, platformBindings: instance.Platform

// Contains the information of each chain that is currently alive.
let chains: Map<number, {
jsonRpcResponsesPromises: PromiseFunctions[],
databasePromises: PromiseFunctions[],
jsonRpcResponsesPromises: JsonRpcResponsesPromise[],
}> = new Map();

// Start initialization of the Wasm VM.
Expand All @@ -110,10 +108,6 @@ export function start(configMessage: Config, platformBindings: instance.Platform
promise.reject(crashError.error)
}
chain.jsonRpcResponsesPromises = [];
for (const promise of chain.databasePromises) {
promise.reject(crashError.error)
}
chain.databasePromises = [];
}
},
logCallback: (level, target, message) => {
Expand Down Expand Up @@ -160,10 +154,6 @@ export function start(configMessage: Config, platformBindings: instance.Platform
setTimeout(update, 0)
}
},
databaseContentCallback: (data, chainId) => {
const promises = chains.get(chainId)?.databasePromises!;
(promises.shift() as PromiseFunctions).resolve(data);
},
currentTaskCallback: (taskName) => {
currentTask.name = taskName
},
Expand Down Expand Up @@ -302,8 +292,7 @@ export function start(configMessage: Config, platformBindings: instance.Platform
if (instance.exports.chain_is_ok(chainId) != 0) {
console.assert(!chains.has(chainId));
chains.set(chainId, {
jsonRpcResponsesPromises: new Array(),
databasePromises: new Array()
jsonRpcResponsesPromises: new Array()
});
return { success: true, chainId };
} else {
Expand Down Expand Up @@ -345,48 +334,6 @@ export function start(configMessage: Config, platformBindings: instance.Platform
}
},

databaseContent: (chainId: number, maxUtf8BytesSize?: number): Promise<string> => {
// Because `databaseContent` is passed as parameter an identifier returned by `addChain`, it
// is always the case that the Wasm instance is already initialized. The only possibility for
// it to not be the case is if the user completely invented the `chainId`.
if (!state.initialized)
throw new Error("Internal error");

if (crashError.error)
throw crashError.error;

console.assert(chains.has(chainId));
const databaseContentPromises = chains.get(chainId)?.databasePromises!;
const promise: Promise<string> = new Promise((resolve, reject) => {
databaseContentPromises.push({ resolve, reject });
});

// Cap `maxUtf8BytesSize` and set a default value.
const twoPower32 = (1 << 30) * 4; // `1 << 31` and `1 << 32` in JavaScript don't give the value that you expect.
const maxSize = maxUtf8BytesSize || (twoPower32 - 1);
const cappedMaxSize = (maxSize >= twoPower32) ? (twoPower32 - 1) : maxSize;

// The value of `maxUtf8BytesSize` is guaranteed to always fit in 32 bits, in
// other words, that `maxUtf8BytesSize < (1 << 32)`.
// We need to perform a conversion in such a way that the the bits of the output of
// `ToInt32(converted)`, when interpreted as u32, is equal to `maxUtf8BytesSize`.
// See ToInt32 here: https://tc39.es/ecma262/#sec-toint32
// Note that the code below has been tested against example values. Please be very careful
// if you decide to touch it. Ideally it would be unit-tested, but since it concerns the FFI
// layer between JS and Rust, writing unit tests would be extremely complicated.
const twoPower31 = (1 << 30) * 2; // `1 << 31` in JavaScript doesn't give the value that you expect.
const converted = (cappedMaxSize >= twoPower31) ?
(cappedMaxSize - twoPower32) : cappedMaxSize;

try {
state.instance.exports.database_content(chainId, converted);
return promise;
} catch (_error) {
console.assert(crashError.error);
throw crashError.error
}
},

startShutdown: () => {
return queueOperation((instance) => {
// `startShutdown` is a bit special in its handling of crashes.
Expand All @@ -409,7 +356,7 @@ export function start(configMessage: Config, platformBindings: instance.Platform

}

interface PromiseFunctions {
interface JsonRpcResponsesPromise {
resolve: (data: string) => void,
reject: (error: Error) => void,
}
1 change: 0 additions & 1 deletion bin/wasm-node/javascript/src/instance/raw-instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export interface Config {
onWasmPanic: (message: string) => void,
logCallback: (level: number, target: string, message: string) => void,
jsonRpcResponsesNonEmptyCallback: (chainId: number) => void,
databaseContentCallback: (data: string, chainId: number) => void,
currentTaskCallback?: (taskName: string | null) => void,
cpuRateLimit: number,
}
Expand Down
34 changes: 1 addition & 33 deletions bin/wasm-node/rust/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,6 @@ extern "C" {
/// [`json_rpc_responses_pop`] in order to have the guarantee that this function gets called.
pub fn json_rpc_responses_non_empty(chain_id: u32);

/// This function is called by the client is response to calling [`database_content`].
///
/// The database content is a UTF-8 string found in the memory of the WebAssembly virtual
/// machine at offset `ptr` and with length `len`.
///
/// `chain_id` is the chain that the request was made to. It is guaranteed to always be valid.
/// This function is not called if the chain is removed with [`remove_chain`] while the fetch
/// is in progress.
pub fn database_content_ready(ptr: u32, len: u32, 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,
Expand Down Expand Up @@ -319,7 +309,7 @@ pub extern "C" fn alloc(len: u32) -> u32 {
/// the pointers and lengths (in bytes) as parameter to this function.
///
/// > **Note**: The database content is an opaque string that can be obtained by calling
/// > [`database_content`].
/// > the `chainHead_unstable_finalizedDatabase` JSON-RPC function.
///
/// Similarly, use [`alloc`] to allocate a buffer containing a list of 32-bits-little-endian chain
/// ids. Pass the pointer and number of chain ids (*not* length in bytes of the buffer) to this
Expand Down Expand Up @@ -465,28 +455,6 @@ pub extern "C" fn json_rpc_responses_pop(chain_id: u32) {
super::json_rpc_responses_pop(chain_id)
}

/// Starts generating the content of the database of the chain.
///
/// This function doesn't immediately return the content, but later calls
/// [`database_content_ready`] with the content of the database.
///
/// Calling this function multiple times will lead to multiple calls to [`database_content_ready`],
/// with potentially different values.
///
/// The `max_size` parameter contains the maximum length, in bytes, of the value that will be
/// provided back. Please be aware that passing a `u32` across the FFI boundary can be tricky.
/// From the Wasm perspective, the parameter of this function is actually a `i32` that is then
/// reinterpreted as a `u32`.
///
/// [`database_content_ready`] will not be called if you remove the chain with [`remove_chain`]
/// while the operation is in progress.
///
/// It is forbidden to call this function on an erroneous chain.
#[no_mangle]
pub extern "C" fn database_content(chain_id: u32, max_size: u32) {
super::database_content(chain_id, max_size)
}

/// Must be called in response to [`start_timer`] after the given duration has passed.
#[no_mangle]
pub extern "C" fn timer_finished(timer_id: u32) {
Expand Down
10 changes: 3 additions & 7 deletions bin/wasm-node/rust/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ use std::{
pub(crate) struct Client<TPlat: smoldot_light::platform::Platform, TChain> {
pub(crate) smoldot: smoldot_light::Client<TPlat, TChain>,

pub(crate) new_tasks_spawner: mpsc::UnboundedSender<(String, future::BoxFuture<'static, ()>)>,

/// List of all chains that have been added by the user.
pub(crate) chains: slab::Slab<Chain>,
}
Expand Down Expand Up @@ -197,17 +195,15 @@ pub(crate) fn init<TPlat: smoldot_light::platform::Platform, TChain>(
.unwrap();

let client = smoldot_light::Client::new(smoldot_light::ClientConfig {
tasks_spawner: {
let new_task_tx = new_task_tx.clone();
Box::new(move |name, task| new_task_tx.unbounded_send((name, task)).unwrap())
},
tasks_spawner: Box::new(move |name, task| {
new_task_tx.unbounded_send((name, task)).unwrap()
}),
system_name: env!("CARGO_PKG_NAME").into(),
system_version: env!("CARGO_PKG_VERSION").into(),
});

Client {
smoldot: client,
new_tasks_spawner: new_task_tx,
chains: slab::Slab::with_capacity(8),
}
}
Expand Down
Loading

0 comments on commit 313eee2

Please sign in to comment.