Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pallet-revive] immutable data storage #5861

Merged
merged 42 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
bfbd24c
introduce contract immutables to stack
xermicus Sep 25, 2024
0177976
Merge branch 'master' into cl/immutables
xermicus Sep 25, 2024
589097a
setter
xermicus Sep 25, 2024
eb8999e
test the immutable data access checks
xermicus Sep 25, 2024
cec4e41
uapi
xermicus Sep 25, 2024
9eb9820
setter updates contract info
xermicus Sep 25, 2024
1cdffd4
terminate can prune the immutable data
xermicus Sep 26, 2024
a61a7b9
charge for immutable data in constructor and set_code_hash
xermicus Sep 26, 2024
b9c348d
disable the set_code_hash API to fix it later
xermicus Sep 26, 2024
0bb6f79
add
xermicus Sep 26, 2024
3c8a357
delegate call test
xermicus Sep 26, 2024
cee50df
Merge branch 'master' into cl/immutables
xermicus Sep 26, 2024
66618ff
bench
xermicus Sep 27, 2024
0290c55
implement optimizations later
xermicus Sep 27, 2024
9f539e8
storage limits
xermicus Sep 27, 2024
86bc702
Merge branch 'master' into cl/immutables
xermicus Sep 27, 2024
f85b18b
prdoc
xermicus Sep 27, 2024
6f3aa5e
".git/.scripts/commands/fmt/fmt.sh"
Sep 27, 2024
6ea3327
Merge branch 'master' into cl/immutables
xermicus Oct 3, 2024
f325b72
Update substrate/frame/revive/src/lib.rs
xermicus Oct 3, 2024
6fd2d01
private field and comments
xermicus Oct 3, 2024
cafbc2f
only charge the storage item on instantiate
xermicus Oct 3, 2024
068c4e1
charge storage deposit for setting immutable data
xermicus Oct 3, 2024
cccf94c
precharge
xermicus Oct 3, 2024
d4fbe66
revert random fmt changes
xermicus Oct 3, 2024
4a8c49b
remove block
xermicus Oct 3, 2024
1257615
fix feature gate
xermicus Oct 3, 2024
a89761b
add another assertion
xermicus Oct 3, 2024
da46a70
Merge branch 'master' into cl/immutables
xermicus Oct 4, 2024
40a2e53
do not charge the immutable item if it was not set
xermicus Oct 4, 2024
d374385
Update substrate/frame/revive/src/storage/meter.rs
xermicus Oct 4, 2024
35b6e2a
".git/.scripts/commands/fmt/fmt.sh"
Oct 4, 2024
fe83697
add test for error conditions
xermicus Oct 4, 2024
294d380
Merge branch 'master' into cl/immutables
xermicus Oct 4, 2024
91a3eaa
fix docs
xermicus Oct 4, 2024
19ab687
start benchmarks at length 1 since empty immutable data is not valid
xermicus Oct 4, 2024
3122a77
explain all error conditions for InvalidImmutableAccess
xermicus Oct 4, 2024
2a4d34d
rename immutable_bytes to immutable_data_len
xermicus Oct 4, 2024
085d5e1
".git/.scripts/commands/fmt/fmt.sh"
Oct 4, 2024
15116a5
".git/.scripts/commands/bench/bench.sh" --subcommand=pallet --runtime…
Oct 4, 2024
c95025d
use instantiators everywhere
xermicus Oct 4, 2024
850b453
Merge branch 'master' into cl/immutables
xermicus Oct 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions prdoc/pr_5861.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
title: "[pallet-revive] immutable data storage"

doc:
- audience: Runtime Dev
description: |
This PR introduces the concept of immutable storage data, used for
[Solidity immutable variables](https://docs.soliditylang.org/en/latest/contracts.html#immutable).

This is a minimal implementation. Immutable data is attached to a contract; to
`ContractInfo` fixed in size, we only store the length there, and store the immutable
data in a dedicated storage map instead. Which comes at the cost of requiring an
storage read (costly) for contracts using this feature.

We discussed more optimal solutions not requiring any additional storage accesses
internally, but they turned out to be non-trivial to implement. Another optimization
benefiting multiple calls to the same contract in a single call stack would be to cache
the immutable data in `Stack`. However, this potential creates a DOS vulnerability (the
attack vector is to call into as many contracts in a single stack as possible, where
they all have maximum immutable data to fill the cache as efficiently as possible). So
this either has to be guaranteed to be a non-issue by limits, or, more likely, to have
some logic to bound the cache. Eventually, we should think about introducing the concept
of warm and cold storage reads (akin to EVM). Since immutable variables are commonly
used in contracts, this change is blocking our initial launch and we should only
optimize it properly in follow-ups.

This PR also disables the `set_code_hash` API (which isn't usable for Solidity contracts
without pre-compiles anyways). With immutable storage attached to contracts, we now want
to run the constructor of the new code hash to collect the immutable data during
`set_code_hash`. This will be implemented in a follow up PR.

crates:
- name: pallet-revive
bump: major
- name: pallet-revive-fixtures
bump: patch
- name: pallet-revive-uapi
bump: minor
43 changes: 43 additions & 0 deletions substrate/frame/revive/fixtures/contracts/immutable_data.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// 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.

//! Tests that the `get_immutable_data` and `set_immutable_data` APIs work.

#![no_std]
#![no_main]

use common::input;
use uapi::{HostFn, HostFnImpl as api};

#[no_mangle]
#[polkavm_derive::polkavm_export]
pub extern "C" fn deploy() {
input!(data: &[u8; 8],);

api::set_immutable_data(data);
}

#[no_mangle]
#[polkavm_derive::polkavm_export]
pub extern "C" fn call() {
input!(data: &[u8; 8],);

let mut buf = [0; 8];
api::get_immutable_data(&mut &mut buf[..]);

assert_eq!(data, &buf);
}
47 changes: 47 additions & 0 deletions substrate/frame/revive/src/benchmarking/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,53 @@ mod benchmarks {
assert_eq!(U256::from_little_endian(&memory[..len]), runtime.ext().balance_of(&address));
}

#[benchmark(pov_mode = Measured)]
fn seal_get_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
let len = n as usize;
let immutable_data = vec![1u8; len];

build_runtime!(runtime, contract, memory: [(len as u32).encode(), vec![0u8; len],]);

<ImmutableDataOf<T>>::insert::<_, BoundedVec<_, _>>(
contract.address(),
immutable_data.clone().try_into().unwrap(),
);

let result;
#[block]
{
result = runtime.bench_get_immutable_data(memory.as_mut_slice(), 4, 0 as u32);
}

assert_ok!(result);
assert_eq!(&memory[0..4], (len as u32).encode());
assert_eq!(&memory[4..len + 4], &immutable_data);
}

#[benchmark(pov_mode = Measured)]
fn seal_set_immutable_data(n: Linear<1, { limits::IMMUTABLE_BYTES }>) {
let len = n as usize;
let mut memory = vec![1u8; len];
let mut setup = CallSetup::<T>::default();
let input = setup.data();
let (mut ext, _) = setup.ext();
ext.override_export(crate::debug::ExportedFunction::Constructor);

let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, input);

let result;
#[block]
{
result = runtime.bench_set_immutable_data(memory.as_mut_slice(), 0, n);
}

assert_ok!(result);
assert_eq!(
&memory[..],
&<ImmutableDataOf<T>>::get(setup.contract().address()).unwrap()[..]
);
}

#[benchmark(pov_mode = Measured)]
fn seal_value_transferred() {
build_runtime!(runtime, memory: [[0u8;32], ]);
Expand Down
Loading