-
Notifications
You must be signed in to change notification settings - Fork 224
/
Copy pathlmdb_fixture.rs
150 lines (128 loc) · 5.15 KB
/
lmdb_fixture.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
use std::{
env,
fs::File,
io::Write,
path::{Path, PathBuf},
};
use fs_extra::dir;
use serde::{Deserialize, Serialize};
use serde_json::json;
use tempfile::TempDir;
use casper_engine_test_support::LmdbWasmTestBuilder;
use casper_execution_engine::core::engine_state::{
run_genesis_request::RunGenesisRequest, EngineConfig,
};
use casper_hashing::Digest;
use casper_types::ProtocolVersion;
#[cfg(test)]
use casper_types::{AccessRights, Key, URef};
pub const RELEASE_1_2_0: &str = "release_1_2_0";
pub const RELEASE_1_3_1: &str = "release_1_3_1";
pub const RELEASE_1_4_2: &str = "release_1_4_2";
pub const RELEASE_1_4_3: &str = "release_1_4_3";
pub const RELEASE_1_4_4: &str = "release_1_4_4";
pub const RELEASE_1_4_5: &str = "release_1_4_5";
const STATE_JSON_FILE: &str = "state.json";
const FIXTURES_DIRECTORY: &str = "fixtures";
const GENESIS_PROTOCOL_VERSION_FIELD: &str = "protocol_version";
#[cfg(test)]
const RUN_FIXTURE_GENERATORS_ENV: &str = "RUN_FIXTURE_GENERATORS";
#[cfg(test)]
pub(crate) fn is_fixture_generator_enabled() -> bool {
env::var_os(RUN_FIXTURE_GENERATORS_ENV).is_some()
}
/// This is a special place in the global state where fixture contains a registry.
#[cfg(test)]
pub(crate) const CONTRACT_REGISTRY_SPECIAL_ADDRESS: Key =
Key::URef(URef::new([0u8; 32], AccessRights::all()));
fn path_to_lmdb_fixtures() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join(FIXTURES_DIRECTORY)
}
/// Contains serialized genesis config.
#[derive(Serialize, Deserialize)]
pub struct LmdbFixtureState {
/// Serializes as unstructured JSON value because [`RunGenesisRequest`] might change over time
/// and likely old fixture might not deserialize cleanly in the future.
pub genesis_request: serde_json::Value,
pub post_state_hash: Digest,
}
impl LmdbFixtureState {
pub fn genesis_protocol_version(&self) -> ProtocolVersion {
serde_json::from_value(
self.genesis_request
.get(GENESIS_PROTOCOL_VERSION_FIELD)
.cloned()
.unwrap(),
)
.expect("should have protocol version field")
}
}
/// Creates a [`LmdbWasmTestBuilder`] from a named fixture directory.
///
/// As part of this process a new temporary directory will be created to store LMDB files from given
/// fixture, and a builder will be created using it.
///
/// This function returns a triple of the builder, a [`LmdbFixtureState`] which contains serialized
/// genesis request for given fixture, and a temporary directory which has to be kept in scope.
pub fn builder_from_global_state_fixture(
fixture_name: &str,
) -> (LmdbWasmTestBuilder, LmdbFixtureState, TempDir) {
let source = path_to_lmdb_fixtures().join(fixture_name);
let to = tempfile::tempdir().expect("should create temp dir");
fs_extra::copy_items(&[source], &to, &dir::CopyOptions::default())
.expect("should copy global state fixture");
let path_to_state = to.path().join(fixture_name).join(STATE_JSON_FILE);
let lmdb_fixture_state: LmdbFixtureState =
serde_json::from_reader(File::open(path_to_state).unwrap()).unwrap();
let path_to_gs = to.path().join(fixture_name);
(
LmdbWasmTestBuilder::open(
&path_to_gs,
EngineConfig::default(),
lmdb_fixture_state.post_state_hash,
),
lmdb_fixture_state,
to,
)
}
/// Creates a new fixture with a name.
///
/// This process is currently manual. The process to do this is to check out a release branch, call
/// this function to generate (i.e. `generate_fixture("release_1_3_0")`) and persist it in version
/// control.
pub fn generate_fixture(
name: &str,
genesis_request: RunGenesisRequest,
post_genesis_setup: impl FnOnce(&mut LmdbWasmTestBuilder),
) -> Result<(), Box<dyn std::error::Error>> {
let lmdb_fixtures_root = path_to_lmdb_fixtures();
let fixture_root = lmdb_fixtures_root.join(name);
let path_to_data_lmdb = fixture_root.join("global_state").join("data.lmdb");
if path_to_data_lmdb.exists() {
eprintln!(
"Lmdb fixture located at {} already exists. If you need to re-generate a fixture to ensure a serialization \
changes are backwards compatible please make sure you are running a specific version, or a past commit. \
Skipping.",
path_to_data_lmdb.display()
);
return Ok(());
}
let engine_config = EngineConfig::default();
let mut builder = LmdbWasmTestBuilder::new_with_config(&fixture_root, engine_config);
builder.run_genesis(&genesis_request);
// You can customize the fixture post genesis with a callable.
post_genesis_setup(&mut builder);
let post_state_hash = builder.get_post_state_hash();
let genesis_request_json = json!({
GENESIS_PROTOCOL_VERSION_FIELD: genesis_request.protocol_version(),
});
let state = LmdbFixtureState {
genesis_request: genesis_request_json,
post_state_hash,
};
let serialized_state = serde_json::to_string_pretty(&state)?;
let path_to_state_file = fixture_root.join(STATE_JSON_FILE);
let mut f = File::create(path_to_state_file)?;
f.write_all(serialized_state.as_bytes())?;
Ok(())
}