Skip to content

Commit 6daae99

Browse files
committed
[feature] #3168: Provide genesis validator in separate file
Signed-off-by: Shanin Roman <shanin1000@yandex.ru>
1 parent fecea84 commit 6daae99

File tree

17 files changed

+266
-73
lines changed

17 files changed

+266
-73
lines changed

Cargo.lock

-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/src/samples.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,5 @@ where
123123
.optimize()?
124124
.into_bytes();
125125

126-
Ok(Validator::new(WasmSmartContract::new(wasm_blob)))
126+
Ok(Validator::new(WasmSmartContract::from_compiled(wasm_blob)))
127127
}

client/tests/integration/events/data.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ fn wasm_execution_should_produce_events() -> Result<()> {
8585
isi_calls = isi_calls
8686
);
8787

88-
transaction_execution_should_produce_events(WasmSmartContract::new(wat.into_bytes()), 10_615)
88+
transaction_execution_should_produce_events(
89+
WasmSmartContract::from_compiled(wat.into_bytes()),
90+
10_615,
91+
)
8992
}
9093

9194
fn transaction_execution_should_produce_events(

client/tests/integration/triggers/time_trigger.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ fn mint_nft_for_every_user_every_1_sec() -> Result<()> {
225225
let register_trigger = RegisterBox::new(Trigger::new(
226226
"mint_nft_for_all".parse()?,
227227
Action::new(
228-
WasmSmartContract::new(wasm),
228+
WasmSmartContract::from_compiled(wasm),
229229
Repeats::Indefinitely,
230230
alice_id.clone(),
231231
FilterBox::Time(TimeEventFilter::new(ExecutionTime::Schedule(schedule))),

client/tests/integration/upgrade.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn validator_upgrade_should_work() -> Result<()> {
5656

5757
info!("WASM size is {} bytes", wasm.len());
5858

59-
let upgrade_validator = UpgradeBox::new(Validator::new(WasmSmartContract::new(wasm)));
59+
let upgrade_validator = UpgradeBox::new(Validator::new(WasmSmartContract::from_compiled(wasm)));
6060
client.submit_blocking(upgrade_validator)?;
6161

6262
// Check that admin can transfer alice's rose now

client_cli/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ mod wasm {
839839
};
840840

841841
submit(
842-
WasmSmartContract::new(raw_data),
842+
WasmSmartContract::from_compiled(raw_data),
843843
cfg,
844844
UnlimitedMetadata::new(),
845845
)

configs/peer/genesis.json

+3-1
Large diffs are not rendered by default.

configs/peer/validator.wasm

747 KB
Binary file not shown.

core/test_network/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,3 @@ rand = "0.8.5"
2828
tempfile = "3.3.0"
2929
tokio = { version = "1.23.0", features = ["rt", "rt-multi-thread", "macros"] }
3030
unique_port = "0.2.1"
31-
json5 = "0.4.1"

core/test_network/src/lib.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,9 @@ impl TestGenesis for GenesisNetwork {
7575

7676
// TODO: Fix this somehow. Probably we need to make `kagami` a library (#3253).
7777
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
78-
let content = std::fs::read_to_string(manifest_dir.join("../../configs/peer/genesis.json"))
79-
.expect("Failed to read data from configs/peer/genesis.json");
80-
let mut genesis: RawGenesisBlock =
81-
json5::from_str(&content).expect("Failed to deserialize genesis block from config");
78+
let mut genesis =
79+
RawGenesisBlock::from_path(manifest_dir.join("../../configs/peer/genesis.json"))
80+
.expect("Failed to deserialize genesis block from file");
8281

8382
let rose_definition_id = <AssetDefinition as Identifiable>::Id::from_str("rose#wonderland")
8483
.expect("valid names");

data_model/src/transaction.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ pub mod model {
171171
PartialOrd,
172172
Ord,
173173
Hash,
174-
Constructor,
175174
Decode,
176175
Encode,
177176
Deserialize,
@@ -395,6 +394,14 @@ impl AsRef<[u8]> for WasmSmartContract {
395394
}
396395
}
397396

397+
impl WasmSmartContract {
398+
/// Create [`Self`] from raw wasm bytes
399+
#[inline]
400+
pub const fn from_compiled(blob: Vec<u8>) -> Self {
401+
Self(blob)
402+
}
403+
}
404+
398405
impl TransactionBuilder {
399406
/// Construct [`Self`].
400407
#[inline]
@@ -1289,7 +1296,7 @@ mod tests {
12891296

12901297
#[test]
12911298
fn wasm_smart_contract_debug_repr_should_contain_just_len() {
1292-
let contract = WasmSmartContract::new(vec![0, 1, 2, 3, 4]);
1299+
let contract = WasmSmartContract::from_compiled(vec![0, 1, 2, 3, 4]);
12931300
assert_eq!(format!("{contract:?}"), "WASM binary(len = 5)");
12941301
}
12951302
}

docs/source/references/schema.json

+21-1
Original file line numberDiff line numberDiff line change
@@ -2979,7 +2979,7 @@
29792979
},
29802980
{
29812981
"name": "validator",
2982-
"type": "Validator"
2982+
"type": "ValidatorMode"
29832983
}
29842984
]
29852985
},
@@ -3672,6 +3672,26 @@
36723672
}
36733673
]
36743674
},
3675+
"ValidatorMode": {
3676+
"Enum": [
3677+
{
3678+
"name": "Path",
3679+
"type": "ValidatorPath"
3680+
},
3681+
{
3682+
"name": "Inline",
3683+
"type": "Validator"
3684+
}
3685+
]
3686+
},
3687+
"ValidatorPath": {
3688+
"Struct": [
3689+
{
3690+
"name": "validator_relative_path",
3691+
"type": "String"
3692+
}
3693+
]
3694+
},
36753695
"Value": {
36763696
"Enum": [
36773697
{

genesis/src/lib.rs

+115-26
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,26 @@
88
clippy::arithmetic_side_effects
99
)]
1010

11-
use std::{fmt::Debug, fs::File, io::BufReader, ops::Deref, path::Path};
11+
use std::{
12+
fmt::Debug,
13+
fs::{self, File},
14+
io::BufReader,
15+
ops::Deref,
16+
path::{Path, PathBuf},
17+
};
1218

13-
use derive_more::Deref;
14-
use eyre::{bail, eyre, Result, WrapErr};
19+
use derive_more::{Deref, From};
20+
use eyre::{bail, eyre, ErrReport, Result, WrapErr};
1521
use iroha_config::genesis::Configuration;
1622
use iroha_crypto::{KeyPair, PublicKey};
17-
#[cfg(not(test))]
18-
use iroha_data_model::validator::Validator;
1923
use iroha_data_model::{
2024
asset::AssetDefinition,
2125
prelude::{Metadata, *},
26+
validator::Validator,
2227
};
2328
use iroha_primitives::small::{smallvec, SmallVec};
2429
use iroha_schema::IntoSchema;
2530
use serde::{Deserialize, Serialize};
26-
#[cfg(test)]
27-
use MockValidator as Validator;
2831

2932
/// Time to live for genesis transactions.
3033
const GENESIS_TRANSACTIONS_TTL_MS: u64 = 100_000;
@@ -77,7 +80,10 @@ impl GenesisNetworkTrait for GenesisNetwork {
7780
let transactions_iter = raw_block.transactions.into_iter();
7881
#[cfg(not(test))]
7982
let transactions_iter = transactions_iter.chain(std::iter::once(GenesisTransaction {
80-
isi: SmallVec(smallvec![UpgradeBox::new(raw_block.validator).into()]),
83+
isi: SmallVec(smallvec![UpgradeBox::new(Validator::try_from(
84+
raw_block.validator
85+
)?)
86+
.into()]),
8187
}));
8288

8389
let transactions = transactions_iter
@@ -100,21 +106,93 @@ impl GenesisNetworkTrait for GenesisNetwork {
100106
}
101107
}
102108

103-
/// Mock of [`Validator`](iroha_data_model::validator::Validator) for unit tests
104-
///
105-
/// Aliased to `Validator` when `cfg(test)`.
106-
#[cfg(test)]
107-
#[derive(Debug, Clone, Copy, Deserialize, Serialize, IntoSchema)]
108-
#[repr(transparent)]
109-
pub struct MockValidator;
110-
111109
/// [`RawGenesisBlock`] is an initial block of the network
112110
#[derive(Debug, Clone, Deserialize, Serialize, IntoSchema)]
113111
pub struct RawGenesisBlock {
114112
/// Transactions
115113
pub transactions: SmallVec<[GenesisTransaction; 2]>,
116114
/// Runtime Validator
117-
pub validator: Validator,
115+
pub validator: ValidatorMode,
116+
}
117+
118+
/// Ways to provide validator either directly as base64 encoded string or as path to wasm file
119+
#[derive(Debug, Clone, Serialize, Deserialize, IntoSchema, From)]
120+
#[serde(untagged)]
121+
pub enum ValidatorMode {
122+
/// Path to validator wasm file
123+
// In the first place to initially try to parse path
124+
Path(ValidatorPath),
125+
/// Validator encoded as base64 string
126+
Inline(Validator),
127+
}
128+
129+
impl ValidatorMode {
130+
fn set_genesis_path(&mut self, genesis_path: impl AsRef<Path>) {
131+
if let Self::Path(path) = self {
132+
path.set_genesis_path(genesis_path);
133+
}
134+
}
135+
}
136+
137+
impl TryFrom<ValidatorMode> for Validator {
138+
type Error = ErrReport;
139+
140+
fn try_from(value: ValidatorMode) -> Result<Self> {
141+
match value {
142+
ValidatorMode::Inline(validator) => Ok(validator),
143+
ValidatorMode::Path(ValidatorPath {
144+
validator_path: relative_validator_path,
145+
}) => {
146+
let wasm = fs::read(&relative_validator_path)
147+
.wrap_err(format!("Failed to open {:?}", &relative_validator_path))?;
148+
Ok(Validator::new(WasmSmartContract::from_compiled(wasm)))
149+
}
150+
}
151+
}
152+
}
153+
154+
/// Path to the validator relative to genesis location
155+
#[derive(Debug, Clone, Serialize, Deserialize)]
156+
pub struct ValidatorPath {
157+
/// Path to validator.
158+
/// If path is absolute it will be used directly otherwise it will be treated as relative to genesis location.
159+
pub validator_path: PathBuf,
160+
}
161+
162+
// Manual implementation because we want `PathBuf` appear as `String` in schema
163+
impl iroha_schema::TypeId for ValidatorPath {
164+
fn id() -> String {
165+
"ValidatorPath".to_string()
166+
}
167+
}
168+
impl iroha_schema::IntoSchema for ValidatorPath {
169+
fn type_name() -> String {
170+
"ValidatorPath".to_string()
171+
}
172+
fn update_schema_map(map: &mut iroha_schema::MetaMap) {
173+
if !map.contains_key::<Self>() {
174+
map.insert::<Self>(iroha_schema::Metadata::Struct(
175+
iroha_schema::NamedFieldsMeta {
176+
declarations: vec![iroha_schema::Declaration {
177+
name: String::from(stringify!(validator_relative_path)),
178+
ty: core::any::TypeId::of::<String>(),
179+
}],
180+
},
181+
));
182+
<String as iroha_schema::IntoSchema>::update_schema_map(map);
183+
}
184+
}
185+
}
186+
187+
impl ValidatorPath {
188+
fn set_genesis_path(&mut self, genesis_path: impl AsRef<Path>) {
189+
let path_to_validator = genesis_path
190+
.as_ref()
191+
.parent()
192+
.expect("Genesis must be in some directory")
193+
.join(&self.validator_path);
194+
self.validator_path = path_to_validator;
195+
}
118196
}
119197

120198
impl RawGenesisBlock {
@@ -135,10 +213,12 @@ impl RawGenesisBlock {
135213
iroha_logger::warn!(%size, threshold = %Self::WARN_ON_GENESIS_GTE, "Genesis is quite large, it will take some time to apply it");
136214
}
137215
let reader = BufReader::new(file);
138-
serde_json::from_reader(reader).wrap_err(format!(
216+
let mut raw_genesis_block: Self = serde_json::from_reader(reader).wrap_err(format!(
139217
"Failed to deserialize raw genesis block from {:?}",
140218
&path
141-
))
219+
))?;
220+
raw_genesis_block.validator.set_genesis_path(path);
221+
Ok(raw_genesis_block)
142222
}
143223
}
144224

@@ -192,10 +272,10 @@ pub struct RawGenesisDomainBuilder<S> {
192272
}
193273

194274
mod validator_state {
195-
use super::Validator;
275+
use super::ValidatorMode;
196276

197-
#[cfg_attr(test, derive(Clone, Copy))]
198-
pub struct Set(pub Validator);
277+
#[cfg_attr(test, derive(Clone))]
278+
pub struct Set(pub ValidatorMode);
199279

200280
#[derive(Clone, Copy)]
201281
pub struct Unset;
@@ -217,10 +297,13 @@ impl RawGenesisBlockBuilder<validator_state::Unset> {
217297
}
218298

219299
/// Set the validator.
220-
pub fn validator(self, validator: Validator) -> RawGenesisBlockBuilder<validator_state::Set> {
300+
pub fn validator(
301+
self,
302+
validator: impl Into<ValidatorMode>,
303+
) -> RawGenesisBlockBuilder<validator_state::Set> {
221304
RawGenesisBlockBuilder {
222305
transaction: self.transaction,
223-
state: validator_state::Set(validator),
306+
state: validator_state::Set(validator.into()),
224307
}
225308
}
226309
}
@@ -323,6 +406,12 @@ mod tests {
323406

324407
use super::*;
325408

409+
fn dummy_validator() -> ValidatorMode {
410+
ValidatorMode::Path(ValidatorPath {
411+
validator_path: "./validator.wasm".into(),
412+
})
413+
}
414+
326415
#[test]
327416
#[allow(clippy::expect_used)]
328417
fn load_new_genesis_block() -> Result<()> {
@@ -338,7 +427,7 @@ mod tests {
338427
.domain("wonderland".parse()?)
339428
.account("alice".parse()?, alice_public_key)
340429
.finish_domain()
341-
.validator(MockValidator)
430+
.validator(dummy_validator())
342431
.build(),
343432
Some(
344433
&ConfigurationProxy {
@@ -373,7 +462,7 @@ mod tests {
373462
.finish_domain();
374463

375464
// In real cases validator should be constructed from a wasm blob
376-
let finished_genesis_block = genesis_builder.validator(MockValidator).build();
465+
let finished_genesis_block = genesis_builder.validator(dummy_validator()).build();
377466
{
378467
let domain_id: DomainId = "wonderland".parse().unwrap();
379468
assert_eq!(

scripts/check.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ case $1 in
88
exit 1
99
};;
1010
"genesis")
11-
cargo run --release --bin kagami -- genesis | diff - configs/peer/genesis.json || {
11+
cargo run --release --bin kagami -- genesis --compiled-validator-path ./validator.wasm | diff - configs/peer/genesis.json || {
1212
echo 'Please re-generate the genesis with `cargo run --release --bin kagami -- genesis > configs/peer/genesis.json`'
1313
exit 1
1414
};;

scripts/test_env.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function generate_trusted_peers {
7676
function set_up_peers_common {
7777
PEERS="$TEST/peers"
7878
mkdir -p "$PEERS"
79-
cp ./configs/peer/{config.json,genesis.json} "$PEERS"
79+
cp ./configs/peer/{config.json,genesis.json,validator.wasm} "$PEERS"
8080
cp ./target/debug/iroha "$PEERS" || {
8181
# TODO this can fail for other reasons as well.
8282
echo 'Please build the `iroha` binary, by running:'

0 commit comments

Comments
 (0)