Persistent Local Storage for offchain workers. (#2894)

* WiP.

* Implement offchain storage APIs.

* Change compare_and_set to return bool.

* Add offchain http test.

* Fix tests.

* Bump spec version.

* Fix warnings and test.

* Fix compilation.

* Remove unused code.

* Introduce Local (fork-aware) and Persistent storage.

* Fix borked merge.

* Prevent warning on depreacated client.backend

* Fix long lines.

* Clean up dependencies.

* Update core/primitives/src/offchain.rs

Co-Authored-By: André Silva <andre.beat@gmail.com>

* Update core/primitives/src/offchain.rs

Co-Authored-By: André Silva <andre.beat@gmail.com>
This commit is contained in:
Tomasz Drwięga
2019-07-02 20:11:06 +02:00
committed by Gavin Wood
parent 24aa882ebc
commit 2217c1e9a1
26 changed files with 726 additions and 118 deletions
+75 -4
View File
@@ -857,24 +857,28 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
.map_err(|_| "Invalid attempt to set value in ext_random_seed")?;
Ok(())
},
ext_local_storage_set(key: *const u8, key_len: u32, value: *const u8, value_len: u32) => {
ext_local_storage_set(kind: u32, key: *const u8, key_len: u32, value: *const u8, value_len: u32) => {
let kind = offchain::StorageKind::try_from(kind)
.map_err(|_| "storage kind OOB while ext_local_storage_set: wasm")?;
let key = this.memory.get(key, key_len as usize)
.map_err(|_| "OOB while ext_local_storage_set: wasm")?;
let value = this.memory.get(value, value_len as usize)
.map_err(|_| "OOB while ext_local_storage_set: wasm")?;
this.ext.offchain()
.map(|api| api.local_storage_set(&key, &value))
.map(|api| api.local_storage_set(kind, &key, &value))
.ok_or_else(|| "Calling unavailable API ext_local_storage_set: wasm")?;
Ok(())
},
ext_local_storage_get(key: *const u8, key_len: u32, value_len: *mut u32) -> *mut u8 => {
ext_local_storage_get(kind: u32, key: *const u8, key_len: u32, value_len: *mut u32) -> *mut u8 => {
let kind = offchain::StorageKind::try_from(kind)
.map_err(|_| "storage kind OOB while ext_local_storage_get: wasm")?;
let key = this.memory.get(key, key_len as usize)
.map_err(|_| "OOB while ext_local_storage_get: wasm")?;
let maybe_value = this.ext.offchain()
.map(|api| api.local_storage_get(&key))
.map(|api| api.local_storage_get(kind, &key))
.ok_or_else(|| "Calling unavailable API ext_local_storage_get: wasm")?;
let (offset, len) = if let Some(value) = maybe_value {
@@ -891,6 +895,31 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(offset)
},
ext_local_storage_compare_and_set(
kind: u32,
key: *const u8,
key_len: u32,
old_value: *const u8,
old_value_len: u32,
new_value: *const u8,
new_value_len: u32
) -> u32 => {
let kind = offchain::StorageKind::try_from(kind)
.map_err(|_| "storage kind OOB while ext_local_storage_compare_and_set: wasm")?;
let key = this.memory.get(key, key_len as usize)
.map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?;
let old_value = this.memory.get(old_value, old_value_len as usize)
.map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?;
let new_value = this.memory.get(new_value, new_value_len as usize)
.map_err(|_| "OOB while ext_local_storage_compare_and_set: wasm")?;
let res = this.ext.offchain()
.map(|api| api.local_storage_compare_and_set(kind, &key, &old_value, &new_value))
.ok_or_else(|| "Calling unavailable API ext_local_storage_compare_andset: wasm")?;
Ok(if res { 0 } else { 1 })
},
ext_http_request_start(
method: *const u8,
method_len: u32,
@@ -1365,6 +1394,7 @@ mod tests {
use state_machine::TestExternalities as CoreTestExternalities;
use hex_literal::hex;
use primitives::map;
use substrate_offchain::testing;
type TestExternalities<H> = CoreTestExternalities<H, u64>;
@@ -1550,4 +1580,45 @@ mod tests {
ordered_trie_root::<Blake2Hasher, _, _>(vec![b"zero".to_vec(), b"one".to_vec(), b"two".to_vec()].iter()).as_fixed_bytes().encode()
);
}
#[test]
fn offchain_local_storage_should_work() {
use substrate_client::backend::OffchainStorage;
let mut ext = TestExternalities::<Blake2Hasher>::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.set_offchain_externalities(offchain);
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!(
WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_offchain_local_storage", &[]).unwrap(),
vec![0]
);
assert_eq!(state.read().persistent_storage.get(b"", b"test"), Some(vec![]));
}
#[test]
fn offchain_http_should_work() {
let mut ext = TestExternalities::<Blake2Hasher>::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.set_offchain_externalities(offchain);
state.write().expect_request(
0,
testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:12345".into(),
body: vec![1, 2, 3, 4],
headers: vec![("X-Auth".to_owned(), "test".to_owned())],
sent: true,
response: vec![1, 2, 3],
response_headers: vec![("X-Auth".to_owned(), "hello".to_owned())],
..Default::default()
},
);
let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm");
assert_eq!(
WasmExecutor::new().call(&mut ext, 8, &test_code[..], "test_offchain_http", &[]).unwrap(),
vec![0]
);
}
}