Commit b7ca7525 authored by Andrew Jones's avatar Andrew Jones Committed by Hero Bird

Read runtime storage value using `ext_get_runtime_storage` (#204)

* [core] add ext_runtime_get_storage

* [core] env2: add ext_runtime_get_storage

* [model] add runtime_get_storage to EnvHandler

* [core/model] fix up compiler errors

* [examples] add example reading from runtime storage

* [examples] add debug statements to the runtime contract

* Add lang2 example for runtime_get_storage

* Hook up lang2 runtime_get_storage, make example compile

* Fix lang2 example generate abi

* Rename to match substrate ext function

* Remove ink 1.0 impl

* Remove remaining ink 1.0 impl

* Rename to get_runtime_storage

* Add some logging contract

* TEMPORARY: diagnostic logging

* Revert "TEMPORARY: diagnostic logging"

This reverts commit 14bfc6e2

* Set storage keys

* More contract diagnosis

* Construct storage key in contract

* More key logging

* Fix balances prefix

* Remove all printlns

* Implement test env accessor for chain storage

* Add back space

* Correct docs

* rustfmt

* spacing

* rustfmt

* Capitalise comment, return correct error in test env

* Update license to Apache 2.0
parent 8165b244
Pipeline #69682 passed with stages
in 25 minutes and 7 seconds
......@@ -322,5 +322,15 @@ where
/// Prints the given contents to the environmental log.
fn println(&self, content: &str);
/// Returns the value from the *runtime* storage at the position of the key.
///
/// # Errors
///
/// - If the key's entry is empty
/// - If the decoding of the typed value failed
fn get_runtime_storage<R>(&self, key: &[u8]) -> Result<R>
where
R: scale::Decode;
}
}
......@@ -321,4 +321,17 @@ where
pub fn println(&mut self, content: &str) {
T::println(content)
}
/// Returns the value from the *runtime* storage at the position of the key.
///
/// # Errors
///
/// - If the key's entry is empty
/// - If the decoding of the typed value failed
pub fn get_runtime_storage<R>(&mut self, key: &[u8]) -> Result<R>
where
R: scale::Decode,
{
T::get_runtime_storage(&mut self.buffer, key)
}
}
......@@ -85,6 +85,10 @@ pub fn get_storage(key: &[u8]) -> RetCode {
unsafe { sys::ext_get_storage(key.as_ptr() as u32) }.into()
}
pub fn get_runtime_storage(key: &[u8]) -> RetCode {
unsafe { sys::ext_get_runtime_storage(key.as_ptr() as u32, key.len() as u32) }.into()
}
pub fn restore_to(
dest: &[u8],
code_hash: &[u8],
......@@ -198,6 +202,8 @@ mod sys {
);
pub fn ext_get_storage(key_ptr: u32) -> u32;
pub fn ext_get_runtime_storage(key_ptr: u32, key_len: u32) -> u32;
pub fn ext_restore_to(
dest_ptr: u32,
dest_len: u32,
......
......@@ -413,4 +413,22 @@ where
fn println(content: &str) {
ext::println(content)
}
fn get_runtime_storage<I, R>(buffer: &mut I, key: &[u8]) -> Result<R>
where
I: AsMut<[u8]> + EnlargeTo,
R: scale::Decode,
{
let ret = ext::get_runtime_storage(key);
if !ret.is_success() {
return Err(Error::InvalidStorageKey)
}
let req_len = ext::scratch_size();
buffer.enlarge_to(req_len);
let ret = ext::scratch_read(&mut buffer.as_mut()[0..req_len], 0);
if !ret.is_success() {
return Err(Error::InvalidStorageRead)
}
Decode::decode(&mut &buffer.as_mut()[0..req_len]).map_err(Into::into)
}
}
......@@ -507,4 +507,22 @@ where
fn println(content: &str) {
println!("{}", content)
}
fn get_runtime_storage<I, R>(_buffer: &mut I, key: &[u8]) -> Result<R>
where
I: AsMut<[u8]> + EnlargeTo,
R: scale::Decode,
{
INSTANCE.with(|instance| {
let storage = &instance.borrow().state.storage;
// Raw runtime storage keys can be of any length, so hash to fit 32 byte Key size
let key = Key(ink_utils::hash::keccak256(key));
let encoded = storage
.read(key)
.map(|entry| entry.data())
.ok_or(Error::InvalidStorageKey)?;
Ok(scale::Decode::decode(&mut &encoded[..])
.map_err(|_| Error::InvalidStorageRead)?)
})
}
}
......@@ -185,6 +185,17 @@ pub trait Env:
/// In fact production chains will generally reject contracts upon deploy
/// that make use of this functionality.
fn println(content: &str);
/// Returns the value from the *runtime* storage at the position of the key.
///
/// # Errors
///
/// - If `key` associates no elements.
/// - If the element at `key` could not be decoded into `T`.
fn get_runtime_storage<I, T>(buffer: &mut I, key: &[u8]) -> Result<T>
where
I: AsMut<[u8]> + EnlargeTo,
T: scale::Decode;
}
/// Implemented by event types to communicate their topic hashes.
......
[target.wasm32-unknown-unknown]
rustflags = [
"-C", "link-args=-z stack-size=65536 --import-memory"
]
\ No newline at end of file
# Ignore build artifacts from the local tests sub-crate.
/target/
# Ignore backup files creates by cargo fmt.
**/*.rs.bk
# Remove Cargo.lock when creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
\ No newline at end of file
[package]
name = "abi-gen"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
publish = false
[[bin]]
name = "abi-gen"
path = "main.rs"
[dependencies]
contract = { path = "../..", package = "runtime_storage", features = ["ink-generate-abi", "test-env"] }
ink_lang2 = { path = "../../../../../lang2", default-features = false, features = ["ink-generate-abi"] }
serde = "1.0"
serde_json = "1.0"
fn main() -> Result<(), std::io::Error> {
let abi = <contract::RuntimeStorage as ink_lang2::GenerateAbi>::generate_abi();
let contents = serde_json::to_string_pretty(&abi)?;
std::fs::create_dir("target").ok();
std::fs::write("target/metadata.json", contents)?;
Ok(())
}
[package]
name = "runtime_storage"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
[dependencies]
ink_abi = { path = "../../../abi", default-features = false, features = ["derive"], optional = true }
ink_core = { path = "../../../core", default-features = false }
ink_lang2 = { path = "../../../lang2", default-features = false }
blake2-rfc = { version = "0.2.18", default-features = false }
scale = { package = "parity-scale-codec", version = "1.1", default-features = false, features = ["derive"] }
type-metadata = { git = "https://github.com/type-metadata/type-metadata.git", default-features = false, features = ["derive"], optional = true }
[lib]
name = "runtime_storage"
crate-type = [
# Used for normal contract Wasm blobs.
"cdylib",
# Used for ABI generation.
"rlib",
]
[features]
default = ["test-env"]
std = [
"blake2-rfc/std",
"ink_abi/std",
"ink_core/std",
"scale/std",
"type-metadata/std",
]
test-env = [
"std",
"ink_core/test-env",
"ink_lang2/test-env",
]
ink-generate-abi = [
"std",
"ink_abi",
"type-metadata",
"ink_core/ink-generate-abi",
"ink_lang2/ink-generate-abi",
]
ink-as-dependency = []
[profile.release]
panic = "abort"
lto = true
opt-level = "z"
overflow-checks = true
[workspace]
members = [
".ink/abi_gen"
]
exclude = [
".ink"
]
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![feature(proc_macro_hygiene)]
#![cfg_attr(not(feature = "std"), no_std)]
use ink_lang2 as ink;
use scale::KeyedVec as _;
mod crypto {
/// Do a Blake2 256-bit hash and place result in `dest`.
pub fn blake2_256_into(data: &[u8], dest: &mut [u8; 32]) {
dest.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], data).as_bytes());
}
/// Do a Blake2 256-bit hash and return result.
pub fn blake2_256(data: &[u8]) -> [u8; 32] {
let mut r = [0; 32];
blake2_256_into(data, &mut r);
r
}
}
#[ink::contract(version = "0.1.0")]
mod runtime {
/// This simple contract reads a value from runtime storage
#[ink(storage)]
struct RuntimeStorage {}
impl RuntimeStorage {
#[ink(constructor)]
fn new(&mut self) {}
/// Returns the account balance, read directly from runtime storage
#[ink(message)]
fn get_balance(&self, account: AccountId) -> Balance {
const BALANCE_OF: &[u8] = b"Balances FreeBalance";
let key = crypto::blake2_256(&account.to_keyed_vec(BALANCE_OF));
let result = self.env().get_runtime_storage::<Balance>(&key[..]);
result.unwrap_or_default()
}
}
#[cfg(all(test))]
mod tests {
use super::*;
#[test]
fn non_existent_account_returns_zero() {
let contract = RuntimeStorage::new();
let account: AccountId = [0u8; 32].into();
assert_eq!(contract.get_balance(account), 0);
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment