Skip to content

Commit

Permalink
feat: Support no_std and WASM compilation for fuel-core crates (#1411)
Browse files Browse the repository at this point in the history
Related issues:
- Closes #1370

Adds no_std compilation support for crates:
- `fuel-core-types`
- `fuel-core-storage`
- `fuel-core-client`
- `fuel-core-chain-config`

---------

Co-authored-by: xgreenx <xgreenx9999@gmail.com>
  • Loading branch information
Brandon Vrooman and xgreenx authored Oct 17, 2023
1 parent ab800f9 commit ff7cf60
Show file tree
Hide file tree
Showing 29 changed files with 251 additions and 150 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,17 @@ jobs:
args: --manifest-path version-compatibility/Cargo.toml --workspace
- command: build
args: -p fuel-core-bin --no-default-features --features production

# WASM compatibility checks
- command: check
args: -p fuel-core-types --target wasm32-unknown-unknown --no-default-features
- command: check
args: -p fuel-core-storage --target wasm32-unknown-unknown --no-default-features
- command: check
args: -p fuel-core-client --target wasm32-unknown-unknown --no-default-features
- command: check
args: -p fuel-core-chain-config --target wasm32-unknown-unknown --no-default-features

# disallow any job that takes longer than 45 minutes
timeout-minutes: 45
continue-on-error: ${{ matrix.skip-error || false }}
Expand All @@ -127,6 +138,7 @@ jobs:
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
targets: "wasm32-unknown-unknown"
components: "clippy"
- name: Install Cargo Make
uses: davidB/rust-cargo-make@v1
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Description of the upcoming release here.

### Added

- [#1411](https://github.com/FuelLabs/fuel-core/pull/1411) Added WASM and `no_std` compatibility
- [#1371](https://github.com/FuelLabs/fuel-core/pull/1371): Add new client function for querying the `MessageStatus` for a specific message (by `Nonce`)
- [#1356](https://github.com/FuelLabs/fuel-core/pull/1356): Add peer reputation reporting to heartbeat code
- [#1355](https://github.com/FuelLabs/fuel-core/pull/1355): Added new metrics related to block importing, such as tps, sync delays etc
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ fuel-core-tests = { version = "0.0.0", path = "./tests" }
fuel-core-xtask = { version = "0.0.0", path = "./xtask" }

# Fuel dependencies
fuel-vm-private = { version = "0.38.0", package = "fuel-vm" }
fuel-vm-private = { version = "0.38.0", package = "fuel-vm", default-features = false }

# Common dependencies
anyhow = "1.0"
Expand Down
22 changes: 9 additions & 13 deletions benches/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,7 @@ use fuel_core_types::{
};

pub use rand::Rng;
use std::{
io,
iter,
};
use std::iter;

const LARGE_GAS_LIMIT: u64 = u64::MAX - 1001;

Expand Down Expand Up @@ -95,7 +92,7 @@ pub struct VmBench {
pub prepare_call: Option<PrepareCall>,
pub dummy_contract: Option<ContractId>,
pub contract_code: Option<ContractCode>,
pub prepare_db: Option<Box<dyn FnMut(VmDatabase) -> io::Result<VmDatabase>>>,
pub prepare_db: Option<Box<dyn FnMut(VmDatabase) -> anyhow::Result<VmDatabase>>>,
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -138,7 +135,7 @@ impl VmBench {
}
}

pub fn contract<R>(rng: &mut R, instruction: Instruction) -> io::Result<Self>
pub fn contract<R>(rng: &mut R, instruction: Instruction) -> anyhow::Result<Self>
where
R: Rng,
{
Expand Down Expand Up @@ -276,21 +273,21 @@ impl VmBench {

pub fn with_prepare_db<F>(mut self, prepare_db: F) -> Self
where
F: FnMut(VmDatabase) -> io::Result<VmDatabase> + 'static,
F: FnMut(VmDatabase) -> anyhow::Result<VmDatabase> + 'static,
{
self.prepare_db.replace(Box::new(prepare_db));
self
}

pub fn prepare(self) -> io::Result<VmBenchPrepared> {
pub fn prepare(self) -> anyhow::Result<VmBenchPrepared> {
self.try_into()
}
}

impl TryFrom<VmBench> for VmBenchPrepared {
type Error = io::Error;
type Error = anyhow::Error;

fn try_from(case: VmBench) -> io::Result<Self> {
fn try_from(case: VmBench) -> anyhow::Result<Self> {
let VmBench {
params,
gas_price,
Expand All @@ -317,8 +314,7 @@ impl TryFrom<VmBench> for VmBenchPrepared {
.iter()
.any(|op| matches!(op, Instruction::RET(_)))
{
return Err(io::Error::new(
io::ErrorKind::Other,
return Err(anyhow::anyhow!(
"a prepare script should not call/return into different contexts.",
))
}
Expand Down Expand Up @@ -423,7 +419,7 @@ impl TryFrom<VmBench> for VmBenchPrepared {
let PrepareCall { ra, rb, rc, rd } = p;

vm.prepare_call(ra, rb, rc, rd)
.map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string()))?;
.map_err(anyhow::Error::msg)?;
for instruction in post_call {
vm.instruction(instruction).unwrap();
}
Expand Down
10 changes: 6 additions & 4 deletions bin/fuel-core/src/cli/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,12 @@ pub async fn exec(command: Command) -> anyhow::Result<()> {
database::Database,
};
let path = command.database_path;
let data_source =
fuel_core::state::rocks_db::RocksDb::default_open(&path, None).context(
format!("failed to open database at path {}", path.display()),
)?;
let data_source = fuel_core::state::rocks_db::RocksDb::default_open(&path, None)
.map_err(Into::<anyhow::Error>::into)
.context(format!(
"failed to open database at path {}",
path.display()
))?;
let db = Database::new(std::sync::Arc::new(data_source));

match command.subcommand {
Expand Down
6 changes: 4 additions & 2 deletions bin/keygen/src/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ pub struct ParseSecret {

impl ParseSecret {
pub fn exec(&self) -> anyhow::Result<()> {
let secret = SecretKey::from_str(&self.secret)?;
let secret = SecretKey::from_str(&self.secret)
.map_err(|_| anyhow::anyhow!("invalid secret key"))?;
match self.key_type {
KeyType::BlockProduction => {
let address = Input::owner(&secret.public_key());
Expand Down Expand Up @@ -130,7 +131,8 @@ fn print_value(output: serde_json::Value, pretty: bool) -> anyhow::Result<()> {
serde_json::to_string_pretty(&output)
} else {
serde_json::to_string(&output)
};
}
.map_err(anyhow::Error::msg);
println!("{}", output?);
Ok(())
}
19 changes: 12 additions & 7 deletions crates/chain-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,23 @@ description = "Fuel Chain config types"
anyhow = { workspace = true }
bech32 = "0.9.0"
fuel-core-storage = { workspace = true }
fuel-core-types = { workspace = true, features = [
"serde",
"random",
] }
fuel-core-types = { workspace = true, default-features = false, features = ["serde"] }
hex = { version = "0.4", features = ["serde"] }
itertools = { workspace = true }
postcard = { version = "1.0", features = ["use-std"] }
rand = { workspace = true }
postcard = { version = "1.0", features = ["alloc"] }
rand = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive", "rc"] }
serde_json = { version = "1.0", features = ["raw_value"] }
serde_json = { version = "1.0", features = ["raw_value"], optional = true }
serde_with = "1.11"
tracing = "0.1"

[dev-dependencies]
fuel-core-types = { workspace = true, default-features = false, features = ["random", "serde"] }
insta = { workspace = true }
rand = { workspace = true }
serde_json = { version = "1.0", features = ["raw_value"] }

[features]
default = ["std"]
random = ["dep:rand", "fuel-core-types/random"]
std = ["dep:serde_json", "fuel-core-types/std", "anyhow/std"]
3 changes: 3 additions & 0 deletions crates/chain-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mod tests {
RngCore,
SeedableRng,
};
#[cfg(feature = "std")]
use std::{
env::temp_dir,
fs::write,
Expand All @@ -47,6 +48,7 @@ mod tests {
state::StateConfig,
};

#[cfg(feature = "std")]
#[test]
fn from_str_loads_from_file() {
// setup chain config in a temp file
Expand Down Expand Up @@ -320,6 +322,7 @@ mod tests {
}
}

#[cfg(feature = "std")]
fn tmp_path() -> PathBuf {
let mut path = temp_dir();
path.push(rand::random::<u16>().to_string());
Expand Down
57 changes: 47 additions & 10 deletions crates/chain-config/src/config/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use bech32::{
ToBase32,
Variant::Bech32m,
};
use core::str::FromStr;
use fuel_core_storage::MerkleRoot;
use fuel_core_types::{
fuel_crypto::Hasher,
Expand All @@ -19,10 +20,6 @@ use fuel_core_types::{
fuel_vm::SecretKey,
};
use itertools::Itertools;
use rand::{
rngs::StdRng,
SeedableRng,
};
use serde::{
Deserialize,
Serialize,
Expand All @@ -31,10 +28,10 @@ use serde_with::{
serde_as,
skip_serializing_none,
};
#[cfg(feature = "std")]
use std::{
io::ErrorKind,
path::PathBuf,
str::FromStr,
};

use crate::{
Expand Down Expand Up @@ -83,10 +80,49 @@ impl ChainConfig {
pub fn local_testnet() -> Self {
// endow some preset accounts with an initial balance
tracing::info!("Initial Accounts");
let mut rng = StdRng::seed_from_u64(10);
let secrets = [
"0xde97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c",
"0x37fa81c84ccd547c30c176b118d5cb892bdb113e8e80141f266519422ef9eefd",
"0x862512a2363db2b3a375c0d4bbbd27172180d89f23f2e259bac850ab02619301",
"0x976e5c3fa620092c718d852ca703b6da9e3075b9f2ecb8ed42d9f746bf26aafb",
"0x7f8a325504e7315eda997db7861c9447f5c3eff26333b20180475d94443a10c6",
];
let initial_coins = secrets
.into_iter()
.map(|secret| {
let secret = SecretKey::from_str(secret).expect("Expected valid secret");
let address = Address::from(*secret.public_key().hash());
let bech32_data = Bytes32::new(*address).to_base32();
let bech32_encoding =
bech32::encode(FUEL_BECH32_HRP, bech32_data, Bech32m).unwrap();
tracing::info!(
"PrivateKey({:#x}), Address({:#x} [bech32: {}]), Balance({})",
secret,
address,
bech32_encoding,
TESTNET_INITIAL_BALANCE
);
Self::initial_coin(secret, TESTNET_INITIAL_BALANCE, None)
})
.collect_vec();

Self {
chain_name: LOCAL_TESTNET.to_string(),
initial_state: Some(StateConfig {
coins: Some(initial_coins),
..StateConfig::default()
}),
..Default::default()
}
}

#[cfg(feature = "random")]
pub fn random_testnet() -> Self {
tracing::info!("Initial Accounts");
let mut rng = rand::thread_rng();
let initial_coins = (0..5)
.map(|_| {
let secret = fuel_core_types::fuel_crypto::SecretKey::random(&mut rng);
let secret = SecretKey::random(&mut rng);
let address = Address::from(*secret.public_key().hash());
let bech32_data = Bytes32::new(*address).to_base32();
let bech32_encoding =
Expand Down Expand Up @@ -132,6 +168,7 @@ impl ChainConfig {
}
}

#[cfg(feature = "std")]
impl FromStr for ChainConfig {
type Err = std::io::Error;

Expand Down Expand Up @@ -184,7 +221,7 @@ impl GenesisCommitment for ChainConfig {
impl GenesisCommitment for ConsensusParameters {
fn root(&self) -> anyhow::Result<MerkleRoot> {
// TODO: Define hash algorithm for `ConsensusParameters`
let bytes = postcard::to_stdvec(&self)?;
let bytes = postcard::to_allocvec(&self).map_err(anyhow::Error::msg)?;
let params_hash = Hasher::default().chain(bytes).finalize();

Ok(params_hash.into())
Expand All @@ -194,7 +231,7 @@ impl GenesisCommitment for ConsensusParameters {
impl GenesisCommitment for GasCosts {
fn root(&self) -> anyhow::Result<MerkleRoot> {
// TODO: Define hash algorithm for `GasCosts`
let bytes = postcard::to_stdvec(&self)?;
let bytes = postcard::to_allocvec(&self).map_err(anyhow::Error::msg)?;
let hash = Hasher::default().chain(bytes).finalize();

Ok(hash.into())
Expand All @@ -204,7 +241,7 @@ impl GenesisCommitment for GasCosts {
impl GenesisCommitment for ConsensusConfig {
fn root(&self) -> anyhow::Result<MerkleRoot> {
// TODO: Define hash algorithm for `ConsensusConfig`
let bytes = postcard::to_stdvec(&self)?;
let bytes = postcard::to_allocvec(&self).map_err(anyhow::Error::msg)?;
let hash = Hasher::default().chain(bytes).finalize();

Ok(hash.into())
Expand Down
Loading

0 comments on commit ff7cf60

Please sign in to comment.