Skip to content

Commit 56c5c17

Browse files
authored
Merge pull request #5356 from igor-casper/c-197
[VM2] Update keyspace variants, store contract state per-field
2 parents b36023a + 9130ac0 commit 56c5c17

File tree

23 files changed

+801
-337
lines changed

23 files changed

+801
-337
lines changed

execution_engine_testing/tests/src/test/explorer/faucet.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ fn faucet_costs() {
663663
// This test will fail if execution costs vary. The expected costs should not be updated
664664
// without understanding why the cost has changed. If the costs do change, it should be
665665
// reflected in the "Costs by Entry Point" section of the faucet crate's README.md.
666-
const EXPECTED_FAUCET_INSTALL_COST: u64 = 149_415_277_468;
666+
const EXPECTED_FAUCET_INSTALL_COST: u64 = 152_120_952_265;
667667
const EXPECTED_FAUCET_INSTALL_COST_ALT: u64 = 149_230_872_143;
668668

669669
const EXPECTED_FAUCET_SET_VARIABLES_COST: u64 = 79_455_975;

executor/wasm/tests/hello_world.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use casper_storage::{
1616
global_state::state::{lmdb::LmdbGlobalState, CommitProvider, StateProvider},
1717
AddressGenerator,
1818
};
19-
use casper_types::{BlockHash, Digest, EntityAddr, Key, Timestamp};
19+
use casper_types::{
20+
addressable_entity::StateFieldAddr, BlockHash, Digest, EntityAddr, Key, Timestamp,
21+
};
2022
use once_cell::sync::Lazy;
2123
use parking_lot::{lock_api::RwLock, RawRwLock};
2224
use tempfile::TempDir;
@@ -45,9 +47,14 @@ fn should_store_initial_state() {
4547
.commit_effects(state_root_hash, create_result.effects().clone())
4648
.expect("Should commit");
4749

50+
let field_name = "greeting";
51+
let digest = Digest::hash(field_name.as_bytes());
4852
let value = match global_state.query(QueryRequest::new(
4953
post_state_root_hash,
50-
Key::State(contract_hash),
54+
Key::State(StateFieldAddr::new_state_field_addr(
55+
contract_hash,
56+
digest.value(),
57+
)),
5158
vec![],
5259
)) {
5360
QueryResult::Success { value, proofs: _ } => value,
@@ -122,9 +129,14 @@ fn should_store_state_after_changes() {
122129
.commit_effects(post_state_root_hash, execution_result.effects().clone())
123130
.expect("Should commit");
124131

132+
let field_name = "greeting";
133+
let digest = Digest::hash(field_name.as_bytes());
125134
let value = match global_state.query(QueryRequest::new(
126135
post_state_root_hash,
127-
Key::State(contract_hash),
136+
Key::State(StateFieldAddr::new_state_field_addr(
137+
contract_hash,
138+
digest.value(),
139+
)),
128140
vec![],
129141
)) {
130142
QueryResult::Success { value, proofs: _ } => value,

executor/wasm/tests/integration.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ use casper_storage::{
4646

4747
use casper_types::{
4848
account::AccountHash,
49+
addressable_entity::StateFieldAddr,
4950
bytesrepr::ToBytes,
5051
contract_messages::{Message, MessageChecksum, MessagePayload},
5152
execution::{RetValue, TransformKindV2, TransformV2},
@@ -887,16 +888,24 @@ fn counter() {
887888
.commit_effects(state_root_hash, create_result.effects().clone())
888889
.expect("Should commit");
889890

890-
let query_request = QueryRequest::new(state_root_hash, Key::State(contract_hash), vec![]);
891+
let field_tail = Digest::hash(b"value").value();
892+
let query_request = QueryRequest::new(
893+
state_root_hash,
894+
Key::State(StateFieldAddr::new_state_field_addr(
895+
contract_hash,
896+
field_tail,
897+
)),
898+
vec![],
899+
);
891900
match global_state.query(query_request) {
892901
QueryResult::RootNotFound | QueryResult::ValueNotFound(_) | QueryResult::Failure(_) => {
893902
panic!("query failed");
894903
}
895904
QueryResult::Success { value, .. } => {
896905
if let StoredValue::CLValue(cl_value) = *value {
897-
let counter: (u32,) =
906+
let counter: u32 =
898907
borsh::from_slice(cl_value.inner_bytes()).expect("should deserialize");
899-
assert_eq!(counter.0, 0u32, "should be 0");
908+
assert_eq!(counter, 0u32, "should be 0");
900909
} else {
901910
println!("{:?}", value);
902911
panic!("wrong stored value variant");
@@ -973,16 +982,23 @@ fn counter() {
973982
None => panic!("get should have output"),
974983
}
975984

976-
let query_request = QueryRequest::new(state_root_hash, Key::State(contract_hash), vec![]);
985+
let query_request = QueryRequest::new(
986+
state_root_hash,
987+
Key::State(StateFieldAddr::new_state_field_addr(
988+
contract_hash,
989+
field_tail,
990+
)),
991+
vec![],
992+
);
977993
match global_state.query(query_request) {
978994
QueryResult::RootNotFound | QueryResult::ValueNotFound(_) | QueryResult::Failure(_) => {
979995
panic!("query failed");
980996
}
981997
QueryResult::Success { value, .. } => {
982998
if let StoredValue::CLValue(cl_value) = *value {
983-
let counter: (u32,) =
999+
let counter: u32 =
9841000
borsh::from_slice(cl_value.inner_bytes()).expect("should deserialize");
985-
assert_eq!(counter.0, 1u32, "should be 1");
1001+
assert_eq!(counter, 1u32, "should be 1");
9861002
} else {
9871003
println!("{:?}", value);
9881004
panic!("wrong stored value variant");
@@ -1025,16 +1041,23 @@ fn counter() {
10251041
.commit_effects(state_root_hash, result_3.effects().clone())
10261042
.expect("Should commit");
10271043

1028-
let query_request = QueryRequest::new(state_root_hash, Key::State(contract_hash), vec![]);
1044+
let query_request = QueryRequest::new(
1045+
state_root_hash,
1046+
Key::State(StateFieldAddr::new_state_field_addr(
1047+
contract_hash,
1048+
field_tail,
1049+
)),
1050+
vec![],
1051+
);
10291052
match global_state.query(query_request) {
10301053
QueryResult::RootNotFound | QueryResult::ValueNotFound(_) | QueryResult::Failure(_) => {
10311054
panic!("query failed");
10321055
}
10331056
QueryResult::Success { value, .. } => {
10341057
if let StoredValue::CLValue(cl_value) = *value {
1035-
let counter: (u32,) =
1058+
let counter: u32 =
10361059
borsh::from_slice(cl_value.inner_bytes()).expect("should deserialize");
1037-
assert_eq!(counter.0, 0u32, "should be 0");
1060+
assert_eq!(counter, 0u32, "should be 0");
10381061
} else {
10391062
println!("{:?}", value);
10401063
panic!("wrong stored value variant");

executor/wasm_common/src/keyspace.rs

Lines changed: 88 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,109 @@
1+
use borsh::{BorshDeserialize, BorshSerialize};
12
use num_derive::{FromPrimitive, ToPrimitive};
23

4+
/// Discriminant indicating which keyspace is being accessed.
35
#[repr(u64)]
46
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
57
pub enum KeyspaceTag {
6-
/// Used for a state based storage which usually involves single dimensional data i.e.
7-
/// key-value pairs, etc.
8-
///
9-
/// See also [`Keyspace::State`].
10-
State = 0,
11-
/// Used for a context based storage which usually involves multi dimensional data i.e. maps,
12-
/// efficient vectors, etc.
13-
Context = 1,
14-
/// Used for a named key based storage which usually involves named keys.
15-
NamedKey = 2,
16-
/// Used for getting all named keys
17-
AllNamedKeys = 4,
8+
/// Context-based storage addressing using a structured address.
9+
Context = 0,
10+
/// Named key based storage which usually involves human-readable names.
11+
NamedKey = 1,
12+
}
13+
14+
/// Discriminant indicating which collection type is being used.
15+
#[repr(u8)]
16+
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
17+
pub enum CollectionTypeTag {
18+
/// A key-value mapping collection.
19+
Map = 0,
20+
/// A set collection for unique elements.
21+
Set = 1,
22+
/// A vector collection.
23+
Vector = 2,
24+
/// An iterable map collection.
25+
IterableMap = 3,
26+
}
27+
28+
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
29+
pub enum ContextAddr {
30+
/// Address of a state field for a given entity.
31+
StateAddr(StateAddrInner),
32+
/// Address of a collection element for a given entity.
33+
CollectionAddr(CollectionAddrInner),
34+
}
35+
36+
/// Address for a specific state field owned by `entity_addr`.
37+
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
38+
pub struct StateAddrInner {
39+
pub field_addr: String,
40+
}
41+
42+
impl StateAddrInner {
43+
#[inline]
44+
pub fn new<T: Into<String>>(field_addr: T) -> Self {
45+
Self {
46+
field_addr: field_addr.into(),
47+
}
48+
}
49+
}
50+
51+
/// Address for a collection element owned by `entity_addr`.
52+
///
53+
/// The `collection_type_tag` identifies the collection kind (e.g., map, set, vector).
54+
/// The `collection_prefix` is an 8-byte collection-level namespace derived from the collection
55+
/// name. The `tail` is a 32-byte element-level discriminator (e.g., hashed key or index).
56+
#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
57+
pub struct CollectionAddrInner {
58+
pub entity_addr: [u8; 32],
59+
pub collection_type_tag: u8,
60+
pub collection_prefix: [u8; 8],
61+
pub tail: [u8; 32],
62+
}
63+
64+
impl CollectionAddrInner {
65+
pub fn new(
66+
entity_addr: [u8; 32],
67+
collection_type_tag: CollectionTypeTag,
68+
collection_prefix: [u8; 8],
69+
tail: [u8; 32],
70+
) -> Self {
71+
Self {
72+
entity_addr,
73+
collection_type_tag: collection_type_tag as u8,
74+
collection_prefix,
75+
tail,
76+
}
77+
}
78+
}
79+
80+
impl From<StateAddrInner> for ContextAddr {
81+
fn from(value: StateAddrInner) -> Self {
82+
ContextAddr::StateAddr(value)
83+
}
84+
}
85+
86+
impl From<CollectionAddrInner> for ContextAddr {
87+
fn from(value: CollectionAddrInner) -> Self {
88+
ContextAddr::CollectionAddr(value)
89+
}
1890
}
1991

2092
#[repr(u64)]
21-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93+
#[derive(Debug, Clone, PartialEq, Eq)]
2294
pub enum Keyspace<'a> {
23-
/// Stores contract's context.
24-
///
25-
/// There's no additional payload for this variant as the host implies the contract's address.
26-
State,
27-
/// Stores contract's context data. Bytes can be any value as long as it uniquely identifies a
28-
/// value.
29-
Context(&'a [u8]),
30-
/// Stores contract's named keys.
95+
/// Structured context address.
96+
Context(ContextAddr),
97+
/// Human-readable named key.
3198
NamedKey(&'a str),
32-
/// All the named keys for the given contract
33-
///
34-
/// No additional info as the contracts address will be used as the base.
35-
AllNamedKeys,
3699
}
37100

38101
impl Keyspace<'_> {
39102
#[must_use]
40103
pub fn as_tag(&self) -> KeyspaceTag {
41104
match self {
42-
Keyspace::State => KeyspaceTag::State,
43105
Keyspace::Context(_) => KeyspaceTag::Context,
44106
Keyspace::NamedKey(_) => KeyspaceTag::NamedKey,
45-
Keyspace::AllNamedKeys => KeyspaceTag::AllNamedKeys,
46107
}
47108
}
48109

@@ -51,48 +112,3 @@ impl Keyspace<'_> {
51112
self.as_tag() as u64
52113
}
53114
}
54-
55-
#[cfg(test)]
56-
mod tests {
57-
use super::*;
58-
59-
#[test]
60-
fn test_as_tag_state() {
61-
let keyspace = Keyspace::State;
62-
assert_eq!(keyspace.as_tag(), KeyspaceTag::State);
63-
}
64-
65-
#[test]
66-
fn test_as_tag_context() {
67-
let data = [1, 2, 3];
68-
let keyspace = Keyspace::Context(&data);
69-
assert_eq!(keyspace.as_tag(), KeyspaceTag::Context);
70-
}
71-
72-
#[test]
73-
fn test_as_tag_named_key() {
74-
let name = "my_key";
75-
let keyspace = Keyspace::NamedKey(name);
76-
assert_eq!(keyspace.as_tag(), KeyspaceTag::NamedKey);
77-
}
78-
79-
#[test]
80-
fn test_as_u64_state() {
81-
let keyspace = Keyspace::State;
82-
assert_eq!(keyspace.as_u64(), 0);
83-
}
84-
85-
#[test]
86-
fn test_as_u64_context() {
87-
let data = [1, 2, 3];
88-
let keyspace = Keyspace::Context(&data);
89-
assert_eq!(keyspace.as_u64(), 1);
90-
}
91-
92-
#[test]
93-
fn test_as_u64_named_key() {
94-
let name = "my_key";
95-
let keyspace = Keyspace::NamedKey(name);
96-
assert_eq!(keyspace.as_u64(), 2);
97-
}
98-
}

executor/wasm_common/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ pub mod entry_point;
55
pub mod error;
66
pub mod flags;
77
pub mod keyspace;
8+
9+
pub use keyspace::{CollectionTypeTag, KeyspaceTag};

0 commit comments

Comments
 (0)