mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-09 20:11:09 +00:00
Make sure frame examples compile for wasm (#5332)
* Make sure frame examples compile for wasm This makes sure that `frame-example` and `frame-example-offchain-worker` compile for wasm. This also fixes compilation for these crates. The offchain worker example doesn't use serde-json anymore as that is too heavy and breaks `no_std` compilation. * Apply suggestions from code review Co-Authored-By: Nikolay Volf <nikvolf@gmail.com> Co-authored-by: Nikolay Volf <nikvolf@gmail.com>
This commit is contained in:
@@ -214,6 +214,24 @@ test-frame-staking:
|
||||
- WASM_BUILD_NO_COLOR=1 time cargo test --release --verbose --no-default-features --features std
|
||||
- sccache -s
|
||||
|
||||
test-frame-examples-compile-to-wasm:
|
||||
stage: test
|
||||
<<: *docker-env
|
||||
variables:
|
||||
# Enable debug assertions since we are running optimized builds for testing
|
||||
# but still want to have debug assertions.
|
||||
RUSTFLAGS: -Cdebug-assertions=y
|
||||
RUST_BACKTRACE: 1
|
||||
except:
|
||||
variables:
|
||||
- $DEPLOY_TAG
|
||||
script:
|
||||
- cd frame/example-offchain-worker/
|
||||
- cargo +nightly build --target=wasm32-unknown-unknown --no-default-features
|
||||
- cd ../example
|
||||
- cargo +nightly build --target=wasm32-unknown-unknown --no-default-features
|
||||
- sccache -s
|
||||
|
||||
test-wasmtime:
|
||||
stage: test
|
||||
<<: *docker-env
|
||||
|
||||
Generated
+14
-5
@@ -3063,6 +3063,15 @@ dependencies = [
|
||||
"linked-hash-map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lite-json"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faa835713bb12ba5204013497da16caf2dd2eee25ca829d0efaa054fb38c4ddd"
|
||||
dependencies = [
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.3.3"
|
||||
@@ -4147,9 +4156,9 @@ version = "2.0.0-alpha.4"
|
||||
dependencies = [
|
||||
"frame-support",
|
||||
"frame-system",
|
||||
"lite-json",
|
||||
"parity-scale-codec",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-runtime",
|
||||
@@ -6718,18 +6727,18 @@ checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.104"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.104"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
|
||||
checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -17,7 +17,7 @@ sp-core = { version = "2.0.0-alpha.4", default-features = false, path = "../../p
|
||||
sp-io = { version = "2.0.0-alpha.4", default-features = false, path = "../../primitives/io" }
|
||||
sp-runtime = { version = "2.0.0-alpha.4", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-std = { version = "2.0.0-alpha.4", default-features = false, path = "../../primitives/std" }
|
||||
serde_json = { version = "1.0.46", default-features = false, features = ["alloc"] }
|
||||
lite-json = { version = "0.1", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
@@ -26,6 +26,7 @@ std = [
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"serde",
|
||||
"lite-json/std",
|
||||
"sp-core/std",
|
||||
"sp-io/std",
|
||||
"sp-runtime/std",
|
||||
|
||||
@@ -47,13 +47,14 @@ use frame_support::{
|
||||
weights::SimpleDispatchInfo,
|
||||
};
|
||||
use frame_system::{self as system, ensure_signed, ensure_none, offchain};
|
||||
use serde_json as json;
|
||||
use sp_core::crypto::KeyTypeId;
|
||||
use sp_runtime::{
|
||||
offchain::{http, Duration, storage::StorageValueRef},
|
||||
traits::Zero,
|
||||
transaction_validity::{InvalidTransaction, ValidTransaction, TransactionValidity},
|
||||
};
|
||||
use sp_std::{vec, vec::Vec};
|
||||
use lite_json::json::JsonValue;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -320,7 +321,7 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// A helper function to fetch the price and send signed transaction.
|
||||
fn fetch_price_and_send_signed() -> Result<(), String> {
|
||||
fn fetch_price_and_send_signed() -> Result<(), &'static str> {
|
||||
use system::offchain::SubmitSignedTransaction;
|
||||
// Firstly we check if there are any accounts in the local keystore that are capable of
|
||||
// signing the transaction.
|
||||
@@ -334,7 +335,7 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
// Make an external HTTP request to fetch the current price.
|
||||
// Note this call will block until response is received.
|
||||
let price = Self::fetch_price().map_err(|e| format!("{:?}", e))?;
|
||||
let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?;
|
||||
|
||||
// Received price is wrapped into a call to `submit_price` public function of this pallet.
|
||||
// This means that the transaction, when executed, will simply call that function passing
|
||||
@@ -357,20 +358,18 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// A helper function to fetch the price and send unsigned transaction.
|
||||
fn fetch_price_and_send_unsigned(block_number: T::BlockNumber) -> Result<(), String> {
|
||||
fn fetch_price_and_send_unsigned(block_number: T::BlockNumber) -> Result<(), &'static str> {
|
||||
use system::offchain::SubmitUnsignedTransaction;
|
||||
// Make sure we don't fetch the price if unsigned transaction is going to be rejected
|
||||
// anyway.
|
||||
let next_unsigned_at = <NextUnsignedAt<T>>::get();
|
||||
if next_unsigned_at > block_number {
|
||||
return Err(
|
||||
format!("Too early to send unsigned transaction. Next at: {:?}", next_unsigned_at)
|
||||
)?
|
||||
return Err("Too early to send unsigned transaction")
|
||||
}
|
||||
|
||||
// Make an external HTTP request to fetch the current price.
|
||||
// Note this call will block until response is received.
|
||||
let price = Self::fetch_price().map_err(|e| format!("{:?}", e))?;
|
||||
let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?;
|
||||
|
||||
// Received price is wrapped into a call to `submit_price_unsigned` public function of this
|
||||
// pallet. This means that the transaction, when executed, will simply call that function
|
||||
@@ -428,21 +427,17 @@ impl<T: Trait> Module<T> {
|
||||
// Note that the return object allows you to read the body in chunks as well
|
||||
// with a way to control the deadline.
|
||||
let body = response.body().collect::<Vec<u8>>();
|
||||
// Next we parse the response using `serde_json`. Even though it's possible to use
|
||||
// `serde_derive` and deserialize to a struct it's not recommended due to blob size
|
||||
// overhead introduced by such code. Deserializing to `json::Value` is much more
|
||||
// lightweight and should be preferred, especially if we only care about a small number
|
||||
// of properties from the response.
|
||||
let val: Result<json::Value, _> = json::from_slice(&body);
|
||||
// Let's parse the price as float value. Note that you should avoid using floats in the
|
||||
// runtime, it's fine to do that in the offchain worker, but we do convert it to an integer
|
||||
// before submitting on-chain.
|
||||
let price = val.ok().and_then(|v| v.get("USD").and_then(|v| v.as_f64()));
|
||||
let price = match price {
|
||||
Some(pricef) => Ok((pricef * 100.) as u32),
|
||||
|
||||
// Create a str slice from the body.
|
||||
let body_str = sp_std::str::from_utf8(&body).map_err(|_| {
|
||||
debug::warn!("No UTF8 body");
|
||||
http::Error::Unknown
|
||||
})?;
|
||||
|
||||
let price = match Self::parse_price(body_str) {
|
||||
Some(price) => Ok(price),
|
||||
None => {
|
||||
let s = core::str::from_utf8(&body);
|
||||
debug::warn!("Unable to extract price from the response: {:?}", s);
|
||||
debug::warn!("Unable to extract price from the response: {:?}", body_str);
|
||||
Err(http::Error::Unknown)
|
||||
}
|
||||
}?;
|
||||
@@ -452,6 +447,28 @@ impl<T: Trait> Module<T> {
|
||||
Ok(price)
|
||||
}
|
||||
|
||||
/// Parse the price from the given JSON string using `lite-json`.
|
||||
///
|
||||
/// Returns `None` when parsing failed or `Some(price in cents)` when parsing is successful.
|
||||
fn parse_price(price_str: &str) -> Option<u32> {
|
||||
let val = lite_json::parse_json(price_str);
|
||||
let price = val.ok().and_then(|v| match v {
|
||||
JsonValue::Object(obj) => {
|
||||
let mut chars = "USD".chars();
|
||||
obj.into_iter()
|
||||
.find(|(k, _)| k.iter().all(|k| Some(*k) == chars.next()))
|
||||
.and_then(|v| match v.1 {
|
||||
JsonValue::Number(number) => Some(number),
|
||||
_ => None,
|
||||
})
|
||||
},
|
||||
_ => None
|
||||
})?;
|
||||
|
||||
let exp = price.fraction_length.checked_sub(2).unwrap_or(0);
|
||||
Some(price.integer as u32 * 100 + (price.fraction / 10_u64.pow(exp)) as u32)
|
||||
}
|
||||
|
||||
/// Add new price to the list.
|
||||
fn add_price(who: T::AccountId, price: u32) {
|
||||
debug::info!("Adding to the average: {}", price);
|
||||
|
||||
@@ -132,7 +132,7 @@ fn should_make_http_call_and_parse_result() {
|
||||
// when
|
||||
let price = Example::fetch_price().unwrap();
|
||||
// then
|
||||
assert_eq!(price, 15522);
|
||||
assert_eq!(price, 15523);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ fn should_submit_signed_transaction_on_chain() {
|
||||
assert!(pool_state.read().transactions.is_empty());
|
||||
let tx = Extrinsic::decode(&mut &*tx).unwrap();
|
||||
assert_eq!(tx.signature.unwrap().0, 0);
|
||||
assert_eq!(tx.call, Call::submit_price(15522));
|
||||
assert_eq!(tx.call, Call::submit_price(15523));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ fn should_submit_unsigned_transaction_on_chain() {
|
||||
assert!(pool_state.read().transactions.is_empty());
|
||||
let tx = Extrinsic::decode(&mut &*tx).unwrap();
|
||||
assert_eq!(tx.signature, None);
|
||||
assert_eq!(tx.call, Call::submit_price_unsigned(1, 15522));
|
||||
assert_eq!(tx.call, Call::submit_price_unsigned(1, 15523));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -208,3 +208,19 @@ fn price_oracle_response(state: &mut testing::OffchainState) {
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_price_works() {
|
||||
let test_data = vec![
|
||||
("{\"USD\":6536.92}", Some(653692)),
|
||||
("{\"USD\":65.92}", Some(6592)),
|
||||
("{\"USD\":6536.924565}", Some(653692)),
|
||||
("{\"USD\":6536}", Some(653600)),
|
||||
("{\"USD2\":6536}", None),
|
||||
("{\"USD\":\"6432\"}", None),
|
||||
];
|
||||
|
||||
for (json, expected) in test_data {
|
||||
assert_eq!(expected, Example::parse_price(json));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ sp-std = { version = "2.0.0-alpha.4", default-features = false, path = "../../pr
|
||||
sp-io = { version = "2.0.0-alpha.4", default-features = false, path = "../../primitives/io" }
|
||||
|
||||
[dev-dependencies]
|
||||
sp-core = { version = "2.0.0-alpha.4", path = "../../primitives/core" }
|
||||
sp-core = { version = "2.0.0-alpha.4", path = "../../primitives/core", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
|
||||
Reference in New Issue
Block a user