From fd14088926ce3a8e4ddbfd7997186f3a2c0ea8b4 Mon Sep 17 00:00:00 2001 From: Shanin Roman Date: Thu, 2 Nov 2023 17:49:02 +0300 Subject: [PATCH] [refactor] #2664: Introduce new wsv Signed-off-by: Shanin Roman --- Cargo.lock | 44 + Cargo.toml | 2 + cli/src/lib.rs | 33 +- client/benches/tps/utils.rs | 10 +- configs/swarm/executor.wasm | Bin 799930 -> 533713 bytes core/Cargo.toml | 1 + core/benches/blocks/apply_blocks.rs | 50 +- core/benches/blocks/apply_blocks_benchmark.rs | 10 +- core/benches/blocks/apply_blocks_oneshot.rs | 6 +- core/benches/blocks/common.rs | 35 +- core/benches/blocks/validate_blocks.rs | 40 +- .../blocks/validate_blocks_benchmark.rs | 10 +- .../benches/blocks/validate_blocks_oneshot.rs | 6 +- core/benches/kura.rs | 13 +- core/benches/validation.rs | 26 +- core/src/block.rs | 90 +- core/src/block_sync.rs | 32 +- core/src/executor.rs | 42 +- core/src/gossiper.rs | 29 +- core/src/lib.rs | 23 +- core/src/queue.rs | 213 +- core/src/smartcontracts/isi/account.rs | 411 ++-- core/src/smartcontracts/isi/asset.rs | 291 ++- core/src/smartcontracts/isi/block.rs | 30 +- core/src/smartcontracts/isi/domain.rs | 224 +- core/src/smartcontracts/isi/mod.rs | 273 ++- core/src/smartcontracts/isi/query.rs | 206 +- core/src/smartcontracts/isi/triggers/mod.rs | 124 +- core/src/smartcontracts/isi/triggers/set.rs | 2 +- core/src/smartcontracts/isi/tx.rs | 29 +- core/src/smartcontracts/isi/world.rs | 221 +- core/src/smartcontracts/mod.rs | 23 +- core/src/smartcontracts/wasm.rs | 481 +++-- core/src/snapshot.rs | 117 +- core/src/state.rs | 1888 +++++++++++++++++ core/src/sumeragi/main_loop.rs | 488 +++-- core/src/sumeragi/mod.rs | 206 +- core/src/tx.rs | 41 +- core/src/wsv.rs | 1432 ------------- data_model/src/events/data/filters.rs | 1 + data_model/src/events/time.rs | 2 +- telemetry/derive/src/lib.rs | 15 +- telemetry/derive/tests/ui_fail/args_no_wsv.rs | 2 +- .../derive/tests/ui_fail/args_no_wsv.stderr | 6 +- telemetry/derive/tests/ui_fail/bare_spec.rs | 2 +- .../derive/tests/ui_fail/doubled_plus.rs | 2 +- telemetry/derive/tests/ui_fail/no_args.stderr | 2 +- .../tests/ui_fail/non_snake_case_name.rs | 2 +- telemetry/derive/tests/ui_fail/not_execute.rs | 4 +- .../derive/tests/ui_fail/not_execute.stderr | 2 +- .../derive/tests/ui_fail/not_return_result.rs | 4 +- .../tests/ui_fail/not_return_result.stderr | 6 +- .../derive/tests/ui_fail/return_nothing.rs | 4 +- .../derive/tests/ui_fail/trailing_plus.rs | 2 +- torii/src/lib.rs | 14 +- torii/src/routing.rs | 54 +- 56 files changed, 4207 insertions(+), 3119 deletions(-) mode change 100755 => 100644 configs/swarm/executor.wasm create mode 100644 core/src/state.rs delete mode 100644 core/src/wsv.rs diff --git a/Cargo.lock b/Cargo.lock index c8f5fd86fa0..180d4eddc09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -54,6 +55,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "amcl" version = "0.2.0" @@ -764,6 +771,23 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "concread" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d12e4b28df8cea511676b6edbc9ea833e4a4c7b2cccfbe9e905ac11e0bc0794c" +dependencies = [ + "ahash", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", + "lru", + "smallvec", + "sptr", + "tokio", + "tracing", +] + [[package]] name = "console" version = "0.15.7" @@ -2346,6 +2370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", + "allocator-api2", ] [[package]] @@ -2817,6 +2842,7 @@ dependencies = [ "rand", "serde", "serde_json", + "storage", "tempfile", "thiserror", "tokio", @@ -3656,6 +3682,15 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.3", +] + [[package]] name = "mach" version = "0.3.2" @@ -5266,6 +5301,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "storage" +version = "0.1.0" +source = "git+https://github.com/Erigara/storage.git?rev=232843aab94770089ecbffe8c8e8cb24c9cedb62#232843aab94770089ecbffe8c8e8cb24c9cedb62" +dependencies = [ + "concread", + "serde", +] + [[package]] name = "streaming-stats" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 9d82e9a11eb..940423c7db1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -134,6 +134,8 @@ parity-scale-codec = { version = "3.6.5", default-features = false } json5 = "0.4.1" toml = "0.8.8" +storage = { git = "https://github.com/Erigara/storage.git", rev = "232843aab94770089ecbffe8c8e8cb24c9cedb62" } + [workspace.lints] rustdoc.private_doc_tests = "deny" diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 9ebcce6f929..943e07173f6 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -16,13 +16,13 @@ use iroha_core::{ handler::ThreadHandler, kiso::KisoHandle, kura::Kura, - prelude::{World, WorldStateView}, query::store::LiveQueryStore, queue::Queue, smartcontracts::isi::Registrable as _, snapshot::{ try_read_snapshot, SnapshotMaker, SnapshotMakerHandle, TryReadError as TryReadSnapshotError, }, + state::{State, World}, sumeragi::{SumeragiHandle, SumeragiStartArgs}, IrohaNetwork, }; @@ -61,6 +61,8 @@ pub struct Iroha { pub torii: Option, /// Snapshot service. Might be not started depending on the config. pub snapshot_maker: Option, + /// State of blockchain + pub state: Arc, /// Thread handlers thread_handlers: Vec, @@ -218,36 +220,36 @@ impl Iroha { let block_count = kura.init()?; - let wsv = match try_read_snapshot( + let state = match try_read_snapshot( &config.snapshot.store_dir, &kura, live_query_store_handle.clone(), block_count, ) { - Ok(wsv) => { + Ok(state) => { iroha_logger::info!( - at_height = wsv.height(), - "Successfully loaded WSV from a snapshot" + at_height = state.view().height(), + "Successfully loaded state from a snapshot" ); - Some(wsv) + Some(state) } Err(TryReadSnapshotError::NotFound) => { - iroha_logger::info!("Didn't find a snapshot of WSV, creating an empty one"); + iroha_logger::info!("Didn't find a snapshot of state, creating an empty one"); None } Err(error) => { - iroha_logger::warn!(%error, "Failed to load WSV from a snapshot, creating an empty one"); + iroha_logger::warn!(%error, "Failed to load state from a snapshot, creating an empty one"); None } }.unwrap_or_else(|| { - WorldStateView::from_config( + State::from_config( config.chain_wide, world, Arc::clone(&kura), live_query_store_handle.clone(), ) - }); + let state = Arc::new(state); let queue = Arc::new(Queue::from_config(config.queue)); match Self::start_telemetry(&logger, &config).await? { @@ -261,7 +263,7 @@ impl Iroha { sumeragi_config: config.sumeragi.clone(), common_config: config.common.clone(), events_sender: events_sender.clone(), - wsv, + state: Arc::clone(&state), queue: Arc::clone(&queue), kura: Arc::clone(&kura), network: network.clone(), @@ -279,6 +281,7 @@ impl Iroha { Arc::clone(&kura), config.common.peer_id(), network.clone(), + Arc::clone(&state), ) .start(); @@ -287,7 +290,7 @@ impl Iroha { config.transaction_gossiper, network.clone(), Arc::clone(&queue), - sumeragi.clone(), + Arc::clone(&state), ) .start(); @@ -307,8 +310,8 @@ impl Iroha { } .start(); - let snapshot_maker = - SnapshotMaker::from_config(&config.snapshot, &sumeragi).map(SnapshotMaker::start); + let snapshot_maker = SnapshotMaker::from_config(&config.snapshot, Arc::clone(&state)) + .map(SnapshotMaker::start); let kiso = KisoHandle::new(config.clone()); @@ -322,6 +325,7 @@ impl Iroha { sumeragi.clone(), live_query_store_handle, Arc::clone(&kura), + Arc::clone(&state), ); Self::spawn_config_updates_broadcasting(kiso.clone(), logger.clone()); @@ -338,6 +342,7 @@ impl Iroha { kura, torii, snapshot_maker, + state, thread_handlers: vec![kura_thread_handler], #[cfg(debug_assertions)] freeze_status, diff --git a/client/benches/tps/utils.rs b/client/benches/tps/utils.rs index 755406d5cf4..d215d1ce203 100644 --- a/client/benches/tps/utils.rs +++ b/client/benches/tps/utils.rs @@ -114,19 +114,19 @@ impl Config { } let blocks_out_of_measure = 2 + MeasurerUnit::PREPARATION_BLOCKS_NUMBER * self.peers; - let blocks_wsv = network + let state_view = network .genesis .iroha .as_ref() .expect("Must be some") - .sumeragi - .wsv_clone(); - let mut blocks = blocks_wsv.all_blocks().skip(blocks_out_of_measure as usize); + .state + .view(); + let mut blocks = state_view.all_blocks().skip(blocks_out_of_measure as usize); let (txs_accepted, txs_rejected) = (0..self.blocks) .map(|_| { let block = blocks .next() - .expect("The block is not yet in WSV. Need more sleep?"); + .expect("The block is not yet in state. Need more sleep?"); ( block.transactions().filter(|tx| tx.error.is_none()).count(), block.transactions().filter(|tx| tx.error.is_some()).count(), diff --git a/configs/swarm/executor.wasm b/configs/swarm/executor.wasm old mode 100755 new mode 100644 index 9eabd2f39068c887b6ee4dcf2cd248213c6b655e..cf16f5c1144c696813b956659d9cb41c6c0bac86 GIT binary patch literal 533713 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

trHKCX8SN>& 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ℜgfJtmg<7{C6|le@mrv zyrOiPZgdz{G9b@XIu|Y?UP`CjSjCyzj!<x^eNvunU>d7NXo|!|GLcwv$^!qPGQ2>TQ>4l; zw#wcLX{a$q8u>g%M0yPbF*`@5m2lp6bjS6a+hL|W({rMf)_sttkb3#}u2dRPA-S>A z)@|_`)F9XnuIcd8p!9TY1kZKrSr{%Q@lW~m&-_Y>KZHX$iNB{b$Cv{2ozNBu`CI`f z$IuL%5(~{j2smq+vmH@Iu3T|#>4!BKe#EJV#8R;r1yk)H7lsBi?s3FlH2cdKsDTyW ze)!FMYM7Nzr5$eGfQ<}_DfwlR`VbGg`)_fvHN?qDHY)N#wMOr+-Vo&FG1_!I2H!U03s*|$qIHN>$ z#ZY1^l*owd<(W6PEL{9AOIH)95IMFM$0|j!=|A5g2yBJr0-K0QpH24iem%}~w_S}2 zyGpk~p*UAo;To5aH{EAPns|0c>|<&-1es&i`B;6Jvx)4J*Fubc(l0=gz>HOree4+t zXme>aR2SMc*Qtw>EG>nf>hc3_~rCx;c?njFAr(MC5h5cM$B4Dl4iO^BrP+z(ZTK<_=2yD%GSU4(X#qobAt7oe~ z*)hS7gqDMQ90g9ioPm1;s#&z?)?0xZ%;_c?LWaojDGeQ*XYGV zi>(fFm6*1@t|$M>&fQAg(F2|kb2VWpOWq}QSBZJi9al|F>{%o34m0=QvI`r|${uiv zye6kg2CtjUgaM`R92O(0iXG0Fhj}&&jB7CE&?O^{0>bc`Sf<67O26WmdI5EkfUa)y z032ehA~qf+2`#`V{xY54l6H$*Jt4} zRTNif(SjAk6p0LQ;%P`l#zsnV{+?KbsTs{=uX2wRe=^;Dpk;WboqHrSTH1oF*8Bsu zTF}cFaxN#zS|oiL&8d#8swe`m*Oo*wENJ?N%% z$6#Gci7d0K+-uw8Mh)M}C=gT5@KMWVEr�rw{boy=JkjmL7EP%Tj9X}F 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

Gms zO}fl3DlSqdaH)A} zkuG0XY;nKCdaOV7dFFA^d3+;p{5g@l8`cxa`ptJeX-Q&e!)1#pnz4HEY7L94AmV%n z&SjP%?Ak#n0Bw&hzr4H+@?t~Hj4?EWxZN10Wn-K+hSOTdFzYDV#(%Bm-ZBl>o!XuW z{M-g3+#()@cXaK$-D9e22E>iq@>6WJ18*?O?0At*$nM{9^6rpp;b?xJ-eqBS?(+(qlkET}sZlm?Q&Y+n&)5N7V8b)SlI+GcB>qS9^B&Rw*AI9{~=fjElSg%{+xe1o$Zkxvb)D1ziNZzQvT z+;4Ey3(m1HDiuUW{v4TY@MEQp4g~Cx^JS%_UnQ(zm6hYqs^+~|QOyL}8Qc+>=TK8+pg^Y|j z0(6jYl?yswjJ4AKFR#Dt1@BcuoR-uZ2XTe(@vw#C2{FO(1LXJBmp{$9Vbs>Bqd6;7 z2u8_S=Y&R>Jz(q*X20SSI)c`_i4)n~l4>Jg5FK2I|Etv6vC;7~X{>wvc(O%NO6pxO zDtR;^G$#vCV+dfk&s|<$ z3R#=Gyk6?8r1@Twm)Gxq^YO`-(&pofFe%NF*z!NU`S{#zrI~2%M%I`1jjZ`@+1%#i z+~%X)Lt4JPfsd|Zqciy+#8xrQ#8nf^Ja?zO#|3B03tV8{cVYGuZXRLFE3Ukke|>2; zk=wD%`?)*ieK@~jwaNSOKl|L-{*8*Mmyl6$)1ixK`dEG@=S;10ZF!wNV}~&8re01b z6t3iGW%S15;`6lZ9Z-;97wh2eY|kBYM#cX!5l#8&>$XP4T(<0kjmgf@ITz7nj^4S5 zra7ZRjPRSxmc4(Y;@TJNJb%Hib0*`m@BF;ncm5B#?_Ab=YkXI~InU+3J}UUfw(j$* z*d*T3HlFC<+~>1+D*n?(#g@C~jEXsp0D= zeje`bQpwS1PnRq7L|39r3IR99mC9x5cv?+1>V@{SMnQshz|gwM`}8pPqVXne#96;y zh1p)N)d;Cd;Bupq?G2Ons_ZhJ?TN;-%A4HF%A2wyTi>i=gEYIZSzLtMqalMbS2=mV zS`r|;fji^J#Ca#XG+|*jN#pFXhS~8-hTiBpE*3C)3J(}O$#^5st8RNGqKr0rJk^Lo z<`o;*VRnc6+Q--BKp>*M`?Gl4juioe)HlrdXxBGID=B2|J1$<$NAGd*@l?`i`^U$W zI*wmpgHtsgBtg$Lkk}dIAq^HGd$ylT~khNCmyH63+!WFAz_V*0QcO4Do4- zC<`8UU+@%c`OiIuo33#up7Ux!P-{rScan$igEsmgxypSi)1LcY`<)mL}LmkiBcFuZWl;w8)6 ze@jP}uUO{0zFuADaJ-Noc8_@8z;FJ?kILhj9h{b8Ek9c7w7i-9hs(d1Bf);n{-bu_ z!_v?BB)Oa6;$5@iw&TduqIlWs+^uFTw;beCJ&XHD!(;LViDLb6@_hCS*H;S%Pj2q&C@c|Gjx zm^#}wTd@!F|ECA#_N5BUZ3)UHMwcAX8q^j{8MCTv2z_--{Cbb`B}^Islxc-T2R7YU z^xT2XQp*l(^u&~{}0%=OJ03yRF$hGkw6xD(e%zUwD4DP1%)4_W@`*lvDgS3I#X z@=L4Q@3Ae%+ADn1tLu?gPohq!KYS$D$L(7e@xYIp~hvg+bV z1wP=Z`yU_n*(^V^Fs)|QQ%44?S~tZU3l3N-YpGTluSalWUcU8{+JDc2HIva?qjt?! zU46FfA{V>$t{Q1@R8YZRuy$NGC(&I*cM%ohh#KlFX?CVlt5&I!UEs<`h;$|fw<^?7 z+gTT_C`mj}j<_qCMvAAy?y1^6DhNi#X~2fT32Ik~rvqWuoyGY;M|dA9eSGtMFzvl4 zLpzoJUcYYKto19u$%9`)m!-0|CGCg(`>wX=OXI;_#^P3~!uU#dg|D_Mku}M0Yjn5Nr|g+GoE*Okhb`89MLlnKI(sqy`&I7=6|DlTKW?a%KhsEdFi)?F0WX&E{#SQ zv6^QSJLBN`#CfL-IC#V^RpF;nj8~<48qr`i4m8wSmKYfd@blrSI~YzQ^^Xc<3(_Vp zR-_rlO(>?EYphS-f@fKV%Q<+_+@3RVM=Qw+13_kBO3e^>UZPVaQ^WUiP05k~8f>ClOdleQpxv&a=PNF+|8t(4JsV3{W3%mWofgMytDjMLtl#Inz5Pw+kgoOdjeEgO>8GOz+0sWO6rjg=XBpUUHoe{X|CY zMwtdZHFo@lYqE)TZ#KmS7rk!4>Q%;#G~jC;GUK^gUS-GM;|=bI zfb&Lb#|W>Kfc5o<8A7yfz}+kHQUn+vh4|w=-TKisJi*!NcT-`SZZ$H|m0Gr9nW| zC}c)WBwh>*)8~hkby-Cy@j54qUn5d!Zel)6_BavBkgY6y%?Q)sIuK!6%vI1t7p<22 zn9dNJ;DmslT&c+El0bKtxEGwY5*||nrl)o+Xlo)O%EdFq#WasHQ8te$$ZrEg1>m)3 zdqJSXm!U?^QKXg`IR?tl0AJ^z`w+VH&v(ap{{0%^uuVZi$ zm*(q2Sc@K99XD%7`K%o?-||nhHc~zfpsMCs0Qtz9utWuu&OUfb;V`gV%gN zYiHWtNUl~-w5}Zo_oF<1!%@o%-`C6+g}!DwpvH&9@YYnhhRH)4bjP`}ca=X_RIcPN zjtY*TRl`e}6duJE{R0YHXf(4% zGoviU+&rq(EA}o%g`>cei?!0p*c{FLloHpgdLb>D+$H7Lh&Tj`0!;DDGd`Otq@LNe zvMGakofkG${DoJ#>ax;cIx^FZ#@4ar#q{GKk`l6c2F2zz{Ru%xi_xo@8EW?rc7qGNuM0cOoJ${ks=tZuUf@bkjVaus>M!kDF*jAYJBnu4MSA}U{JCNJ8+&67} zFnfQ}M94RUv>fXBCk+5+b~`*0k$g(iunZSye{}E7O5Os3s#(ulgdoFOR--1$B1hNVWjF^;8h~Ce zCY=5BM!aOsM13N#n5e4&;F}Hr#HG=d_1c@2(He(^vmF-R=&*36qxs)Dn*U!K(ow(C*FCK4LM9b`+?zr9i(KsG}_%j}FAS_^R8L>a5W>SnZe8+6_C7 z1;6Exg+z@7|GSX*%IM6o^zA}Swi};mWViW^MrWH6?sHwWs|v&Kr)pI1v$QT=q#lrR zH15KQ;Ms@_24%%pDgxs=PODJF)RhCE9fss$YQv*LkjJ#@sunD*!W%Td-W9DDj{XQ* zYPB2%z2-2O-bpS7VXK@T2%?}pL<@jOD_f`WOB69V7wrVo85xo;dzY!zY5F64z(%%c zrYg6U@nX#&^3;rl3K|K4i(=Q6+|Oy>vC%1f5D)U&w8z%9rdCgZ?$zt-a@Z)3 zoRk|4rF|!0%?4@hxM(>aOeDj6bRVmgDov=9wD-hNK{}Naf+`=?69P@YWc1L?gIfKI z6N6VUir>>HP+Cr1iPzcuaLcCJa1b$M=j8NLf30@vEGsN6nY4i)X1m@uIl=9eG)0up zlzt@J#9ejzEtXtqQ2V=lS1v+`(sJQh>PfiU_c)- z6{@0!!nHOl&L{b>{?Z+cmwRY}+W9Tr)qHoecZP?Dn={o3SW(a5T|L({yJ>k$tWdIB zJ*C}gPuiRIxnIe{zISNW?ES4FW)>srA5Sc(gRjfSlbcQI0_F7jy#H!eT_dD-K&v!j z>89Ao5L(uZeyivqO;FV&9jFp3ZYkp@7gL zU#KQ;gX=HU2UI*O8~(fGBIU!>1NaF-BsXiCuJ!TA1%1?BVJ+hIU^(iA!oN|4VFzo? z-fYb|=uG-56rr3|@{pmvfm#zs?je(DB0mvG093<@bJ{bp1ox_2vqI= zch0eXXWY4%B%z2#9H?p$jJkcG{6|X=PRoy(AxgEp(r0r3)!e<}-u%tXN-b|{+vBji z_K8lKf7NM``3e| zT$>7QDEBw*9NoaolpG}wOXo^~^{{T>gK3PX7IR$>Q#6dGZ{*&2BIJgkWI9Zp8}^(?-OG7ps~dX~aNw9B($KS1X?Vngo-MmeSoLLZqGWc5P+iSMm98ElsRxt!!;6+I zTmG`N@jI{hz0sAwf8;`;XNF7RZs?x}6mvtID0_j_ABI+i&PUMolTVdgt^%zTB#J zU7IvF>Ho)SRqoXe@VvU*T<*rsfpk<`rRH#>9t@PHl$))2?|9y1kD4Y#4Ta9kWv z4^41yLKAoMr#0pjQL4rymJ8hL{+H{)2DN@*)wH3dgW+i%I6|*bdmDXw9qQtZE$uFx z)~>_oNDlZS4>;#)+(;Jt2TSt@OZb;|-y~ecm9Dn0FbwmFhKXhD{?!giM0nShqa%Vm6Z0Ye8ZF|IGMicw?I+UpU zTH5{HY3(|y7S_-2x!1C?$sn_vs@}|Y<0|ZI6lLK){lfs0O@_2q4Y?f#pA@kn80xk^ zYgnr-q7=O0POxI4CBIw`acWq6v@&}WtOB01f~(AjM?Du|BTHMovzNIi|#mG`em(I42p+~$Fy;6d32NIH@0AQ4nmZF@;<$@wOaC+zSh1J z#@fj)0+_v9i%o09F7z$7EdrLg*VJv>JZ_19tN8dgEfd*vkj1Ys=Og)GS-}lD z8N-_3>k)iqJZBUbrkWeO@- z!z^b+S`Hfyi%Q8;DqkDqxv}D2`*~b*?MBd=A1Y05Et<=^RVen4yZuGv^-AB9pbN)|eoVo}7Px%`z=AwaO-NL5o(cQw;;GPhH%nK@)&Y z^SNr*1;&XG14pIWDivbE<@(wgA(yP9B+L+%6fcd%2*v_t6Zx%iyq=%xCSb8%X{ z3EwH5tnr;lzsMgD)BbFL1hw8E*{ShqKOyJ7KC@)RRmUtDL>N$OEALFl7y}x5abO9bSyLhJ4E560H_s!{)}3ZC7kK zEg4Vl5!|i7Rg0GF81@lfVc>m9fz26{wJ%2leprN$X4AdiY7Z zD+2~ShN6)vRB=1a02|bPcv2;%#duwuk9Kcd*6Ld6?KcKXupHd~+KxX@I6Er%2a6M! zSF%tn*MNj_kubvoQA21#ph`V6L#p)HMJVNwjTYUo7abffB{#QmD*aFPhR?aksV2I~1e7gz5024yq zpuGkTuPohj18YNhgc=C_TPcJr%HD1Im6;*RE_GjN^yu*Cj9#BlM|Bjoolz+uxVH&` z!-9OhvtR);+hJj|H9idsIdYn)DUhLsP7_f=&8AqjNv&bBmlH~uyn|;BVfx%i_!l8= z4sqtAcf@;8t6<4*PCN$K3D}Y=q9RMg>i}$5;h@3p@?bMf<@CO_05Oaj z>RjP6U8hm7+f8$MoA)={WLkue#R-m74Mm-aNlND=^@db5(sU(e%BGB!8VaY|Qif$^ z{0vf*6Y5IT&J=*RLL|(*8xGg!r3y|pQtm`o2vZk1xV-I{$10fI|k6V;6uEo*1H_ohU-mD0m@fz;b!fen$D2@+H~*4>L7Pb`|$l)>>#? zw3*u+>FCu(*gkLo%mGsDuSZ zz*npgFLYDBlZh>_<5k9Aic!+qEhn%@J36o;D&|!^u)sq*I%1RJ^q9)JN~_#bA9@%5 z4l9u4n^JNO4kb6}!Lfk1t{rt`ZN_dlzn==GMUW&#b!N}lA=fX z;%?LxOmCmS&{x6xT9$_49#Ik~1&A zkODd7(S+HN(q|hm?%gf*Ucqe)y&;uxIKKa7;w`qZN^!u)4=fYUy8p$wE*f3|WO(j# z)@7glWmGM(kQ)@I6^I+OpgPyMD^cc*6{^?djFPQ)0H#pIVp=WedPfT1Fm~k?Cmm?u?cR zx}#CZpGQR6nA~FA!9JtPz%p0EDGK0eRB}C$Lo~fFjkXC>u4QIaGrcjRKMS@{9S4;V zBzP^x(SbMtz8tm8MsiY604eI&^9?HIJI^1g-hH8&)>2^J#P(Qr2HKUH@#9<_X&bpD z)~g4WHbvT<%87!|sOUtrtbRqsZ3aXxAW$gVYERrm?f~6vPmthN%*yDOn)T|zgg?jZ zUQt^Jbui0HDGC;hwAV#P8iOU4MWo*#Eu_DsG;$)j38R@F#GaR?f*g_doN3yX=Nz)f zx>%xEt$=JdKS%~$@XbstiM=|(5{%+l@IZ+Aq~^~SMCAr)>TmdSQH{CgHiFwCVMQW2>o%oyFXoXmiXNs4~KxKs$;xiLQB55+WHeY6!3ox*b#IW|L6aurXI+)?*}6;E_LO529%x)Ip67%`OSnH#!2 z!@$V#Rg{yIGl>|7xrZEA3j5iJjx0o@qybJFAk@wMFIE0mrpha*^4Ll{UMkz(i*s(v z7xZ49y$3BaO=dsd4-6T~7FfiHF;tbNe)(i{@%RCH4 z6~izK0_Bs)W!qN0zhq(wN~=t`p>^3gj{^mHMF0zajJPn}vaz0Pq>{u8EaIt~u}VtC zji+TavyU3WioHB`Y}C_7Tnth$X!veYSgfDzMyzFse?=NqS^Y0`)WhvZy^tnLQ zv>kQt^ijh|S?>!Yw;i=~j98x9bj`=MgPGdOCd0IeZFZJmVL8>D0Lv^<$-EFCvrQoY z8(v8h?ctO@lPYvSD)=dX{PHT^V`eKe8yB*JqKb9p;jHKDN)!7{VXjH{pVY@!@@IJ zURhyZWR}lImZQ+wXzJfk$OgE~0AsdzvoYDqc&lT&c$>UeGE>>@XXy6eXFZOtnVf{cx#iStexnCCFeY*~04_^NgLG%r6bYbC`ufnG`Hh zLfa}NBYEU-=Fm{)_`z8?ewNX=tt`WTvv=% zR5)4ldE7tu8yxKkQCcCEh1C&47s_~@#24(h64t=0BZkBEaZ2|MrAMQ!Y{6=xHX`ZR zvayb!Z6X16RYV_&J#rH8ZEq-k#z_-$DxM|p#CZo^p35VR^M<9l>^^P!+6sNDaKa3N zP16sGE-e{rZye|?%fbIs$bl7h4A2JkiagQGdWhw~faqKf2n`YQznF4h=g4Qu0c_!@ zE|ayS`S&Xa?o7H-UR?E}$pKTWCk9^*jT~46i6_K=QVs;|Z%z*8AqV79%Z+Grl}Z34 zaBd@z#B>-0elcD)T}5iILRf-ejI#tO)?aJ~^>EmmC&+{xs0vQA^Dl0V_#xZG=kEa?133HFa=$r14P#3S=y{2D_lWNGn^Gy@3m-i zJMJ7=dPfE4Htb?6j$E1L=kQjfSTH8@Q-bdZb&rqrgkbs>(Qs*tsAPd!Q!Pw%-~lWw z4S)rU-&mA|Xl8gmtAoR6YcAa~zGie_|kn5hN#8rYydepqH<7gPTL%>SOqTpO54N zR@dYRpDQY=dmPcfJo0%Hcrc^~Op{tB1mb@azsH$3%_ETYnmbgP(rp~lEfy#2Y>51TeQR6P zwm9G}z9{Ecmuo|Hle@~{v6q#9bQlc$)pDJO;XmR?r*$s~%bfyi(!AD!AI*OApeGC1 zhSQ?NO2cqMZc#+i}MV$AQ zXQMoGab!8h?4et7@^y*cOE`rY8;U(FzN)@9-TWxsz#PKJGAC|S+8d&$0E>v&!5C%| zT6CaRI3q}~1fCt+w@exC{IO;gvRQ(EX{D+sz^IM%O` zJr6bKIXG)F6+d#|ati&=?hTNDt|_7|=LE_>+Nhp3`iPSe@U%OYoQ3d~%WP76PnQJ? z7l)ZAi8l;f^kuX~vC@SV zUFF#VhKttkWBo;0wW-J`A257h?qjK0JcKbVJ}la#q5_6@6%k88c9(%c6}Uflrz+88hIyNno+}!50*RA($1=|Xh9!U)B!xi1OZN+I0atTU?>uaj zBc9WH#DtFc9CInt%n>p|xCPDf6d|te_(8ic;s*uuWB5C``zy${eCSri7IXI(-MMXI z;(c{eB{d|25W9LVA11Kn(Zod4hzvrR`JNpPimf7pPaZqnD^?CK-oSWABjT&`@M58; z>jJW9YJ1EIi9~}$g=Lj&Rq|bR)u_IjNGx;u zZr2G5&K}tA87+vqxra%4RBI^cLlOk?5cKHV8u^yV?bJiogPyFC^3T5YpG|yGPj>A+pSXt*1M(mA zSra6E068Wqc8+9%anMZv*~dm3I73m_>Nl+&8?EQL-457C+pOTLr4$Hz?XCy>S70W! z;4b2MEJ$)CzzQQh4kHYPFW!$76M2Nc!gA^;Q5YSmiDk0Ax#6Okc4rGu9ncK{-C6h+ zAZqj{Q-<#W@D2JWLjQZv@Sm+2ug?Sbw3@IS9b#DX`L`f;m|CDE$zVE^eCcgskAbw` z9cWQ^)pUW{Cogkz`>s1(a8_fXWr;FG4eR&~e}d#*0U@J+f0E-aO4j?*gfUyBSPcER zx8o$DDS8x;)k`G{(wtUPC%$#gg)20A?4Zi54T*}gHgq!mOlyc}?eRK`wT0<|@%kbe zN98AtMJb5wN*ATwui;}M*o8IbrHjCUz!|RhFoIr%TG;pzB1waIxz8xssCg2S{LOfI zdz`8Z3M}?-dUWW}_E{IFef}8&uo7MuKlCcSQZH4YnZuGt!J>4*sgT}4s|xRMIX`rO zXdy@7l+TGGXStaF1zDeVF}pdm(|zg)!=i3iI&39)&cO8VH6zItc3RCEo1YGeW`p%5 z=}>(sYzKc0WtBCam6p7k7KU^(Xlc4c?2^T65`PY{V4fPU=JP@j8;DI`6B0h zaOd`gDU^06Up97wwNI}PIVEx$i5~%BNqsO~S|3Um)fb3D0QQ&k&4;9!Y_?tWqVC5V zgEEen@iVrcFk!_wu`vR3iGkbaY}Q}zjhe)wu^8B5 zm7=VkoXqdObn!@|=J}#mDnXg)Ja_bp7kW(&ssd>NAoY-;&XA8z6EsLlFA(wLb$}A5 z3-kf<1(kr2q_u&hvC#KvYG`4zPe~O)hrJF$;Z96xcmQn%OU(O)b;5`(UtZdKR?ZE> z=`xXiI2{3-qW)kys3D1f>)t#rzS*Xxllhuu-5V)j`_Komj)SIq$kirW+ zt0tF-V-bx=$7G(?+ysYlZK#`4&8CY_QtUCrM=Npo zkcNf-MlY$>2<=66sHYXjx!IKf%Z+xl4DRt0Ey|(NaPR4=1}Hryo#0**+F~c)w*(7f=)GL-l?Jb%k+dnNJ}M`IlQDVo*Ma+cBvl2|VL#bK* zJ*r2uwe)2mq6b!FN;04p9v!|b#IaQ~EySS1@e_H1c4hovT8dADl`wERnlb9QrK*D7 zg~H21>C!%F2c$@?SzAcGfVSiznp%^~bCSN6a**N_ba*Q#XciVHOjKzOuIkA+{$vvz z6OJ%vvB&GHC!<3h*gfisTBFo~pBAFPQ7<(=00cn#q8I?if*Sw@G4RQG_HHb6Q*NmE z6GO~O+7P#!EkmikCL>--YE^Z#u~J{?<{X>d?EWXp%Ve+-np`atHi+&Tg?X?V_}mrO ziH0#g)=5##%8S$LSy}(zCI93QqZ4fsa|+!NC-3(UOGL+yU1=oDd=s^l)yEpY(XOaLZM28 zoPI?Bq(?VPQkP(P>ywL{H^ndzr)yyw^q=19a}$Si(R_{@u(%xyKw~WguCVLJ*bPDl z*gA6ED5b>(PmvP`@s5_0MCw0A z?(=H@;e7c{VlrD$=IaYrhDO8^F>MNb46j&=9C>-eR%f;r5#d#$VTY3 zV>OK$PPqVMB@%=_5K!) z71&muOK~A(I6891Df1BlDe4xfE+Cn5kStD%^H$kLS8B-AzJqtdJ+ff1=`2XU$Hkk0 zLNM(GjwuHda6&Bqe1quDcg2^0h;)7qfGQCbL1YmPAia0!lj#o6iZSV=D&dbv7v~64 zjVZd>sS+&ea4%?@j;B--Z&5l_ZhIqbYm39!zL3c|?`1Kd(HxvV zQU8+gkOvSSo;5y8KJ5VGxIS4dq%SaZ%c3Wg+gRXTjvU=9BqjIhud^Lwwu)J3k64+C zsbd)gG_P^r?}W&{Kj6OGMAuU&C}hc2-)LKB$Dm>T@>aUrdy5>29+B`lA(M{0- zNUI06Ka)cW(<@JEGOF`F?07(m{4{@tcBS8b+0Q9j4O zwU0X&O3mWV)qxU@60>Shw8|Zf-8|wxfC3WWmgCxRL5O3Gzq{C^D`kGT3(w$0$a9pi z)hzjPWgp|*Ody8p<8ySkX((-VyCah_Q#<=#nW#fN!8kq!M^^;eQd_aYT>w?n(e)L{ z(25l+=C4>Vyl};eMT?iLSg~~33JyO_AE6)J3SPKz)vNMO-xcBlBQdj1A}7oE#_(mm_CC+00yr-9pt?I@aelwq4DqO$`6^mORWO1)wp2TJnja zM;9vQD7UOAr}N^ZFe|m^rh8XzDKBQ=fb-glnIEGAbumcuGkUi+WGLV@K z;fYiDLaCv0-;3K-Avjnu+@&gpNI(2V#jyG)hkvP5uH5v(c1_o)GCXS)EZRD9ECQl* zG`fN*6ti0DsEp?Y1wibgE1Yzea`Pn%VzEZI%R16z6fbmcgiS`V4Jp_23<3?(7h#yC zzRCm|;zfr9nny##T$UOtV^R8OM&AilY%{IkyK>)L7?(|&fH(+b`~{Z5E$0;Xr3Fzx5+u8Dioxz>Yeg_%M+A0!YSSHfGFDFD=K6M{%|8Oki> z`U|EYDx)Lq;g~`_TL;x_Asv`gr=Z3|Zqw|j;Bj{UD98nifdaMFK7iaAy6}rn@bi_y zqf%MKTl`TFa9zs!M$ee$rsQ<%A^57(OTo7k4ic;k9v~)CjT{wxg7}a3TZ2c_@QLBZ zFk1>F3Kp5h*j{k^crk;ObQvpttx}a1;Co*v@KjuRaKJAI5|WmbASpAH)Z7fAcUoRd1%IH4B;mT;IbbU%2xZg_MmfDc-<{lrkRG?+iTtE>#o^z^;u41Tmt;yqF=U&$h$q;5<^ zT_r3NO{M7FIxWlgGT6iuEfcMNZ+FCqCqw*LLly>=od~Lrg1L1bBftRDnoHd4;xEhF zw~E_NfrHi2DuH%1VsK*cr+g*)=~a9X_VgEgbnAK=1NU@3dXI}<&j%q@$49F*p50)=L?gzz;9OlMeRqwZR!&g`kYR4Py2;P0A2H8N5% zs78{Z5&buRWWmVD@WMqSBa4@qZGsW%bfj4z`7Z#u3wzcnDYH3v<9?YrQT#HSAg|@7 zdww~+Zj*ZP&gSc)p3TF>JDY8bUp5~WC2dkGfB~?goXuVQa=j5^UV3LLJ%7gwrR;a` zn-b64Xc?zzU*5QKrUCO`l-p6XV2Unp!6}>Km+29HS@?$&$ov;=kc8Q{&PJg@>-sW* zq@4R@Vpp^%i4w~?GS$QvdU9kDT7c{S2p;%EWw3FcsB8^iA7U|hou-D{KX|Jay3!^5 zBFsUF5cL)Hacp!BU+9CsX)~}uyON_?g3i!fx$KxmD{C%yHkTTf&PAFlEQcIS(EDh# zz8P{rI6>-~cf)f4T&0PxPFX>-MN1#Pvia|pLuoj#hCA;wVL7*0Gg~ApvsC`1PqAhS zBo|sxri=o=!pi6fMfIC&O3tg~k@adk(&C3w9K0bAB>B8*>T3@$s8(zl$x>^~0&&!Z zBNnR+7Z7Hd$Z%RpO}ThwTP&jC%sZ7Qimd$~v!s1wNsA9m1J7Y*H=U+{8SSX7OX^46 zvw~Vz$)g{32Ic4na^mY~^H0uf@ zuBKu;S4OLSAh*!11LAqybdTC)GbIjWA=8sEINEUca+M;ahl{9b98qXijaEa2*79K3 zRFbE28_l&;3sMm$KmP4vhGP7aE#o(C_;fQ``KaK}WFMOIQiOyLRt9)a3|47wdx+-d zA0zQ(nO+wP@5Gi>4jxO)dD5Z1V*y7K?Z#2<9JPoDg{Tb3p z(9V>Vb9_s!jLI1?Y8P;50yTiwhh=5BmMUHup2t^36v9u;+u5I3M0578D@_&JOfR<$ zTVNr*24r*9Ep)_P1?rX%FCSx7y$0k-9K*6QJ40~1-uc5MSrNMxr%_1UkX?jZ+?nr0 zMb&8@>wuWlP4Aza2=vm20r;b5<|bDE$s?|k2Z0BV`UgNG!WeLl;*NqC7UehQbZQux zYy^0rbn%ObiS=~GaWN>-w>OB38LKRt^?(Q8a~Zp$Wn=bc-b6xGZ3w|oX@$$$^J_Uw zGS9RYV(zp61D3+xfJ%1f<2^$$s-UKUD33;GCUEeC)IcSVG#ic9(sY=7FRyVPczrDY zVc=}A6IZT+Wu22phjw&`OM39#?%FLgxj_s1(O=RjnJ1y4>QBht&DF z9y>19gW{GCNaQO%t`ewR27*m%7Xxc-#0>@@Qp2A{mKn%4M)E0i5j;ye=IO49O1ezz$GFwPQiyZKC>lELLnBP9 z@Q7*LG#taB{!GZHFg?i+j^BmZb8N6h(P~`d;n;T{yzKj5`q@nrr;R#yq)Foq8y741u5$ShD=w&Q4HtB)Z8W5LF z4z79?EkGZe6Ye(Xu|2n6ecg>e|KzPtu$8u#KE}ScVfSYr`lq|E|6?DXNi=GK7B|l4 zJpwj3P+f8>mpC!ZuA}K#jL3Lb;x#`m*kX`Fh8-S14LD zXFm6bWA{C>?>}z*>i4#&71a_s?8<{f~a|#an|;skgJ0dickTdbOF0{bD{_wN0|d*_dBd;86wxaG<3Z&}tU_2j-P$=)5FdhB-`4%6PPhg|mBpte)m zO?D)EKYr$mw}0x6sSoW6#xA<)(gz>@%unw6gHDMz3Gw=xHp`?Z-VRT^>uzhYS&j~F z(t{IAHcQ_Oo8@g6?s?b8w@qC8kKSat{i(+{Uv=ddzVs9}j2$U?@obi-4!P*H!|i^k z_tvt_@?=MncjM-XFaOJjKlhF;>&C8m@1ChAF4=KKcc;|b-by`u+NL;fIXtMh`;b$w z9d0|N-lH8+@3Lzj`0>^Mde>vjd(XaO>RZ2j*U$El)VjIA+3$j3TPyj90Wk92ba;?& z*C8igTjX|1zi)L!zi;jS#q)dr{qhTc9*n(fj{p8}VPNi>iEA=*e>TNkZ zsQ2t4m%cWr?UZ)E>`3+=`oSH0FaPYz}>st?zlJW7*r(NWG^A?*7oD_uqE!qjv>kSAO!Ln|}PB z8=m@h$I^)Ry-dyxo}BMKJjpq6$Y;j3$nKP$`#PfM4Zr-}4WIbQr?&k4*ey?e|E|f4 zuiy2CPG?3o_RG|q@YGy?cv5rYA*W^=RCh|tOFFW0e)##zuit#@kMI4|y0I_ZddaoB zAD;Y|f9#Z)+Hfjq+Vk8zxz;&n4l_Ob$wN-cc9`yzmN#@n%NsAg{EFLlUiR;=9sBa< zuD)X5!x#PPzjaDVf^f>DJjao8&0$H(>kc_7+aS7AN?zL$CBOanPdDCu)w5sto3Ri5 z_!k#n`pZYZa(|l%6DO!yCdF>1+bh&IaMS*IBS8vm6`tLwj6M9=*RH+&se5nQZF_~e zbz|GKZfg+t8HUc-y+7Y}$pd%2e?7ad&b+z$?N2=XsXbR;bLBd3-*7Bt|9gX+XjEW_ z+iSGF!D{=x!C${X`$jQ6Zl~}-dxPz8xgTm>RJL!P>u5f_<+(3B@r}>_?AH3&)YDI1 zzU`s+etK)i*6LKN;GO9NkB|uM>mFu0^c{y>@Y-Q|KeTMIRo~u`?>+kKC%*B| z|7Gm%Z+`NIUv9naU(fDThbn5LG##GdNcsB1l9V4moZ~fu*YdfWtPD3bg7-f{L`^#F5Ub2Js-N^+1E`&?ngY8I!11r zCJzMb)g5ud-4oyV{x>GR|Lx<)_CCM;-+%So^ApK5Sl1sctZkY+5Udw<1nc`BzT(pN z|77!b{&?)E?_Y7v58rm-{VlsHVuOD^SYX?=c_3)7>Ki_Uz?1m65yOSfR8f)P&RHnr@D8 z2KnxLirrG3EI2xNLJ#&IrMK^^S6#dN%lBSB?!AX!{MCj_-oE?M?SJ~BZ;n3hDS7DP zW82|%Hi~huU_t=p+WfJO`0Ub4zx9cC-LdWd+k&y1?|t6~w?6sZyH6?c*?uX<;jU7I zgLop`%W2u9z3Yqb+i}CjEqA`RW1-`W zTZwd}KatQ49}cAZ@gWzwwutSNc6&RLy7j+!@5UYfyz!wsgRuwqUG<|IKXdDaozo6Y zSfU;2h(x>o!-011I^<&47O|bu?wy^<-Hzw)*|FtYyI(!F`A5I_`lhR{{K@o*`7^$q zj_t{B&TMivm~-uZQ+rrBILWbVcb1WJ+hjLmOU<2Qw)2fHrHY>En4Io+*3w~feuF^m zp4U}}$iLQWIoSLS0y{ac9Ob7ZNs_H3#`<4u*TYFrvYsnhKAB9P*|%?B_qmN~@=ZxW zgY%`N+T+B%{UZ6iG7q2UGL>>~OE2l<`#8<7JjMO78my%KN+;h<`dgPyp5)s;ojjQ! z;9ZR1vWV`fYgwWPlb;GNv_a}^C9IF?10))pmkyADg+htWI1Z(W zE2iVG1NC{zIx?WtsRQILPb`gYxwiFXEdxD)PVKqftm)RdaaW# zWPaLzDv6=3w47st@RJZ@+T84}fU?k=xzsR!D}c52z*)eON8jl-1o)bXcPy5d)VVkSVvDug$FP{Bks`@k%eo8%f%)v$zac`@Ga6 zQ?TWMBqcH_=+&3&kJ9NdbOHm7MEkg)0lwpGv)~xY&asrjkVN^tsr;7%RqN#Zac&l% zCsj#A^zpxhM0BnKL)n>nLHF~Qq$4DfA52F?!yb`2;k+t5WEI?jd2U#y>O6P~u2ly4d1<#kfE)^fIEK%MWAugz z&Y)2^9n`!25zlmq*vv6K@`Y&8(@;9kcOqnQ`$J;P%MT@n2M&F}q1Ks~rAE=>5HcqI zHEFfc@hyC_MxHY=ue*h2BoVliBKbR)1BgSGl1hJs1O!29C|^v%WKMYt^Q{@+U9xEt z*kO`*&9C>{cW&ztW5npB(DQMsw8Cm6jRIur zSalqE5cuY@Bb?3`-ks?_&Tt=RyN@>*^V(EQf?ssnF#nc_&q9{~le-Z@v@_k;K}ek} zKq1LSlwDp)XoR)p)PSVc5LYIk*3el<8P^F|{$B~4vQH$gRN7BJG(@e0fBjNq{n8%& z>AcZsa~G9GkI|KuW*-gTNDgT`f?&J~ooh0s!flO!ANhHnj{?X{QG{eVfuom@J z#>d;lCsotW{734CA<^7s$HaYu8$4Gyh7epf7RxOUHh9p@zd=VrN4TUQ<{)F~HU(*e z8)Vqba429Yc~D>jEXK`vc{HSaW6d!`Ase)Mr5@GMZiYI(Dp9^^$3&TNXI28TL?EbH zzLVu?P{?$D7A93B3x^fp`*G!P8wcE(8mM~APcw|SnGY~{&=_=&84D6R-snFWBw!qX0ID<+^aQ265T#3%vz1FnlDqR! zpH4wb#j9zHnGX9X*=AV_7}@E`P5LT$4*Y*Qa8wWs_QyeYPZ;&~DHE(&DHs}>5eId7 zBMNcs8y9}?SL^Tp!R>bkV^?4J-K!pW_#I!oXkGGa0hEIYnwahjP?Q03;n?#x@3{WL z&p!LyjqAoXt-s{M-`RHYhu7@Cn(nPGtBy~(amnqxBQy=UOZj6a`L1M{*{)4^YO=26 z>p5~J!$v=#J1lr6=!e%Vop^GmD|S@Cl$deJkM(wrq=vOkR$U|VI^A6#Fg}o=2*c>c zRyyceq2NkTt^r6y{~XtK_R$XrJ}~2yGoMRD++?qfOKM#9E&UVeWnJ@`{8}%?bHl!s zclcK3Il2Rj%`d(c5O$6rB+>gCuhL|O*jQ4gM$A*EqTM)>y9^1!lB(qoBLdE!nAuN~?KLRQ!%nvD!jKnKms{TxxWfK|&M%bC`^b zgcT$^2iKF(G#g`R!LYytL575BlqHQiw+g|@_STIEi95%p5N6gALS1!_PyyV6P+bUn zFueR-!$PG63kSi=-1Gz^G4<{G zXFk&sqdR3h!2NtEoaAf9fX*JtS$ko+B3!nQoeC6b3umLhv;=9@rOI+m@u*QhZc_go)4^T@G+1vqptD0wvDtP*d7sn$Hr-}l zvPa$SkwZ~QEhu&3Y!Ka4(yrUWtPegSyF429F@vkG3D>sJE(-I2=|+WidT@u@;~Jwv zQ@_{iFq6cdoXv&BlFgQ2L?e4u-SSvCE@j8!NnIE zOSYPH2Z3&^ou!Uzv(46$?s@7M*f{TJmGNP;p=!t8rS{a@$&qsmQ8vGOE>5 zsaaorfQ^83;4Ehk`WH>J(VR?@`&`U*wXm(}&HIuL`eo7*EgP4lZawF1Yb~YzrOPj} zHg(D3je(X`7B<|`;gwyOF!?ge7GLJP(h46%ZA!D%AgTw1Bs6AE^xgUJu2J++J&jDk@2$ElLQUj;ZIygtG# z#he+43EKakF2;Zw>}O$C3&OtMp6;${qy==6j_@MGbvcCf-F#3zX&_N)H@K34EfD-z!Ek;vqGTUwK18|2U1)?r7YG~&gMqRF*&b4t@*wi**Q0|xE-X;M9+IW6)u0T1@e}@QtqHh2 z6>qd`QyVWUcih~|UNxQZF}sUuvQm*bm@Pd&c7L@@q|h2^_g zmev|ePa4o|arjXk1|2`(H|2@om$AO!Gd_aqa%qK#vr)_1L9L$Ms$lo!;}QM+XVSfri*EDY6cA!>0#o zNULByx>A8TZmH(YrJCrM7E>E3wluTpj&ckc zH8v5t!TCnpj8PU3;lY*U8y4mOPDUFz`Jo*(7UQPX;)3p)qc$)R0kNh!8+ z25g>I+U`xZqdhdHtpU<9<1UiVFYvTEhk2u0U1l}eHs+{Q2kcS*8x;(=$xsN?O=0y# z`rHwsU-?{HAMrpBx;7f}9kLR1sj%IuQZbY&YI%CBT3P zFrQbR0KLqg5MaI$fE&I6xk-W{k)X$ufUXJ>U^`g%Y`6fD2LfaHEbI!6SwP?sVsHao zwryX^q>Y#YVbWUiy_^^+$b}Y1fa@sja%5GBbR;J$qF_IatU?Q zD+$>;T$HjmmDV6%Z+)5Db2Fa~kr7&+$+J`l7Ie`KZru3{2@zadU5n1uN8w32s)D z4bxT_lxVi*rltBKY!jMlr^Tm&{b})HieevCvxH3+=S9dY&1u~v;gj0dfEm~=NeGcV%ZbbB6n(BG}2*jyByOPywJ_Ifo5kRvaCL&GE*PSj-_L_*W zl8mI!Onb3vC~L26FtUdB`UdzDS38!3d0`_~U|O2Nvwz!IEcEHx5TUQTt?rudQMWXC z!fu(##kKwFaW9SyndV$zDKRzqP=%NSVtczzLqGmYL0?u&eetLbSd!MZc?eN*1 zBgiyXS6UNfe!779j7Eapce>bVDy_rS>5w)+VZoYhkXq+?W#!OEBnFcw4S{e1VIdmb zuxYCKg{B%zm*!-UrqT~1gVR)%bR>DoT1d32*5ZX`P(|D)UdclM-XI-g+a`nb6;7K2tqYk^g+2nQ=1D*2^R4PN7>4;gc3o3B9##cTApVYE&cH&nQfBGi|j zMx6Y3z0^=lV2&dCn8e*3ylI&H+P@(*NZgILATr7*d;(EP{f(}ad|axm_KLvjb0JVj zd<0e|eYi`3C`#=>wQEZ$mEwm8C{y7D!D2U9_1Yj!rpQ_Hv^uV>;;IJ~wXOtfbXFQ_ z6M>P^=l4}9f7->T0vC*)ux>q0xZN=H} zc=7|mM&%f=RUgKzhrn&j#Tc3Y9OUjAH+#=QF2)LQK&O@3MARG&W<J(5kz$!g6BBGtd8OaX?ldR5uW-U1qxFa6+r5;J=R3^DgB}D4F#l=7YSxbRe%crH5iIMOk4uX@xfoj2$Q5M`wVgD&h5&Q|XP>au z4S_%lfq^L>FrYeMidG~F-X4gT-1JsT(=!ySG;}HT4jB+UP@E7SERyAk=HsM+P|gcH zE#pT9LH0#hqaxhO`qM*|IZHtF51C*DW^Xsr93rR=8Eje7E10MMpY~*bKR#IPs)S)R zsHHu`b@8Y{w|qPpuu|Y*RwE3QkyW!g5xsRE(M!Oj8b1B}F zrKcL9_XRR?#DNv9)byoC%jceK<8vpE^9K@(A$D$m^LV!N=09-1K65|6s_y$E7qgas z#2342D?hGU;@&*;2tNkh_Y2O={$($0ptjCDH=FvJ|9<=X{=e~z&4@19?iI9b+LJsg z;SkY#vPV9_XtE=-o3GX9jxc-vI(_+4oII{V$OcsI$tJhy>1ej`Q~JCr%079czI-xr z#n|AWaa~%!M8qYkX;|I4C}nWQ{9Uym{Ig5T$}Pj_2qm{5HHvx3%T~py7gZ@)z7&<2eui_&;HaZe0G-CtB z@tVw!jaIo|N4SsWg)qp>ZO9ae+%5;!1NIWrCZPxWz?^eVR8Z)Y)aBu_KkE_g%Uu$n z&ME5-AM~K%8@(8qnxi#kOC_&Ldo>RO%xH8>NkhZ4ftZdFLc)6Jn(x&Im!AWTj57Eu z#>Jmj{M165FfE9L@am*jfpc)7VEr=#)eS;3gTVC7IoKP34Y(L+Ata4FoP7nH8Xu}; z8l1?83NyvtmS1sO*%45k z;TIzf6gLMlwA|>UEq19wD)vDQuCf(qh6%pq$Y7rY7Gfw}X}5UUkgWx#YS4}~HUsaW z7}V}n3+u>doK5C8E2d#B2~uidFzpb}WkKz;QH!Y-9bxlp&OG!@mOC>%Be5D0g(e)K zJ|{S#!D@xCeb%(L@P^Z$7!m>Pe609m-q_n_W0~N~mz~08}vo4@1!c$hSux zW@~&r@89DR9v zTlZJKR^n=HdE&OuT(aw?O^*jwEy??tt=bY-?Ta71^vb*Ue)IFYU9}|#s+PKH_uhZQ z2Y3Jc`|tUxtCr4QO#>S}f9!)_+xOLvd|}7LvbEgWT&w-Gw<}uLeL-l)S&LQrUbx!Z zhe!q1XT8e5F8+1%uZMrV{OdEHRwe6Voy*T!ws~E>pDzRUykEX9V;LloQyF%01ndjv z{1z{X7W2jkV=?KDE#hl1+hZ>%R(>@B^A4(jYoYu~d_Aa(Q)j-~Y_F(0^VK$cMe_RT zuLjcETAd9%{d!K`#eTPkLbAvRZe_K(mc@7t(zEi8GrGV+^Y1t7eec>vpL>rjXu@8+ z->COJYa6(-RbR#_z%}bn=zaHEoc{J+j%#=D&9{FJue;WAve905^I8&FG=C=E@G9xO z(=J+H%a3mR>`r^^vnTDfPmDtQ#9MBkoX_QxMRGr~>AeWC?w?PqW53?_(|vdE+xN?N zuDd{_DhW%8Zj%(%%R*pE?@&Q{)s%|TyVSjFvVQ4Z;@&m+yZO!~PZ zdyk8!^vS*H_vw?n)OYH$>$sSmA96iwe-hOQplk{}pU-Z*7`x{a{F<1YxII82Oj@O} ziHU7LyL4jW6aRhe!u%U0n_Ber5zy+J=9F#P^*7fy=AGXd7<=%aZn<;swO@UF(>l1& z!=Md62@?g;ny2}aw|?h`k8Zo<`W+vlB+wevEY+=KPfN*_wAc^5dstD zU%M`VI`LS#0rK%3dEy(K>k zfu^Unn7Jm~Xmip&q29b7(W$jlxQ9NvARE=H65>SJd8}2kU|vm=SZa6a%8>WpDu2Vv zeIbX_D5#|^$9K=b2atue+hSVkyMUrWVm5PmqB(#iBOfV7Hy=ytUHV8^ zp7YUD?=hbXrK}7rt}=}-SYoG}3!6d?nsyb;!_;Z*Av6#iq%;M7y?c{LZ}G>{>_r%7AJs!)L{ffj9}F)s?YdmMkpfpN6BNV3`Lx@(xe}!_9e6=VSYD4sPx}{7@=@v$ucu3&Piv7TJ~hARL1H?htxV=9I9> zdTnD+27qDTPr%3tc3h>=wboln_F!jaZ6oQ7E_)e(z z_^DxmW`Zb#Ia;LA*j+A>HDVNM3C3)##RN)C6aEagV7(yhZH_GT zk%_`1d49SG>=U@l%bl==@yE_^ZDRylFm3o^i;a6&UM)6S8S`vp=_KYsG5S*Nm}kms z3sj{q<(a2iW}cm}g)vXj){tonG6ac-Ok<#*XiLn4Z2Pv7bRn-vNaT3P6vsx|vQ#q# zi!Dfpt&!zu6sjX4zB+^67Qtc*au!=4JWPOxC7rR@<~A%QkHl=6XKx!8+w9A?WwFUN zEVj{CXv<<7+pyS#uh5porrNL=&I%hsI~LpMSZs6#i^~jcE0Rrj1w$QXsv(LD#(7x`* zH|)NSes7FNkN4hqzJ%u$c<-=9+V^x#pT{uC?OxP3Cm2_iAcQ^qQj z!+9i3a>z{TOZAh(gGhzsaK4a=esVYqEGaQ`zlYUN4iBLaO%4lKUJeO{G`viS&3h$= zg7L7Hn*b6?D{(YMh6)CjwvVjhFB1H|qI&2^QMJNHiYZHgb@9e_sz3W1?G|*zF7+W zj~D4#xhT1ix&^Yy@(WHNbx3k{u#l7mvdM~y*$Sy+r#!=e@Qp8!O*W<(6-df3r4AL6 zvOqTR70?f0$p&Vc^04~JW*1npote0NDKDGc;bvlGI`R-wXEs~`Yn2IQy2eD*1_84L z^MEPLLIjtk$BAaSh&sa%SQ>vQ2H)azI%!Rp88m|sP6Y+|9-nU3llJFq{*kBp2<~*;^_r4T?yec)~4J*?lyMbjk zPeI=aXGt&h8p6!4uGd_QUJuJl+6q4#qp-NX&9SOB*TJF# z3rUSF>Klqp8~2hL8^-7MBlSF85~;Cje5rn<-t9@v?B+}LBX!r4n)%L`a;79^h@0+> z)CWDOQC7Z`A7VYE#`Pge7vA56g!b+g#$v+;#(dG{Lh4|W`F3KXe=l{Ietm8~>b%ks zhxynS>PMVePaL$VFV&AY_=F^eg|mAhX}m8871S`T!IbWyE$k2mfY0HmuSBM2WTB&d zZodu%u0w@Geqn#SJ7$rV(dR|WwxIAW%W-ss7i!s&UZ|ygy->?4lb^g$_=Mzz(t)X^ zc%f=raf9~pLb0J^&Na$8=aLnydZBPvIWLqeNVry|VATsnL3yF@;~PZWR(-ut(jr5C z-?MpF6o=cQOKaQMcvC%^MYLjw73|c z9?zZmAj2aGXa1;KU+Pu&QAItDM2ULTNLaRqX$=yFTfw4ALOhRhGq;!g|Ft0FrzFN9UOiGKGl%DZr@s0MSr#a~T7Ja}_&p%zd-{)4k|7=bJ zmf!3@6BxMRf^5&V>^J*;>Sr8~amD4@3YafqY^nhnYJe~Vw{!2CqJ8__eA8e!kcS_1 zxs*0q>tm`ETQ;XiJOd@?KmJ&Y2U08zC}R>5%%3;OEyNZS!VW-PR*qH6qbOX;fDc)z zt19tkdDYd_P)Z*9;(qzk(MC1wV3x5w>M9$Gs^$`P0cKrBt}EFCTd6_v8c~Q`xmc`Z zPZ`X1mv=;H3%C>ZVf1zXwus-&=J0$R35C>ThuYC5LDAb;)+&Z-Pm4;mZg7Ms$Bi`K zhN#%ZP_FXSrBvWzhz|HDEA>oDGN&n_2XV5?cbNFIolsp0W-d*jlEdQPlSQ(5B2FJfICM&a|Rq6n!Bj17;kdvX)XNsJuUF z+-0aSxb6bSAS(w)Evr)0Nvv;)T*^3F*hZ)I3=B%pxalQ29%ju^V6`2lNM0+7t#+}) zg6l+@+uucoO4m?>>M&C~BP?+p^JxoWcuW+0Rt|5q_TjoCo7~_tl>kRvU`{z=?RWFh zMrFhf6ahmTCi(b`HoB;;YHXKAG9_G~o>9n)UsR-N5L!;rPfmc?7;{4#p|&BmaO|s; z=;unIa^<}3kr9_-u)S0QSI$Fo^A=R_H7&2eO0aV21Ofx~$XZH`y#yC+VZ~P=ck`P0 zDDbeVWs0HBln@q}3WIuq4|rFlmx;#V>qZ+pd_}7Z2tpJpn&e71C|xIdP-baR=x=uC zM4AO6iQY9JML~Av>K+E0a=x@;nkxFrf18f6pek{60e46#)He-FVbTVnRYgnUv8Q0p z>5BIExgG{{MYOTQs&ogzYsuB>u+1alcvW*a8K@O>milxuc?`~3rB$d;+cngR)nld#}bHQ#RQ_{w2c6z_zF%I`pdW*JuFW=yL_G1%yL$t zXs5~%jRBj8ocKuQBp!|y7Mxj;cBEpUk|nbGINQY~Lw#Z%a0)05n$ICBv0?ynz#+~i zrJ%z!Qk-~9R6q?&Fb)0}9UVXwPQIx5#*huoBJPXRS>Bp3x%Gj4vMFs+=hcMI&LJNb z9yuCdapLcAJHB>cYIeJ%=W-h^cJeJM;x9}cW3+>F9xmAIgnr-N*`)aqhUSODGG@G6 z2}d>a#+rP+nlP07VLMcLJ826vyR()23}FLEOk>G2UyN&GzK2+(n({BA zM`yz_2zRb0AhdUKK>{?satd%#LQO3!qqLrKPR{bS+ebmON6R6aMa0W{!ZKuae^dnb zAzVgE#0L4ug0q7UqKb(6;XfQPxIsZC9QK=aiwc6>P$X{3B?n}d>@A1&Hov;YfdCYUk6IEh*%dfh+DikpVA;p zvG@vYAY`8tGyzBL_|$ra6AO}T9TnKO>qgA-=GT`Y*kk!l{amdg*|rz&cj~KK@r(LD zXVHR-`8u@&y{Kce>ofa$G1=Kz++}I15P#;8E=qG9%5%e}w?&<>?3d?cf3a-=&+z)y z+O^!@8$UBQ@0>@dpGz07&*I&j*Wv(V`*#zoOPT#VvzF1Xi~B)^PRzw^bFs0_YXK=Z zFL0(+c91c;<#`~|jX!uc94SiQDliVlIP>CTB*pAeNU_`J^>t-;eP(}Bcp~;Ag`A9w zl4FiMaXcpelfUe@b5e#y^THi*ACc(1DMl;j#FQ%;?iTJ8i&D#pHtOZ4$ft$<&xUA> zWa~jc`9X77USFc^_L+T&*7ccviH5syQKGR&WCJ0RV-9~-j6X|zoQgxkbT5XB+fXEy zP@?2_4CRiLn+&n{M03Hjc!ONLrulStt0CoSyrP{3m@dKU61`)@G~ zwBP6TCGD)w>`Pi4qz1JwX}dnN-|%FL5=rMmIY;q*#%!-b+Lqmt`_s2y9;pRsln`p@E@7yS`DUy3WD`p_Rq@cDo~sI9PL z)d$+6T#Wv6KCdtR{kB(M@~20kzTeI|lK9Ma+6>Vq%zQx{?tE0YU0k@_woHb$hfxV1 zA91m%`S!W#Rc%{(Q|2hl(1xU#!5BUYGxjrNOwSa=CNihWj1i}2WZt+98L&K`JtuhK zc0fk`u8z;<9(C6#yn;SfL>pf0%v3hK2$xqO4wUo9_TLa5N@<9rR*upuZBnUM44>X! zjZ}yDM%SrwE6PjQsGo6~etSgfyBBZ5bA`8UvS{;1c%#dFp+O7&EJg+sI6JbG(gq@9 z_9%F7aGgWydeO!YZ6qKTTs~EZRDj_fFcHc8tJWWAh?vnWijoG(%VT5*%r+G-YF%Nn3qS^CG?>!scB=xvxLT3s?+hjxHX3DzhM3 zYhKRgC!%t3FjrX|$fu3{cTbpGmP!C~jO=^lp3P4Y*}ZtZDN+yS&CS<4>NAP_D-b0> z@%W)P1FV`T71b}XI2wm`0+NbVKq=9mW3p!WM_Q~>g2qS$4Uaiywh39K2q$H-U2jq6 z++tB&2jeOYnDss3kcohcB(iR|b|GKfFVT%Iyoj$ZPVB);E^7Yo-uzx4VBX7a59Lr+ zK4(_-(pegC?PJ?kTs~_q$g`}14w+KM)&>;-sTFE|tG4)3GkK$1G@9mW$DOlwvJB}y z9dGZE~L34%t{ON&9*bA4z!*yh9AYbhr z;vj6@i9J%w*_{h;g)qV+cO6WOXJlQV`y}}@(?S)m3sNp7;Lju{{`teRi&BK^f(BvA zpx!KHk`OM+!vxGZXfjd}%J6OO4l!LnwxC5Ov+co1&5kF501D)=+?X+_^+`fa9 zLbWW~+(4zdfl6}&MRNmrb1!Ez!uD&Jlap&M?{=MQs*bkl$k&zzotT?eGu&N;8%#B- zvkM*=cPoJ|;;74PTTAD=90o%5o?A|Nc<4fxgL*FJp%&lzRi3Ps&*d_q!kn0ok~H9k zF;&B7(v}ZJUybDLD%Ly@hxL3{kw#+INVHM^bv0b^cKsq$J89b7B2mnVm6Y;@@s%s5-??{L!16Vpsgc7GF_IPy(m0QVN)78wfY z9vQn5gDN~Vhx_ps>m@(2v;?Tpu20GHL?$1h<}F=(KvySJ9x~a5$>mKV+|we`Ll4`i z7UIdr1+-n$%|CGpL(j$1t*HYdT1!7oypbE?W_qD|brOkjTL_ zNNyD%cd67-@zO!Iqu?z%70j)(T`g9*iPtWVd#xm>*?cA5$!H^a?n)MWoGLO3(#kl+}PTY-RcCR|rPwu}=_(v=q*u_OaAyM^avD+)&#2%E0%ra8g`Qr=X>R>RF>H_hSovzz8>R!fFOBmuLV=AgH3nv2{`bIu)T zH_b&FhCm`KziG~{agoSzIm^gu!&P5+kr@dZYlxHXM2oOboya$JcMf8te}ZJMIMw`i zSUPx<-VUi$8)9j{1XQq)&JM62?H-n_`?fg*28&%eotf%UUq=)?!J4XiWW!$*}}7M0qP3>4gb?uMfDn$o%dy)u(fIpnb)TZyRC|`$z(^ccc<`e(f$P zv?ELS$w=78&Eu?Pn=(MouqavAf z!N96{+SuKUaq>OMtGkd94NRMitJp2aS_8@ZG$_~&VEi(QbP+VeC)H((J_|Bf2eKO^ z{mN!wR>nL%XJG?#^1;wNkzrnHzG!1s%SN2;Ynxtm)3H+!MwFU+{KmHbVUB2n1`{p# zXEOPjI-q-79Mvq_&$mM&jYPG%j4$r@0uzm1`)Ypk^jdW31lKaG$Zgtg@0AQlW5CMH zg)Ow=cq3w>TiN_=M>4wWz{4WYmLD4g7Kk;7t6auDTLwcqxE@{6`N{8ge(TO#{{6t3 zD>~^_?cvS`4t?c`5B==+Pdrbap@AzpGgq~7+~B2I7oXqeHhun{rDMU|DBZR_lqD{F zYs|7Xv{~(sZ%M~HzrE|py>EEWM-P2FFu>!TU;OlmC*OPHA8vdv=eU=qOAIncx+c=` zD>@slBI13iPLV9Tq7z=#UdcfUwe)hqvad<%a5)`!B&$2@h-oP|Wo;&Hzz`Zd-FeqT zzy0Q2Z}`^bdoQ&%r#rVj@c#Ebbnqu1y^S`HOIKT)+~%<+owhbpE7np=4H~q-vbO#N z?Gp@i0NnQ4ElS#$uDzo3r7Pb4^WQyq?Ui40om$)3^~PVlpX$y5&|nes;ei~q$5THK2cul;+L_j zk&dJ@hU-X5Z1jq>Y0xM!qrxS&s#$3dYj?KGV*&{l#>KvM<5I6XaCU^cc#x9|$f@dk$S$ES4U_;k6IS)OwA z;P~`7gFxmQr5kIZIsZF3U2j?7cLp~z&^+8dHEmg;R=Q4DonnyrJVnR)PAMQ;x7S&9 z%giC?>2#a)Q@PRsh%=n>1}hLD*{}rb8{clI(c*k{*OYFflt}aeWKNk6qtFIq6%& zs)D6ym1jIYCAX%z7E&sw`Z3I(P}uCg#q}*heeC+yEUfjK{d&eHW8fI|?ZTPK`1>sQ ziGZ0M5A_$(w{ZN}^{pH6N6ZSy#xaaf$t{A>Ln*U7o&`UVtdk@hi|boT9J{`CBVGnb z50-G}82BlDz^rvXSsC z_({tr$+%iv-=aH?-M)o5(zgq>h*@pNq;G8|s%SFw$+Mb2^Lb@)eQR=?OG@whb8-8Y z4ocselvS`qdX8ayN^XrZ6@6=#$Ftz4*$D{8;`$Z}9=pDU4YF^Mg`V+gTEy|QNspaB z!AdY8V+XY4io%fOU;HK~(a(jFZ<@?eeWhF>Prc&N^2D@_b?g5;CF!ps)ZXEbrnKpsYgA%Zp2 zP@F7S^R$=2(lcW?6UOELY#2aFjHW{dkbh}zIi!G6Y(6>wnV zGOe;pm&+4`9HPgigI7=wVW3$dLW>S3?&y%~lYY=(gsTTGK5-da{9}4!@@5m;~T55r*p)3=gtaUcn72Eg5x&Hz?GdAJ-$m`(whc zZsnC#5LoH7sa=kd^n@#L4#I2@fBd|q1N_8wE3x&1zHguV%`$Y@310~?L-wD#%d$_XBqadP& zQG|v!r;(n506o2IMk)8g1P_*gZ?=WU1T0TanDO#AN%4j4!OblWXT%$|P5$?6?$xx< z4~N(d(*DYT6}Gb<>_+k&3$Jc$xv)L3nXN@~H|rj0Q0KhTAkb)>M_o4QCHfGM!~*{P zDAz#pTf6i0SsOT^WgEyrgJr*Y-p@9*CogP8K!%5W+2&r*8pvU>L*!r){d2J+B3qG{ zT+|EJ$Ps0Uy{YD2B4f8E4K}O4F#!#!9$jT~)cS2m((g)_33?#-M6nO<$v*X*XhrL} zB+(i`Ac{^8T6c|t7Q9gOw5E4F>TKe&NvdO8zQdOMM>_2J!R{3 ze|8YFFk>q`4en@%9fj8LpC=Co+uPB3?U>7=Drqm7%3?~A>aGRmlHkGZ=S?+tMSie2 zQraGWt!_GNN zr~tLWY~W%JRJ8_dlmiYSu{zweez7V>RJ=$-jw*-j**~t>$VM)%RN)a!RmmJ)JTv5K zN`GBM*`*2tJrrDcQG39OaEJ?qqHb|ZEE9d|M zRf)DB8Yz=vqfV53QoFwT+SF!@r5p3$|}wx-VZ}=8T1NLVrQ7)Gp8{$7xGZr zVD=Oz*Bb0oZ)y#JB%uhcDBft@;k_Uvio`(mv&22|Y#Z6&Kt#&mCBPrJ+Gny;D)K@M8phxAsze+^6T5u0gjh6p3gV0 z#Yz!1xP<;j_|0I3(r?!AJwYrDve`0t1a|6o9ysDf$l=VWm71*yb)-8DH5k%ba;IqI zMz`f@jFPVWJWt5Qf@?1WwR5bMR0KTPAb4j~8-h+cbWTI4JVuPVIv{AK6bGSEgZ~dP z81=}a6y>mKiAIZy*JKpfi=Gsx+dQ?Q>K1hqv^W+?VtW*7@csz4!N|VW5@2x?7A5N> z$L`%t^*asK*_Ousm>7Uc(ESN;LEScF(lPOZ34&)mAV!mXeY|*zIB^=$%bCO33~xP( z;uMI)nz4PX1@*CnM*|WuC^62AQ5IB(9pcjXGIDu!gKmsC6+G0uEX3Zguq|>SQS>3E zUdg()Ar6UHG`z$fq>Hl|MaKeHO&W1vqFRKLAYh9a!&5BIS*7K`tcw0M5&RHA-3`&7c%;&j$~xvqSEg zU9P>crNV%yYhyysva*Ot$uSmVNNY54PXi&jYQk8p-nrstm=WqLplXlT;W=-^(+jRA zBsrVJ=s<)OL4=U~VxTCVcre>0+n$z~)4?GnM(tuN({7}7x85*(MKk0eAGUNE6AR)5 z&8wntFc*bt7_Fx`Ts2K2o2y3fY~5)bF&-fG6pJVMj7F}A5Ag_HoNP15n04D6hbOc# zCFWLQrN}ys#kTd7pA2mz)5e^wjzcvSK&%>n^d|iPN9O0p#kRM9A_i*a<14;ZHyUVYM@3z)RNN6aQp?xpt_TE_=e01L!g+!pRacvEtZ zI@r7-BzRpzns&VU^~9lfX$*04bu3cLI6){Q22Z58KBn+AkT4+O*v;yx)UY%A23|y4 z13=KZCm1Viz#}kx=`MtY)GwM!gDH#TV>Z6qh^`n;9{KoL_@`pxu`@7Zk-7*Iu@IjP z9}6zh>;9a&Q~C=6$$x<0)x;AKTDsL@|Gjr0-o(-<`%7EEQJQSgU+X1gK^BFx;Hb)Q z*JMN#^M*3r3zb%jf97VUF#9uQ!%Qc7NEFfW#$_O&Z1U=sA4}q5K}o4|b^u3NEC8VS zcZix)hfYRWXes&5>U8nq!~^G#l_M{kS#7k*w?kS84NeCVOzO^^Qc(IUIG7YWhf^N* zK3iA^P7Y1@TFCdlR@{MPBA5D1!^Bu(neo6($b7P+4g7AqF7>XZM7(XQ$uc5s7Cgu| z|JNYen1%;2b7A2}xZ}4guK>FPe@KiLM z&gORfPgo6>Cfk<8tdZgP%DUJ7X%ipLE;C2 z#>iz+#8Z2waV4rj2Z7wmbj>ud75j$yVGE`jGXtl1w_=n@v1(k7BHA2L)9K>Dm$sH5 z#l!!EpzD`C0?nMNyy2HZBQCL!OTD&5?;ZuMC7M)6(@`miOHDy=3fbCp9yYL|>zHu7 ziIgretJ43{)F}9Oz{{1I43H|;N=LxTNqd}M@ zWy)Y50D|X-l&87gPB9MYs0$-4`Mf3@dOqYl!=^S5pcW`6%Z)A_aO+dNI+Pibj&#b; z8u?pZ)rNGQBBHUQjTVj6_JgFZ z5oPA9BhCNA*y|-bVAfa?2T>@w!C2JF6mi6E!VPBUcqkERj**4z6^k_Al$~e^P&31& zenMA~o(9cYfFS-ei&jQ^EteTPX-8ursuW?tp43sH6v?My-2EKo)yHeJ%zcq&d0~Mi zDJzcEcIZ5g5KLV$kK-F;EBb-kWkHcIunwEQ4H4k#8%&jKL-vh;e#!3wx|}caR*4z7 z%%taH?$WtST1XWtP}--KU8c9JHJG(haT&{T{p^7ZX5P;uzu+)b z-C*X|`XFs-{|eN!o0U2YM;wM+#8bu4iVa99vVM|T7P`}#T5<85YwqRN&k@Ex^9ubL zE&D?snWRV`b%!(g&7@(Cro&)^OsdDOnLcdgGP@$BxnUGr7VZBDLa|NwSHh|J}3^fO0PI58jhHU0X;&Ebh(F{Kijt6 zh~O8PHx?p6qgaegzl(PXGT3r(z`5^tKV!{sCKCDILoxhKp?0M)x_0~-5o9EalBHi; zwnNMW=~SP23N^7&A|J2Kaj3@{A}b~6U@dIsig1p7me{EsXwKxLQ;}%?=jfM?KgM<` z;B0tiG~;sCx}4Pgn&uCa6Gyc&MhhnaH3U$(riJyVVa|cm%;6Jlj1eUhr+9tvml7w& zNUPyeW}>xdAyTH>L)s4GYX*J9rS@pvVf{?yvWqvSp)(rL1Djg-;noL1Ys6rXvAI2( zHJqfP6Gl>O$K*SgGjIf8gwe?#+=(WsKKr|QwgxZRL5sy9Bi^PJ=*^qOxCbCg$`3Q+ z@dXM)DWYJr0`@HX^j$WbApKHL0aDU36ph zdGt+?MPyW`^+Da|J~J7NUf?`DbxxX4UCCHUdAW(2Z;8r#j?2@Wl1jB0ZA6G|R}T}P zdn1{Toe`@A01U%~q9KeIE+#i#&;)zv@F~-lq8h{brsa=ZE82j&z`5c8jJHsnz=Fi9 zft3LF(t%gChj4RXrXK#o@Bd}fg=xd5Hk8`%sTXBkK)o8*6fHnAgCR^dJ0#5I1p*Eg zw6-h|%w%2Q;CJLfais8G$<8ppSmjvhfAU6+L)~oxxIrLUkUuoWvbD7v!+H}OP3xr6 zf3rogCA=FA*xm#*Qzl_d8^&=mFR5#ufWCm-wP+*B+L$IEh{+R|NhaO4-jyNPq`~^2 z)(R=dN1bv#XO0>abXMcEH_iq}482kjxo>lG(rQEp4mgY-C}wn=6xr$-@R@Vqx+S zeML+z`oHl$X=)F%rZ+@|Ix^Lcv*3*2ySL1BHJ3A#)SLCeyb{!@AR9g#m4zUj9Srj) zS{DxFN0R9(-EWw)h)Mj>c;S5kb*BL`#%p8}WfyKBSmxf>B+zE;q0jRJ>a1unHZyl$ zz#ge9tW_;w26bxy$MUBbpGVvpAoy0%WF80=(PQ$9g*Zyu0ki%2Z-bF z3nf`-IH`nu3*N5RaQ8Hzm<~xXHE-5Nhl*jtNy7&j_w!uHP;vW!{6;pk2dMwX8!0)c zYlm?NXyV7*Czz(qH?zOK_3(Q`^GmaZO|6}}w82d-ntF%Qkr%Vh$NbkH-h4)}#sF2= zQC?i-%!|x$1sL_60;cjXDwU}(KwC3g5XV8A!VdGfHH=6Ai_{SzXNxu|(z*~Dn$!N( z-_Zzm#0zN}?}5S#n1up5da+7{7a>=2du?tHX8{2-YWG!iA@cJKKQFXpzihcb3*cU{ z8c@oA2&*Y4iUZ#>Q~P24D@7l)gDG2%2UGqQg8$5fQ97{(*P6i~elK=c4)I~`z7jl% zy2J!#$yLJ&POcg)Y6g-=C8x>v^lwW8JHK8b4Vt$_Jq<=>gA^5aZKFy5G<3}nd;q)| zb(V=>Xn`#OAx4GYI&d%;TfPyb> z;pHQRbv&gD;)Tu6YAXqRA&N#LvKF=2B~h>wDb6OgeHPZi+G5j!pC!`J-;dU!jjSf$ zqT4<+MVYD<(~6Y#;EU0XY~+PgB0D}hMR3N%Dq-tTnLKB(VTsTLJ12ygh&_~RkXwJD zf<`3IFnkIRyK~Cg?uvK5$nR#-`;BThKwptVW+2ibJ?CpkTL*|7eHBP>(b6S<%Pt+E zD41WH8A)`B?t)h>dsk1VxPG$Ism6&W9qU;Hbh@8>bxa*LL}C!a9;M$b z_BBsh9n6&%wj0_j1=+O0oi>yr>3$<-4$Y~%rVHKimh*6+UKr-@Cu>x@(-NU2L)_J_IGrX-` zXAj~=?vXLqdi_R7i(`vD4UHhL(wv?H6-@@(2BX;$u2X;oT&SQmn5-BXq|i3bhA$|# z(Oi9m7P8@Q1B_NXqNivnvO*e~Pyw2fwHj?p?Y0dDaOVYAw&N?(cq^Zm{zIYIKBHI! zuc4}Y;EobVfaWHNEPoM88!$Fs@{jBe*~tt9DHKIS5>>Bn#7wJH`wn!uJez&%gVZ^} zk`^mb{hr1xCD|aPKz?zj6y@|=V41LVC<;)Du=eCvruH#QvEd})xos|eovnn)Tmg% z1x4K|Nj$l<-jA}4;OX|mojQPxrZ}KaV-qGJ!enl;;a9NvGK_m~gDzx6u3c!lLqTOR zK6<)_tpki=269^N+NF&_?P!}`t8E+LuOFY?vFZ&@I0hI9x0$%s)(0DNCxGWlE&IOB zMWEz2F9?NYN@D%Lqz13#@5jOa-00C!{yuTzoHkzO_04|E!=nfb(J0x&qX>YgP!|2o zQH!G9BCBao+}V#>_V_5)i!=pV>#xEeSu4I}zpG>|yVfGul3koBZf)Ln(Az@?%wn5L zhc95hz4H&of9_p6r`=R=7M5j4hL!%4vYSc8hXTD;lYu-LbGt}m02I4kI-t9)` zP90!_6d(R2+44=quhnpj;xigf?sF97<<3?@DrC-PFHLJ_glBOOBl&{MJtN%M8Yh>H zuml@TxVWYm3&|H%#<3yV*uiV4+B2I$T_<3c?&O_x?n|0bt+btNZ!bU3a3-uXNtf=r zvArU>-?gHpka_KpQ&>F7QLAxO%ccYE$#jW!9i4x+wTe?nOMriX_u;t`9uLoIO(auj z!$CxrxU2@R>NZ{h$`p8^{Tfdj7i?~=a6QEPmySChayq3F%>JAwGMhxxIvj0e!>_QN z5rtk^$|~AWk6P26u$9nGl1}f^g`DgQr(=IlD@HxRUaskq8>pBr%~4Ij-tacayF8uP z-dfIyB(og`HQB2YBgIu6Ba5z4ZdsZn4~iR1{?xKBf?_k7b8O0I><3y?k`{C$s}f1C zpbsSn1l`_Qt7V7OLOQ(pfKN3`C7sIb+=Ephd&+GrB)9ig^itxJ>8E1_c#cO-pd{?LX54H^(k+FE+uz) za?x1wS$*f7&4zvKG1D8JLb|48C9x7hVy6yZ0WUeHJ#66|QwX}moTSYr>EmXoSy2;> zymKGZ*uR#C^A>i_QkkEPyv*_iIUbT)x|P+#ku^vq2HMq_ENd)AK;aG)Q7!hhR@vM& zn2sak(yf}*V8i1>SldJcGi225)eJKt=T-uMaSsPp%6t|FBofS*jAHH4GMt@RCNjyI zas^g7vNCxU37<1r$QrGNCOcPba$&K_#1#{84!)EnhZdF`Doe7yu9P&Im8Eeh3rp9Y z%X3XA-eT;9Ow4Y8qZ=AtC0N-)64KSSAIQTC=@_mABgVKhKk}L`-pnZ@FN!E@s!2nf z5#G~iu~3?c?ryZkkt;8aqNC$)-N{@TO$aCOgQHYYm~vO65m_FXD$|e$HmYoW0Q;OX z+c0CX#su!O$6d6Dbaf&oI8jcA`J}Lgix`-3Dy$F2Wh9J?P~*OvVzhNZ9GE)bbi6t7 z5gZh=&_Jk?gKi+nmPF35Fj36C6EF2+Cumeb3E3q7yUNTB7^_6~F;31bEWB~BC|v0n zZ91^=X%}rKU-BdWZblbZf+C(N*6bWZRFhzEW_0UFDw$=RCBIZz8$aE#P2PVo*!*fT zQs)s~g|(OeAK0cXT5la4Y!;R;GyF|Ff|*-KY=Yf1T!uV|HrW@3b#97%Pj4IzU>x!P zig6UnINHyKSZ*ABP~Vs}H^~#FP+Zz@1Gy~fjiW`1+G(FwW2k?9aL&$DC3?J;KA#2HncFdf_3UMT}?(%sy6*47U1nJn8* zS0>XH+go6>K1kcERR5-7nOmr3_6OIREj1{`fXVj?IyjJxOF75c>v_w9U4W%tlbN%_ zrgZp+NtX^@u(_S=qhX!do=%sEFRK)&ueB8Lb!2E6+TH--UC04W$ZVQOLB9%_li9MK zmVy4{cCyD+Y8<;)i-}oK@%1rx$b@7o(&byX>3DeZfcPih3L6EXy>7dZ;V98iX zrekb?S1=l<2%yJ{AuTGQw7~cQx)Em|q{W6egj;+(veHbl zaj-}IWjx$EfE#&nBWd73Yqd4aX%iVquH6xD4)IeBSAMGH{HLsnINPJvWj**hZbraR z*3nS|NA_xGLLutTi1L6dKLt4dSpbG}S^*X&7OwA^=!>y1?zKt7(DZ7KPmjyK#plY3*svV?ZYdTnxQ!!Z_OILXd1CwPy)1XV8 zDou+#!J8~>NpdivEV*=H$)&l;!bul~T3OPNElazR=-YF7g~_6UE^nL$`rsxWhZ^t$ z(jjA9acq-b{A0`q(9sw#>X>{al7V)Dcrum#oz^90?4Wq`vz{jS#aNmf4uuf#lc7-k zSrrinL6KH@J#Pp*oN{W&qiFgsbh__`u(i}R#Uj;_VX3!uP?nRjbp|u+H13-~{-i)x z$+&UCNEU=&Dz!71jkBO=VnR45u!9fmrvbF&YUykZ7H{7mToY383R)vt%N!^=x{tS| zYE{J9ZY0k_0_?u=s}Wz#1Z)THz{N&AB)?Wd^YhLDT^t1LmI06Q?gg#D@zQftO||K;+@8!> z7eX-FN`5X_?1AWnj@%aX(EFa#xXpPWA1qnBabZxDLbY+q);h^cp;WC?+l4Dl(UK7R zD6q~Dnt)MeT~vL-UPh$6Z259l1|^dai4ylsJneAKzgFPu6R!HJCfx;_IfE0TvV^_!=Tb zRqd|yt%-CA;kD)SS9xS!^UbmLYmp3MvD&!uk8|G~o6@LK#pIH+gEjWm;OlsoKTT81 z`8mzcS~kkplzr9tTJ4HVwj(yx&JOH2O*YKeN-`p%8qN+@xFQpX+ZaE!XIAGx{@KAY zmyzH~oZ#m;KXC!`r6W8F2@!V;+Yx+RB z^IMUVU^uPc+;1*qF9xwy`pv!Sd=6d=7Ay37nBQ7`)A*Er5AvG>nHSUCq<+WEr<+Ti zS2p%*kcI0J{hr9zBS>fL6%8VWV6|)eh{2MGwFmaK2e)C=?|te^Z{&Q7$;Wn#`Y(U@ zi#1NJix|WEFyuLeOif-R-N%;uAg5iIaFJ2xr#F7%hn()^49s!6c(`u?l%zAUQ?*9g z<2W1Bron5pm+iw;B)QREzOTK4V>h%i(q6d_GkBC=9IQ>Sd1*sOp%QL@uhZPrRu!AT zW*ykHj~m`ssiYFL)wPeit5-WfNM5>(gf+^Y=JuoI{77Bl6vy8qW=H#(pF-2+x-0#E=|t62K{%gG7Q+W^$ONK(LWutLN|r9li_U9oaEou(_b&LD$&Y^tf&5lC6+)VjDeLO8cuJvM#$jZCiRu zx_;Z1tz1x+w$tNDY`9AG0>iS$gE@wSIfhM#;pl>fn?>bzx^`Q7#@3We^MSp_T38JV zATvqA42^>ZGLW7OGT^~&arK2X8Z2nk%rR)@7>qg$RxH3^w5T$qF|=VTU6#bBsl=5l z>iL&D?fLfv!|7yj0GpFYI8g*i*9o2Wq!&z`V#_(!EI^Ou*o@}btmu!8*9MgVpBKEb zm{5qLP1iCA)6=)Y3E|oRb78s0u+$J-m(FYhzmvg?kw+?xG<*!`;YLs!b$6S?BmdZ82j3kdC)$DKb2M1EOW>MB} zE3(<^te%4rkCM1+F1$}mPs#V1I6OZ@P)_7+9H8t{pkX%S6XK20Mo>;McRI2oof*l7 z7dxQ*!`?T+EZ!I;*hB8lm`S)LFUAYFGc5ZnIs6SOlb??N5K$=)6J2KaOt-V8dX)s# z$|Y;&YSpX@R^Vh;Q>^Ca{`|>#4CV?fUk_s*BnS0=T#dI@xpCz@ncT4mqNowo!Ar}+ z_Iz3$?2P>*XK&KM8|{2hoDm=#GZD1h=BfkFnp_6hI(HYHRtnBM`c#m5f_S&(?+`H~5&-38{nYwFg%43!w9v`{_P93ZpxJ~vsu$miC0X;Q zQ7F?O8dRg(T;BRzM~enzzC zpbG3Mr~>;8s(_`T3T!5*0=`JO+u1tcXp;Q#`Z!I%)^5%Y!OC#cJ}KN71HS1}Vx_#2 zlx1v^mt|f~%2I`6sg{*FpAGVb>pIb+s%vq?{L1_JXk$o=GJz$ z+!R0Da#M-5c4HrLXJU&jFtNsLeP`=SQj_+QeeE%qI?a{^t2I&^?WOzL%aqCiH@Avh z%PNutUB|?8S-@CpOG#Iz!9tR1)9uy!+EXqmVF^jnMEf{ajV@`@trkbxDQiQQl(yR{ zRco`vJ7F8wJBaS}bZi^iQ!A^{R9H+BD6#AUw@#c$)xe5$oPSrDQ9f;^>6(=Kn8eGi zx;fuS^a6N@0K`i zt!Sh@CE={ZhOET6t#gR9v6{kb!l19vY8NP?_q4UN##(BtC8=)Va-wiii7CrF#juz# zxXm;*i0Z{E!-6}M`Cen~+454;i*UgiP`A8sqYJB2tEoOt81xnCkU2z7 zNp%aClZA^)Oj+J39*an@NL}H*2*P37aBvIIwtDOIJ@3k!8HO)8HxQdYj!RRsOQ>mK zYMLfZuJqRCLu~lSnky0rI*T$=iv(?^qG_cwuJD!65mrehc>y_3zR8lwEb8)@y(`1N z^+uFE?WqB*+Z6A

H1V0T9ux!FWfcFxj@oTBWH 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%0D;y^74k!pD({+^mog}(Y^XGlv!Ov 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

;{0R#&oh%l3Z%|Di~Z3clh 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­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 J#mfn5{vTbK#IFDV diff --git a/core/Cargo.toml b/core/Cargo.toml index e2f86e46f47..17d88b76cd3 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -50,6 +50,7 @@ iroha_telemetry = { workspace = true } iroha_primitives = { workspace = true } iroha_genesis = { workspace = true } iroha_wasm_codec = { workspace = true } +storage = { workspace = true, features = ["serde"] } async-trait = { workspace = true } dashmap = { workspace = true } diff --git a/core/benches/blocks/apply_blocks.rs b/core/benches/blocks/apply_blocks.rs index c16c16fd8cb..e816c209235 100644 --- a/core/benches/blocks/apply_blocks.rs +++ b/core/benches/blocks/apply_blocks.rs @@ -1,5 +1,5 @@ use eyre::Result; -use iroha_core::{block::CommittedBlock, prelude::*}; +use iroha_core::{block::CommittedBlock, prelude::*, state::State}; use iroha_data_model::prelude::*; #[path = "./common.rs"] @@ -7,13 +7,13 @@ mod common; use common::*; -pub struct WsvApplyBlocks { - wsv: WorldStateView, +pub struct StateApplyBlocks { + state: State, blocks: Vec, } -impl WsvApplyBlocks { - /// Create [`WorldStateView`] and blocks for benchmarking +impl StateApplyBlocks { + /// Create [`State`] and blocks for benchmarking /// /// # Errors /// - Failed to parse [`AccountId`] @@ -25,28 +25,36 @@ impl WsvApplyBlocks { let assets_per_domain = 1000; let account_id: AccountId = "alice@wonderland".parse()?; let key_pair = KeyPair::random(); - let wsv = build_wsv(rt, &account_id, &key_pair); + let state = build_state(rt, &account_id, &key_pair); let nth = 100; let instructions = [ - populate_wsv(domains, accounts_per_domain, assets_per_domain, &account_id), + populate_state(domains, accounts_per_domain, assets_per_domain, &account_id), delete_every_nth(domains, accounts_per_domain, assets_per_domain, nth), restore_every_nth(domains, accounts_per_domain, assets_per_domain, nth), ]; let blocks = { - // Clone wsv because it will be changed during creation of block - let mut wsv = wsv.clone(); + // Create empty state because it will be changed during creation of block + let state = build_state(rt, &account_id, &key_pair); instructions .into_iter() - .map(|instructions| { - let block = create_block(&mut wsv, instructions, account_id.clone(), &key_pair); - wsv.apply_without_execution(&block).map(|()| block) + .map(|instructions| -> Result<_> { + let mut state_block = state.block(false); + let block = create_block( + &mut state_block, + instructions, + account_id.clone(), + &key_pair, + ); + state_block.apply_without_execution(&block)?; + state_block.commit(); + Ok(block) }) .collect::, _>>()? }; - Ok(Self { wsv, blocks }) + Ok(Self { state, blocks }) } /// Run benchmark body. @@ -56,17 +64,13 @@ impl WsvApplyBlocks { /// - Failed to apply block /// /// # Panics - /// If wsv isn't one block ahead of finalized wsv. - pub fn measure(Self { wsv, blocks }: &Self) -> Result<()> { - let mut finalized_wsv = wsv.clone(); - let mut wsv = finalized_wsv.clone(); - - assert_eq!(wsv.height(), 0); + /// If state height isn't updated after applying block + pub fn measure(Self { state, blocks }: &Self) -> Result<()> { for (block, i) in blocks.iter().zip(1..) { - finalized_wsv = wsv.clone(); - wsv.apply(block)?; - assert_eq!(wsv.height(), i); - assert_eq!(wsv.height(), finalized_wsv.height() + 1); + let mut state_block = state.block(false); + state_block.apply(block)?; + assert_eq!(state_block.height(), i); + state_block.commit(); } Ok(()) diff --git a/core/benches/blocks/apply_blocks_benchmark.rs b/core/benches/blocks/apply_blocks_benchmark.rs index 9a3f54b1dfb..93b1c56c5ef 100644 --- a/core/benches/blocks/apply_blocks_benchmark.rs +++ b/core/benches/blocks/apply_blocks_benchmark.rs @@ -2,7 +2,7 @@ mod apply_blocks; -use apply_blocks::WsvApplyBlocks; +use apply_blocks::StateApplyBlocks; use criterion::{criterion_group, criterion_main, Criterion}; fn apply_blocks(c: &mut Criterion) { @@ -14,9 +14,9 @@ fn apply_blocks(c: &mut Criterion) { group.significance_level(0.1).sample_size(10); group.bench_function("apply_blocks", |b| { b.iter_batched_ref( - || WsvApplyBlocks::setup(rt.handle()).expect("Failed to setup benchmark"), + || StateApplyBlocks::setup(rt.handle()).expect("Failed to setup benchmark"), |bench| { - WsvApplyBlocks::measure(bench).expect("Failed to execute benchmark"); + StateApplyBlocks::measure(bench).expect("Failed to execute benchmark"); }, criterion::BatchSize::SmallInput, ); @@ -24,5 +24,5 @@ fn apply_blocks(c: &mut Criterion) { group.finish(); } -criterion_group!(wsv, apply_blocks); -criterion_main!(wsv); +criterion_group!(state, apply_blocks); +criterion_main!(state); diff --git a/core/benches/blocks/apply_blocks_oneshot.rs b/core/benches/blocks/apply_blocks_oneshot.rs index da6ce8527dd..db09e9d427f 100644 --- a/core/benches/blocks/apply_blocks_oneshot.rs +++ b/core/benches/blocks/apply_blocks_oneshot.rs @@ -7,7 +7,7 @@ mod apply_blocks; -use apply_blocks::WsvApplyBlocks; +use apply_blocks::StateApplyBlocks; fn main() { let rt = tokio::runtime::Builder::new_multi_thread() @@ -19,6 +19,6 @@ fn main() { iroha_logger::test_logger(); } iroha_logger::info!("Starting..."); - let bench = WsvApplyBlocks::setup(rt.handle()).expect("Failed to setup benchmark"); - WsvApplyBlocks::measure(&bench).expect("Failed to execute benchmark"); + let bench = StateApplyBlocks::setup(rt.handle()).expect("Failed to setup benchmark"); + StateApplyBlocks::measure(&bench).expect("Failed to execute benchmark"); } diff --git a/core/benches/blocks/common.rs b/core/benches/blocks/common.rs index ef489686047..7b7a96d7ed6 100644 --- a/core/benches/blocks/common.rs +++ b/core/benches/blocks/common.rs @@ -5,8 +5,8 @@ use iroha_core::{ prelude::*, query::store::LiveQueryStore, smartcontracts::{Execute, Registrable as _}, + state::{State, StateBlock, World}, sumeragi::network_topology::Topology, - wsv::World, }; use iroha_data_model::{ account::Account, @@ -22,7 +22,7 @@ use serde_json::json; /// Create block pub fn create_block( - wsv: &mut WorldStateView, + state: &mut StateBlock<'_>, instructions: Vec, account_id: AccountId, key_pair: &KeyPair, @@ -32,7 +32,7 @@ pub fn create_block( let transaction = TransactionBuilder::new(chain_id.clone(), account_id) .with_instructions(instructions) .sign(key_pair); - let limits = wsv.transaction_executor().transaction_limits; + let limits = state.transaction_executor().transaction_limits; let topology = Topology::new(UniqueVec::new()); let block = BlockBuilder::new( @@ -40,7 +40,7 @@ pub fn create_block( topology.clone(), Vec::new(), ) - .chain(0, wsv) + .chain(0, state) .sign(key_pair) .commit(&topology) .unwrap(); @@ -53,7 +53,7 @@ pub fn create_block( block } -pub fn populate_wsv( +pub fn populate_state( domains: usize, accounts_per_domain: usize, assets_per_domain: usize, @@ -162,11 +162,11 @@ pub fn restore_every_nth( instructions } -pub fn build_wsv( +pub fn build_state( rt: &tokio::runtime::Handle, account_id: &AccountId, key_pair: &KeyPair, -) -> WorldStateView { +) -> State { let kura = iroha_core::kura::Kura::blank_kura_for_testing(); let query_handle = { let _guard = rt.enter(); @@ -177,25 +177,30 @@ pub fn build_wsv( account_id.clone(), Account::new(account_id.clone(), key_pair.public_key().clone()).build(account_id), ); - let mut wsv = WorldStateView::new(World::with([domain], UniqueVec::new()), kura, query_handle); - wsv.config.transaction_limits = TransactionLimits::new(u64::MAX, u64::MAX); - wsv.config.executor_runtime.fuel_limit = u64::MAX; - wsv.config.executor_runtime.max_memory_bytes = u32::MAX; - wsv.config.wasm_runtime.fuel_limit = u64::MAX; - wsv.config.wasm_runtime.max_memory_bytes = u32::MAX; + let state = State::new(World::with([domain], UniqueVec::new()), kura, query_handle); { + let mut state_block = state.block(false); + + state_block.config.transaction_limits = TransactionLimits::new(u64::MAX, u64::MAX); + state_block.config.executor_runtime.fuel_limit = u64::MAX; + state_block.config.executor_runtime.max_memory_bytes = u32::MAX; + + let mut state_transaction = state_block.transaction(); let path_to_executor = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../configs/swarm/executor.wasm"); let wasm = std::fs::read(&path_to_executor) .unwrap_or_else(|_| panic!("Failed to read file: {}", path_to_executor.display())); let executor = Executor::new(WasmSmartContract::from_compiled(wasm)); Upgrade::new(executor) - .execute(account_id, &mut wsv) + .execute(account_id, &mut state_transaction) .expect("Failed to load executor"); + + state_transaction.apply(); + state_block.commit(); } - wsv + state } fn construct_domain_id(i: usize) -> DomainId { diff --git a/core/benches/blocks/validate_blocks.rs b/core/benches/blocks/validate_blocks.rs index f7a5e40b1b0..ec1e730e1b2 100644 --- a/core/benches/blocks/validate_blocks.rs +++ b/core/benches/blocks/validate_blocks.rs @@ -1,5 +1,5 @@ use eyre::Result; -use iroha_core::prelude::*; +use iroha_core::{prelude::*, state::State}; use iroha_data_model::{isi::InstructionBox, prelude::*}; #[path = "./common.rs"] @@ -7,16 +7,15 @@ mod common; use common::*; -#[derive(Clone)] -pub struct WsvValidateBlocks { - wsv: WorldStateView, +pub struct StateValidateBlocks { + state: State, instructions: Vec>, key_pair: KeyPair, account_id: AccountId, } -impl WsvValidateBlocks { - /// Create [`WorldStateView`] and blocks for benchmarking +impl StateValidateBlocks { + /// Create [`State`] and blocks for benchmarking /// /// # Errors /// - Failed to parse [`AccountId`] @@ -28,11 +27,11 @@ impl WsvValidateBlocks { let assets_per_domain = 1000; let account_id: AccountId = "alice@wonderland".parse()?; let key_pair = KeyPair::random(); - let wsv = build_wsv(rt, &account_id, &key_pair); + let state = build_state(rt, &account_id, &key_pair); let nth = 100; let instructions = [ - populate_wsv(domains, accounts_per_domain, assets_per_domain, &account_id), + populate_state(domains, accounts_per_domain, assets_per_domain, &account_id), delete_every_nth(domains, accounts_per_domain, assets_per_domain, nth), restore_every_nth(domains, accounts_per_domain, assets_per_domain, nth), ] @@ -40,7 +39,7 @@ impl WsvValidateBlocks { .collect::>(); Ok(Self { - wsv, + state, instructions, key_pair, account_id, @@ -54,25 +53,26 @@ impl WsvValidateBlocks { /// - Failed to apply block /// /// # Panics - /// If wsv isn't one block ahead of finalized wsv. + /// If state height isn't updated after applying block pub fn measure( Self { - wsv, + state, instructions, key_pair, account_id, }: Self, ) -> Result<()> { - let mut finalized_wsv = wsv; - let mut wsv = finalized_wsv.clone(); - - assert_eq!(wsv.height(), 0); for (instructions, i) in instructions.into_iter().zip(1..) { - finalized_wsv = wsv.clone(); - let block = create_block(&mut wsv, instructions, account_id.clone(), &key_pair); - wsv.apply_without_execution(&block)?; - assert_eq!(wsv.height(), i); - assert_eq!(wsv.height(), finalized_wsv.height() + 1); + let mut state_block = state.block(false); + let block = create_block( + &mut state_block, + instructions, + account_id.clone(), + &key_pair, + ); + state_block.apply_without_execution(&block)?; + assert_eq!(state_block.height(), i); + state_block.commit(); } Ok(()) diff --git a/core/benches/blocks/validate_blocks_benchmark.rs b/core/benches/blocks/validate_blocks_benchmark.rs index 70026372cb0..454e07e3f4c 100644 --- a/core/benches/blocks/validate_blocks_benchmark.rs +++ b/core/benches/blocks/validate_blocks_benchmark.rs @@ -3,7 +3,7 @@ mod validate_blocks; use criterion::{criterion_group, criterion_main, Criterion}; -use validate_blocks::WsvValidateBlocks; +use validate_blocks::StateValidateBlocks; fn validate_blocks(c: &mut Criterion) { let rt = tokio::runtime::Builder::new_multi_thread() @@ -15,9 +15,9 @@ fn validate_blocks(c: &mut Criterion) { group.significance_level(0.1).sample_size(10); group.bench_function("validate_blocks", |b| { b.iter_batched( - || WsvValidateBlocks::setup(rt.handle()).expect("Failed to setup benchmark"), + || StateValidateBlocks::setup(rt.handle()).expect("Failed to setup benchmark"), |bench| { - WsvValidateBlocks::measure(bench).expect("Failed to execute benchmark"); + StateValidateBlocks::measure(bench).expect("Failed to execute benchmark"); }, criterion::BatchSize::SmallInput, ); @@ -25,5 +25,5 @@ fn validate_blocks(c: &mut Criterion) { group.finish(); } -criterion_group!(wsv, validate_blocks); -criterion_main!(wsv); +criterion_group!(state, validate_blocks); +criterion_main!(state); diff --git a/core/benches/blocks/validate_blocks_oneshot.rs b/core/benches/blocks/validate_blocks_oneshot.rs index abfc09f7e7b..118ce739b99 100644 --- a/core/benches/blocks/validate_blocks_oneshot.rs +++ b/core/benches/blocks/validate_blocks_oneshot.rs @@ -7,7 +7,7 @@ mod validate_blocks; -use validate_blocks::WsvValidateBlocks; +use validate_blocks::StateValidateBlocks; fn main() { let rt = tokio::runtime::Builder::new_multi_thread() @@ -20,6 +20,6 @@ fn main() { } iroha_logger::test_logger(); iroha_logger::info!("Starting..."); - let bench = WsvValidateBlocks::setup(rt.handle()).expect("Failed to setup benchmark"); - WsvValidateBlocks::measure(bench).expect("Failed to execute bnechmark"); + let bench = StateValidateBlocks::setup(rt.handle()).expect("Failed to setup benchmark"); + StateValidateBlocks::measure(bench).expect("Failed to execute bnechmark"); } diff --git a/core/benches/kura.rs b/core/benches/kura.rs index e8e0e6b75c5..26eb1907a44 100644 --- a/core/benches/kura.rs +++ b/core/benches/kura.rs @@ -10,8 +10,8 @@ use iroha_core::{ kura::{BlockStore, LockStatus}, prelude::*, query::store::LiveQueryStore, + state::{State, World}, sumeragi::network_topology::Topology, - wsv::World, }; use iroha_crypto::KeyPair; use iroha_data_model::{prelude::*, transaction::TransactionLimits}; @@ -49,11 +49,14 @@ async fn measure_block_size_for_n_executors(n_executors: u32) { let _thread_handle = iroha_core::kura::Kura::start(kura.clone()); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(World::new(), kura, query_handle); + let state = State::new(World::new(), kura, query_handle); let topology = Topology::new(UniqueVec::new()); - let mut block = BlockBuilder::new(vec![tx], topology, Vec::new()) - .chain(0, &mut wsv) - .sign(&KeyPair::random()); + let mut block = { + let mut state_block = state.block(false); + BlockBuilder::new(vec![tx], topology, Vec::new()) + .chain(0, &mut state_block) + .sign(&KeyPair::random()) + }; for _ in 1..n_executors { block = block.sign(&KeyPair::random()); diff --git a/core/benches/validation.rs b/core/benches/validation.rs index 814e565fce0..be69acc5211 100644 --- a/core/benches/validation.rs +++ b/core/benches/validation.rs @@ -8,9 +8,9 @@ use iroha_core::{ prelude::*, query::store::LiveQueryStore, smartcontracts::{isi::Registrable as _, Execute}, + state::{State, World}, sumeragi::network_topology::Topology, tx::TransactionExecutor, - wsv::World, }; use iroha_data_model::{isi::InstructionBox, prelude::*, transaction::TransactionLimits}; use iroha_primitives::unique_vec::UniqueVec; @@ -56,12 +56,12 @@ fn build_test_transaction(keys: &KeyPair, chain_id: ChainId) -> SignedTransactio .sign(keys) } -fn build_test_and_transient_wsv(keys: KeyPair) -> WorldStateView { +fn build_test_and_transient_state(keys: KeyPair) -> State { let kura = iroha_core::kura::Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); let (public_key, _) = keys.into_parts(); - let mut wsv = WorldStateView::new( + let state = State::new( { let domain_id = DomainId::from_str(START_DOMAIN).expect("Valid"); let account_id = AccountId::new( @@ -78,6 +78,8 @@ fn build_test_and_transient_wsv(keys: KeyPair) -> WorldStateView { ); { + let mut state_block = state.block(false); + let mut state_transaction = state_block.transaction(); let path_to_executor = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../configs/swarm/executor.wasm"); let wasm = std::fs::read(&path_to_executor) @@ -85,11 +87,13 @@ fn build_test_and_transient_wsv(keys: KeyPair) -> WorldStateView { let executor = Executor::new(WasmSmartContract::from_compiled(wasm)); let authority = "genesis@genesis".parse().expect("Valid"); Upgrade::new(executor) - .execute(&authority, &mut wsv) + .execute(&authority, &mut state_transaction) .expect("Failed to load executor"); + state_transaction.apply(); + state_block.commit(); } - wsv + state } fn accept_transaction(criterion: &mut Criterion) { @@ -142,12 +146,12 @@ fn validate_transaction(criterion: &mut Criterion) { .expect("Failed to accept transaction."); let mut success_count = 0; let mut failure_count = 0; - let wsv = build_test_and_transient_wsv(keys); + let state = build_test_and_transient_state(keys); let _ = criterion.bench_function("validate", move |b| { let transaction_executor = TransactionExecutor::new(TRANSACTION_LIMITS); b.iter(|| { - let mut wsv = wsv.clone(); - match transaction_executor.validate(transaction.clone(), &mut wsv) { + let mut state_block = state.block(false); + match transaction_executor.validate(transaction.clone(), &mut state_block) { Ok(_) => success_count += 1, Err(_) => failure_count += 1, } @@ -169,12 +173,14 @@ fn sign_blocks(criterion: &mut Criterion) { let key_pair = KeyPair::random(); let kura = iroha_core::kura::Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(World::new(), kura, query_handle); + let state = State::new(World::new(), kura, query_handle); let topology = Topology::new(UniqueVec::new()); let mut count = 0; - let block = BlockBuilder::new(vec![transaction], topology, Vec::new()).chain(0, &mut wsv); + let mut state_block = state.block(false); + let block = + BlockBuilder::new(vec![transaction], topology, Vec::new()).chain(0, &mut state_block); let _ = criterion.bench_function("sign_block", |b| { b.iter_batched( diff --git a/core/src/block.rs b/core/src/block.rs index 7a044a6abdc..ec29754fb03 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -96,6 +96,7 @@ mod pending { use iroha_data_model::transaction::TransactionValue; use super::*; + use crate::state::StateBlock; /// First stage in the life-cycle of a [`Block`]. /// In the beginning the block is assumed to be verified and to contain only accepted transactions. @@ -161,27 +162,29 @@ mod pending { fn categorize_transactions( transactions: Vec, - wsv: &mut WorldStateView, + state_block: &mut StateBlock<'_>, ) -> Vec { transactions .into_iter() - .map(|tx| match wsv.transaction_executor().validate(tx, wsv) { - Ok(tx) => TransactionValue { - value: tx, - error: None, - }, - Err((tx, error)) => { - iroha_logger::warn!( - reason = %error, - caused_by = ?error.source(), - "Transaction validation failed", - ); - TransactionValue { + .map( + |tx| match state_block.transaction_executor().validate(tx, state_block) { + Ok(tx) => TransactionValue { value: tx, - error: Some(error), + error: None, + }, + Err((tx, error)) => { + iroha_logger::warn!( + reason = %error, + caused_by = ?error.source(), + "Transaction validation failed", + ); + TransactionValue { + value: tx, + error: Some(error), + } } - } - }) + }, + ) .collect() } @@ -191,14 +194,14 @@ mod pending { pub fn chain( self, view_change_index: u64, - wsv: &mut WorldStateView, + state: &mut StateBlock<'_>, ) -> BlockBuilder { - let transactions = Self::categorize_transactions(self.0.transactions, wsv); + let transactions = Self::categorize_transactions(self.0.transactions, state); BlockBuilder(Chained(BlockPayload { header: Self::make_header( - wsv.height(), - wsv.latest_block_hash(), + state.height(), + state.latest_block_hash(), view_change_index, &transactions, ), @@ -237,7 +240,7 @@ mod valid { use iroha_data_model::ChainId; use super::*; - use crate::sumeragi::network_topology::Role; + use crate::{state::StateBlock, sumeragi::network_topology::Role}; /// Block that was validated and accepted #[derive(Debug, Clone)] @@ -260,7 +263,7 @@ mod valid { block: SignedBlock, topology: &Topology, expected_chain_id: &ChainId, - wsv: &mut WorldStateView, + state_block: &mut StateBlock<'_>, ) -> Result { if !block.header().is_genesis() { let actual_commit_topology = block.commit_topology(); @@ -286,7 +289,7 @@ mod valid { } } - let expected_block_height = wsv.height() + 1; + let expected_block_height = state_block.height() + 1; let actual_height = block.header().height; if expected_block_height != actual_height { @@ -299,7 +302,7 @@ mod valid { )); } - let expected_previous_block_hash = wsv.latest_block_hash(); + let expected_previous_block_hash = state_block.latest_block_hash(); let actual_block_hash = block.header().previous_block_hash; if expected_previous_block_hash != actual_block_hash { @@ -314,12 +317,13 @@ mod valid { if block .transactions() - .any(|tx| wsv.has_transaction(tx.as_ref().hash())) + .any(|tx| state_block.has_transaction(tx.as_ref().hash())) { return Err((block, BlockValidationError::HasCommittedTransactions)); } - if let Err(error) = Self::validate_transactions(&block, expected_chain_id, wsv) { + if let Err(error) = Self::validate_transactions(&block, expected_chain_id, state_block) + { return Err((block, error.into())); } @@ -336,7 +340,7 @@ mod valid { fn validate_transactions( block: &SignedBlock, expected_chain_id: &ChainId, - wsv: &mut WorldStateView, + state_block: &mut StateBlock<'_>, ) -> Result<(), TransactionValidationError> { let is_genesis = block.header().is_genesis(); @@ -344,7 +348,7 @@ mod valid { // TODO: Unnecessary clone? .cloned() .try_for_each(|TransactionValue{value, error}| { - let transaction_executor = wsv.transaction_executor(); + let transaction_executor = state_block.transaction_executor(); let limits = &transaction_executor.transaction_limits; let tx = if is_genesis { @@ -354,12 +358,12 @@ mod valid { }?; if error.is_some() { - match transaction_executor.validate(tx, wsv) { + match transaction_executor.validate(tx, state_block) { Err(rejected_transaction) => Ok(rejected_transaction), Ok(_) => Err(TransactionValidationError::RejectedIsValid), }?; } else { - transaction_executor.validate(tx, wsv).map_err(|(_tx, error)| { + transaction_executor.validate(tx, state_block).map_err(|(_tx, error)| { TransactionValidationError::NotValid(error) })?; } @@ -677,7 +681,10 @@ mod tests { use iroha_data_model::prelude::*; use super::*; - use crate::{kura::Kura, query::store::LiveQueryStore, smartcontracts::isi::Registrable as _}; + use crate::{ + kura::Kura, query::store::LiveQueryStore, smartcontracts::isi::Registrable as _, + state::State, + }; #[test] pub fn committed_and_valid_block_hashes_are_equal() { @@ -706,7 +713,8 @@ mod tests { let world = World::with([domain], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world, kura, query_handle); + let state = State::new(world, kura, query_handle); + let mut state_block = state.block(false); // Creating an instruction let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); @@ -714,7 +722,7 @@ mod tests { Register::asset_definition(AssetDefinition::numeric(asset_definition_id)); // Making two transactions that have the same instruction - let transaction_limits = &wsv.transaction_executor().transaction_limits; + let transaction_limits = &state_block.transaction_executor().transaction_limits; let tx = TransactionBuilder::new(chain_id.clone(), alice_id) .with_instructions([create_asset_definition]) .sign(&alice_keys); @@ -724,7 +732,7 @@ mod tests { let transactions = vec![tx.clone(), tx]; let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) - .chain(0, &mut wsv) + .chain(0, &mut state_block) .sign(&alice_keys); // The first transaction should be confirmed @@ -749,7 +757,8 @@ mod tests { let world = World::with([domain], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world, kura, query_handle); + let state = State::new(world, kura, query_handle); + let mut state_block = state.block(false); // Creating an instruction let asset_definition_id = AssetDefinitionId::from_str("xor#wonderland").expect("Valid"); @@ -757,7 +766,7 @@ mod tests { Register::asset_definition(AssetDefinition::numeric(asset_definition_id.clone())); // Making two transactions that have the same instruction - let transaction_limits = &wsv.transaction_executor().transaction_limits; + let transaction_limits = &state_block.transaction_executor().transaction_limits; let tx = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([create_asset_definition]) .sign(&alice_keys); @@ -785,7 +794,7 @@ mod tests { let transactions = vec![tx0, tx, tx2]; let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) - .chain(0, &mut wsv) + .chain(0, &mut state_block) .sign(&alice_keys); // The first transaction should fail @@ -813,8 +822,9 @@ mod tests { let world = World::with([domain], UniqueVec::new()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world, kura, query_handle); - let transaction_limits = &wsv.transaction_executor().transaction_limits; + let state = State::new(world, kura, query_handle); + let mut state_block = state.block(false); + let transaction_limits = &state_block.transaction_executor().transaction_limits; let domain_id = DomainId::from_str("domain").expect("Valid"); let create_domain = Register::domain(Domain::new(domain_id)); @@ -841,7 +851,7 @@ mod tests { let transactions = vec![tx_fail, tx_accept]; let topology = Topology::new(UniqueVec::new()); let valid_block = BlockBuilder::new(transactions, topology, Vec::new()) - .chain(0, &mut wsv) + .chain(0, &mut state_block) .sign(&alice_keys); // The first transaction should be rejected diff --git a/core/src/block_sync.rs b/core/src/block_sync.rs index e4d71aad0a1..a88903fa812 100644 --- a/core/src/block_sync.rs +++ b/core/src/block_sync.rs @@ -10,7 +10,7 @@ use iroha_p2p::Post; use parity_scale_codec::{Decode, Encode}; use tokio::sync::mpsc; -use crate::{kura::Kura, sumeragi::SumeragiHandle, IrohaNetwork, NetworkMessage}; +use crate::{kura::Kura, state::State, sumeragi::SumeragiHandle, IrohaNetwork, NetworkMessage}; /// [`BlockSynchronizer`] actor handle. #[derive(Clone)] @@ -38,8 +38,7 @@ pub struct BlockSynchronizer { gossip_period: Duration, gossip_max_size: NonZeroU32, network: IrohaNetwork, - latest_hash: Option>, - previous_hash: Option>, + state: Arc, } impl BlockSynchronizer { @@ -56,13 +55,6 @@ impl BlockSynchronizer { loop { tokio::select! { _ = gossip_period.tick() => self.request_block().await, - () = self.sumeragi.wsv_updated() => { - let (latest_hash, previous_hash) = self - .sumeragi - .apply_wsv(|wsv| (wsv.latest_block_hash(), wsv.previous_block_hash())); - self.latest_hash = latest_hash; - self.previous_hash = previous_hash; - } msg = message_receiver.recv() => { let Some(msg) = msg else { info!("All handler to BlockSynchronizer are dropped. Shutting down..."); @@ -94,9 +86,16 @@ impl BlockSynchronizer { /// Sends request for latest blocks to a chosen peer async fn request_latest_blocks_from_peer(&mut self, peer_id: PeerId) { + let (previous_hash, latest_hash) = { + let state_view = self.state.view(); + ( + state_view.previous_block_hash(), + state_view.latest_block_hash(), + ) + }; message::Message::GetBlocksAfter(message::GetBlocksAfter::new( - self.latest_hash, - self.previous_hash, + latest_hash, + previous_hash, self.peer_id.clone(), )) .send_to(&self.network, peer_id) @@ -110,9 +109,8 @@ impl BlockSynchronizer { kura: Arc, peer_id: PeerId, network: IrohaNetwork, + state: Arc, ) -> Self { - let (latest_hash, previous_hash) = - sumeragi.apply_wsv(|wsv| (wsv.latest_block_hash(), wsv.previous_block_hash())); Self { peer_id, sumeragi, @@ -120,8 +118,7 @@ impl BlockSynchronizer { gossip_period: config.gossip_period, gossip_max_size: config.gossip_max_size, network, - latest_hash, - previous_hash, + state, } } } @@ -191,7 +188,8 @@ pub mod message { previous_hash, peer_id, }) => { - let local_latest_block_hash = block_sync.latest_hash; + let local_latest_block_hash = block_sync.state.view().latest_block_hash(); + if *latest_hash == local_latest_block_hash || *previous_hash == local_latest_block_hash { diff --git a/core/src/executor.rs b/core/src/executor.rs index dcc12a9fa42..38da0d0b0e1 100644 --- a/core/src/executor.rs +++ b/core/src/executor.rs @@ -17,7 +17,7 @@ use serde::{ use crate::{ smartcontracts::{wasm, Execute as _}, - wsv::{WasmSeed, WorldStateView}, + state::{deserialize::WasmSeed, StateSnapshot, StateTransaction}, }; impl From for ValidationFail { @@ -136,7 +136,7 @@ impl Executor { /// - Executor denied the operation. pub fn validate_transaction( &self, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, authority: &AccountId, transaction: SignedTransaction, ) -> Result<(), ValidationFail> { @@ -149,19 +149,19 @@ impl Executor { return Ok(()); }; for isi in instructions { - isi.execute(authority, wsv)? + isi.execute(authority, state_transaction)? } Ok(()) } Self::UserProvided(UserProvidedExecutor(loaded_executor)) => { let runtime = wasm::RuntimeBuilder::::new() - .with_engine(wsv.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs - .with_config(wsv.config.executor_runtime) + .with_engine(state_transaction.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs + .with_config(state_transaction.config.executor_runtime) .build()?; runtime.execute_executor_validate_transaction( - wsv, + state_transaction, authority, &loaded_executor.module, transaction, @@ -179,23 +179,25 @@ impl Executor { /// - Executor denied the operation. pub fn validate_instruction( &self, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, authority: &AccountId, instruction: InstructionBox, ) -> Result<(), ValidationFail> { trace!("Running instruction validation"); match self { - Self::Initial => instruction.execute(authority, wsv).map_err(Into::into), + Self::Initial => instruction + .execute(authority, state_transaction) + .map_err(Into::into), Self::UserProvided(UserProvidedExecutor(loaded_executor)) => { let runtime = wasm::RuntimeBuilder::::new() - .with_engine(wsv.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs - .with_config(wsv.config.executor_runtime) + .with_engine(state_transaction.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs + .with_config(state_transaction.config.executor_runtime) .build()?; runtime.execute_executor_validate_instruction( - wsv, + state_transaction, authority, &loaded_executor.module, instruction, @@ -213,7 +215,7 @@ impl Executor { /// - Executor denied the operation. pub fn validate_query( &self, - wsv: &WorldStateView, + state_snapshot: &StateSnapshot<'_>, authority: &AccountId, query: QueryBox, ) -> Result<(), ValidationFail> { @@ -223,12 +225,12 @@ impl Executor { Self::Initial => Ok(()), Self::UserProvided(UserProvidedExecutor(loaded_executor)) => { let runtime = wasm::RuntimeBuilder::::new() - .with_engine(wsv.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs - .with_config(wsv.config.executor_runtime) + .with_engine(state_snapshot.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs + .with_config(state_snapshot.config.executor_runtime) .build()?; runtime.execute_executor_validate_query( - wsv, + state_snapshot, authority, &loaded_executor.module, query, @@ -250,20 +252,20 @@ impl Executor { pub fn migrate( &mut self, raw_executor: data_model_executor::Executor, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, authority: &AccountId, ) -> Result<(), MigrationError> { trace!("Running executor migration"); - let loaded_executor = LoadedExecutor::load(&wsv.engine, raw_executor)?; + let loaded_executor = LoadedExecutor::load(state_transaction.engine, raw_executor)?; let runtime = wasm::RuntimeBuilder::::new() - .with_engine(wsv.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs - .with_config(wsv.config.executor_runtime) + .with_engine(state_transaction.engine.clone()) // Cloning engine is cheap, see [`wasmtime::Engine`] docs + .with_config(state_transaction.config.executor_runtime) .build()?; runtime - .execute_executor_migration(wsv, authority, &loaded_executor.module)? + .execute_executor_migration(state_transaction, authority, &loaded_executor.module)? .map_err(MigrationError::EntrypointExecution)?; *self = Self::UserProvided(UserProvidedExecutor(loaded_executor)); diff --git a/core/src/gossiper.rs b/core/src/gossiper.rs index fdb5bcc85f0..98745408035 100644 --- a/core/src/gossiper.rs +++ b/core/src/gossiper.rs @@ -8,10 +8,7 @@ use iroha_p2p::Broadcast; use parity_scale_codec::{Decode, Encode}; use tokio::sync::mpsc; -use crate::{ - queue::Queue, sumeragi::SumeragiHandle, tx::AcceptedTransaction, wsv::WorldStateView, - IrohaNetwork, NetworkMessage, -}; +use crate::{queue::Queue, state::State, tx::AcceptedTransaction, IrohaNetwork, NetworkMessage}; /// [`Gossiper`] actor handle. #[derive(Clone)] @@ -43,10 +40,8 @@ pub struct TransactionGossiper { queue: Arc, /// [`iroha_p2p::Network`] actor handle network: IrohaNetwork, - /// Sumearagi - sumeragi: SumeragiHandle, - /// Local clone of [`WorldStateView`] - wsv: WorldStateView, + /// [`WorldState`] + state: Arc, } impl TransactionGossiper { @@ -66,28 +61,23 @@ impl TransactionGossiper { }: Config, network: IrohaNetwork, queue: Arc, - sumeragi: SumeragiHandle, + state: Arc, ) -> Self { - let wsv = sumeragi.wsv_clone(); Self { chain_id, gossip_max_size, gossip_period, queue, network, - sumeragi, - wsv, + state, } } - async fn run(mut self, mut message_receiver: mpsc::Receiver) { + async fn run(self, mut message_receiver: mpsc::Receiver) { let mut gossip_period = tokio::time::interval(self.gossip_period); loop { tokio::select! { _ = gossip_period.tick() => self.gossip_transactions(), - () = self.sumeragi.wsv_updated() => { - self.wsv = self.sumeragi.wsv_clone(); - } transaction_gossip = message_receiver.recv() => { let Some(transaction_gossip) = transaction_gossip else { iroha_logger::info!("All handler to Gossiper are dropped. Shutting down..."); @@ -103,7 +93,7 @@ impl TransactionGossiper { fn gossip_transactions(&self) { let txs = self .queue - .n_random_transactions(self.gossip_max_size.get(), &self.wsv); + .n_random_transactions(self.gossip_max_size.get(), &self.state.view()); if txs.is_empty() { return; @@ -118,11 +108,12 @@ impl TransactionGossiper { fn handle_transaction_gossip(&self, TransactionGossip { txs }: TransactionGossip) { iroha_logger::trace!(size = txs.len(), "Received new transaction gossip"); + let state_view = self.state.view(); for tx in txs { - let transaction_limits = &self.wsv.config.transaction_limits; + let transaction_limits = &state_view.config.transaction_limits; match AcceptedTransaction::accept(tx, &self.chain_id, transaction_limits) { - Ok(tx) => match self.queue.push(tx, &self.wsv) { + Ok(tx) => match self.queue.push(tx, &state_view) { Ok(()) => {} Err(crate::queue::Failure { tx, diff --git a/core/src/lib.rs b/core/src/lib.rs index 50b47920da2..75051059e74 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -10,16 +10,15 @@ pub mod query; pub mod queue; pub mod smartcontracts; pub mod snapshot; +pub mod state; pub mod sumeragi; pub mod tx; -pub mod wsv; use core::time::Duration; -use std::collections::BTreeSet; use gossiper::TransactionGossip; -use indexmap::{IndexMap, IndexSet}; -use iroha_data_model::{permission::Permissions, prelude::*}; +use indexmap::IndexSet; +use iroha_data_model::prelude::*; use iroha_primitives::unique_vec::UniqueVec; use parity_scale_codec::{Decode, Encode}; use tokio::sync::broadcast; @@ -42,18 +41,6 @@ pub type PeersIds = UniqueVec; /// Parameters set. pub type Parameters = IndexSet; -/// API to work with collections of [`DomainId`] : [`Domain`] mappings. -pub type DomainsMap = IndexMap; - -/// API to work with a collections of [`RoleId`]: [`Role`] mappings. -pub type RolesMap = IndexMap; - -/// API to work with a collections of [`AccountId`] [`Permissions`] mappings. -pub type PermissionTokensMap = IndexMap; - -/// API to work with a collections of [`AccountId`] to [`RoleId`] mappings. -pub type AccountRolesSet = BTreeSet; - /// Type of `Sender` which should be used for channels of `Event` messages. pub type EventsSender = broadcast::Sender; @@ -110,7 +97,7 @@ pub mod handler { } pub mod role { - //! Module with extension for [`RoleId`] to be stored inside wsv. + //! Module with extension for [`RoleId`] to be stored inside state. use derive_more::Constructor; use iroha_primitives::impl_as_dyn_key; @@ -174,8 +161,8 @@ pub mod prelude { #[doc(inline)] pub use crate::{ smartcontracts::ValidQuery, + state::{StateView, World}, tx::AcceptedTransaction, - wsv::{World, WorldStateView}, }; } diff --git a/core/src/queue.rs b/core/src/queue.rs index 195dd81ddcc..474ee0a41ed 100644 --- a/core/src/queue.rs +++ b/core/src/queue.rs @@ -18,7 +18,7 @@ use crate::prelude::*; impl AcceptedTransaction { // TODO: We should have another type of transaction like `CheckedTransaction` in the type system? - fn check_signature_condition(&self, wsv: &WorldStateView) -> MustUse { + fn check_signature_condition(&self, state_view: &StateView<'_>) -> MustUse { let authority = self.as_ref().authority(); let transaction_signatories = self @@ -29,15 +29,17 @@ impl AcceptedTransaction { .cloned() .collect(); - wsv.map_account(authority, |account| { - account.check_signature_check_condition(&transaction_signatories) - }) - .unwrap_or(MustUse(false)) + state_view + .world + .map_account(authority, |account| { + account.check_signature_check_condition(&transaction_signatories) + }) + .unwrap_or(MustUse(false)) } /// Check if [`self`] is committed or rejected. - fn is_in_blockchain(&self, wsv: &WorldStateView) -> bool { - wsv.has_transaction(self.as_ref().hash()) + fn is_in_blockchain(&self, state_view: &StateView<'_>) -> bool { + state_view.has_transaction(self.as_ref().hash()) } } @@ -106,8 +108,8 @@ impl Queue { } } - fn is_pending(&self, tx: &AcceptedTransaction, wsv: &WorldStateView) -> bool { - !self.is_expired(tx) && !tx.is_in_blockchain(wsv) + fn is_pending(&self, tx: &AcceptedTransaction, state_view: &StateView) -> bool { + !self.is_expired(tx) && !tx.is_in_blockchain(state_view) } /// Checks if the transaction is waiting longer than its TTL or than the TTL from [`Config`]. @@ -129,12 +131,12 @@ impl Queue { } /// Returns all pending transactions. - pub fn all_transactions<'wsv>( - &'wsv self, - wsv: &'wsv WorldStateView, - ) -> impl Iterator + 'wsv { + pub fn all_transactions<'state>( + &'state self, + state_view: &'state StateView, + ) -> impl Iterator + 'state { self.accepted_txs.iter().filter_map(|tx| { - if self.is_pending(tx.value(), wsv) { + if self.is_pending(tx.value(), state_view) { return Some(tx.value().clone()); } @@ -143,10 +145,14 @@ impl Queue { } /// Returns `n` randomly selected transaction from the queue. - pub fn n_random_transactions(&self, n: u32, wsv: &WorldStateView) -> Vec { + pub fn n_random_transactions( + &self, + n: u32, + state_view: &StateView, + ) -> Vec { self.accepted_txs .iter() - .filter(|e| self.is_pending(e.value(), wsv)) + .filter(|e| self.is_pending(e.value(), state_view)) .map(|e| e.value().clone()) .choose_multiple( &mut rand::thread_rng(), @@ -154,14 +160,14 @@ impl Queue { ) } - fn check_tx(&self, tx: &AcceptedTransaction, wsv: &WorldStateView) -> Result<(), Error> { + fn check_tx(&self, tx: &AcceptedTransaction, state_view: &StateView) -> Result<(), Error> { if self.is_in_future(tx) { Err(Error::InFuture) } else if self.is_expired(tx) { Err(Error::Expired) - } else if tx.is_in_blockchain(wsv) { + } else if tx.is_in_blockchain(state_view) { Err(Error::InBlockchain) - } else if !tx.check_signature_condition(wsv).into_inner() { + } else if !tx.check_signature_condition(state_view).into_inner() { Err(Error::SignatureCondition) } else { Ok(()) @@ -172,9 +178,9 @@ impl Queue { /// /// # Errors /// See [`enum@Error`] - pub fn push(&self, tx: AcceptedTransaction, wsv: &WorldStateView) -> Result<(), Failure> { + pub fn push(&self, tx: AcceptedTransaction, state_view: &StateView) -> Result<(), Failure> { trace!(?tx, "Pushing to the queue"); - if let Err(err) = self.check_tx(&tx, wsv) { + if let Err(err) = self.check_tx(&tx, state_view) { return Err(Failure { tx, err }); } @@ -228,7 +234,7 @@ impl Queue { fn pop_from_queue( &self, seen: &mut Vec>, - wsv: &WorldStateView, + state_view: &StateView, expired_transactions: &mut Vec, ) -> Option { loop { @@ -246,7 +252,7 @@ impl Queue { }; let tx = entry.get(); - if let Err(e) = self.check_tx(tx, wsv) { + if let Err(e) = self.check_tx(tx, state_view) { let (_, tx) = entry.remove_entry(); self.decrease_per_user_tx_count(tx.as_ref().authority()); if let Error::Expired = e { @@ -271,11 +277,16 @@ impl Queue { #[cfg(test)] fn collect_transactions_for_block( &self, - wsv: &WorldStateView, + state_view: &StateView, max_txs_in_block: usize, ) -> Vec { let mut transactions = Vec::with_capacity(max_txs_in_block); - self.get_transactions_for_block(wsv, max_txs_in_block, &mut transactions, &mut Vec::new()); + self.get_transactions_for_block( + state_view, + max_txs_in_block, + &mut transactions, + &mut Vec::new(), + ); transactions } @@ -284,7 +295,7 @@ impl Queue { /// BEWARE: Shouldn't be called in parallel with itself. pub fn get_transactions_for_block( &self, - wsv: &WorldStateView, + state_view: &StateView, max_txs_in_block: usize, transactions: &mut Vec, expired_transactions: &mut Vec, @@ -297,7 +308,7 @@ impl Queue { let mut expired_transactions_queue = Vec::new(); let txs_from_queue = core::iter::from_fn(|| { - self.pop_from_queue(&mut seen_queue, wsv, &mut expired_transactions_queue) + self.pop_from_queue(&mut seen_queue, state_view, &mut expired_transactions_queue) }); let transactions_hashes: IndexSet> = @@ -363,8 +374,11 @@ pub mod tests { use super::*; use crate::{ - kura::Kura, query::store::LiveQueryStore, smartcontracts::isi::Registrable as _, - wsv::World, PeersIds, + kura::Kura, + query::store::LiveQueryStore, + smartcontracts::isi::Registrable as _, + state::{State, World}, + PeersIds, }; fn accepted_tx(account_id: &str, key: &KeyPair) -> AcceptedTransaction { @@ -416,16 +430,17 @@ pub mod tests { let key_pair = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = Arc::new(WorldStateView::new( + let state = Arc::new(State::new( world_with_test_domains([key_pair.public_key().clone()]), kura, query_handle, )); + let state_view = state.view(); let queue = Queue::from_config(config_factory()); queue - .push(accepted_tx("alice@wonderland", &key_pair), &wsv) + .push(accepted_tx("alice@wonderland", &key_pair), &state_view) .expect("Failed to push tx into queue"); } @@ -436,11 +451,12 @@ pub mod tests { let key_pair = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = Arc::new(WorldStateView::new( + let state = Arc::new(State::new( world_with_test_domains([key_pair.public_key().clone()]), kura, query_handle, )); + let state_view = state.view(); let queue = Queue::from_config(Config { transaction_time_to_live: Duration::from_secs(100), @@ -450,13 +466,13 @@ pub mod tests { for _ in 0..capacity.get() { queue - .push(accepted_tx("alice@wonderland", &key_pair), &wsv) + .push(accepted_tx("alice@wonderland", &key_pair), &state_view) .expect("Failed to push tx into queue"); thread::sleep(Duration::from_millis(10)); } assert!(matches!( - queue.push(accepted_tx("alice@wonderland", &key_pair), &wsv), + queue.push(accepted_tx("alice@wonderland", &key_pair), &state_view), Err(Failure { err: Error::Full, .. @@ -470,7 +486,7 @@ pub mod tests { let key_pairs = [KeyPair::random(), KeyPair::random()]; let kura = Kura::blank_kura_for_testing(); - let wsv = { + let state = { let domain_id = DomainId::from_str("wonderland").expect("Valid"); let account_id = AccountId::from_str("alice@wonderland").expect("Valid"); let mut domain = Domain::new(domain_id).build(&account_id); @@ -480,12 +496,13 @@ pub mod tests { account.signature_check_condition = SignatureCheckCondition::all_account_signatures(); assert!(domain.add_account(account).is_none()); let query_handle = LiveQueryStore::test().start(); - Arc::new(WorldStateView::new( + Arc::new(State::new( World::with([domain], PeersIds::new()), kura, query_handle, )) }; + let state_view = state.view(); let queue = Queue::from_config(config_factory()); let instructions: [InstructionBox; 0] = []; @@ -506,7 +523,7 @@ pub mod tests { }; // Check that fully signed transaction passes signature check assert!(matches!( - fully_signed_tx.check_signature_condition(&wsv), + fully_signed_tx.check_signature_condition(&state_view), MustUse(true) )); @@ -518,11 +535,14 @@ pub mod tests { let partially_signed_tx: AcceptedTransaction = get_tx(key_pair); // Check that none of partially signed txs passes signature check assert_eq!( - partially_signed_tx.check_signature_condition(&wsv), + partially_signed_tx.check_signature_condition(&state_view), MustUse(false) ); assert!(matches!( - queue.push(partially_signed_tx, &wsv).unwrap_err().err, + queue + .push(partially_signed_tx, &state_view) + .unwrap_err() + .err, Error::SignatureCondition )) } @@ -534,23 +554,24 @@ pub mod tests { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = Arc::new(WorldStateView::new( + let state = Arc::new(State::new( world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, )); + let state_view = state.view(); let queue = Queue::from_config(Config { transaction_time_to_live: Duration::from_secs(100), ..config_factory() }); for _ in 0..5 { queue - .push(accepted_tx("alice@wonderland", &alice_key), &wsv) + .push(accepted_tx("alice@wonderland", &alice_key), &state_view) .expect("Failed to push tx into queue"); thread::sleep(Duration::from_millis(10)); } - let available = queue.collect_transactions_for_block(&wsv, max_txs_in_block); + let available = queue.collect_transactions_for_block(&state_view, max_txs_in_block); assert_eq!(available.len(), max_txs_in_block); } @@ -559,16 +580,19 @@ pub mod tests { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new( + let state = State::new( world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, ); let tx = accepted_tx("alice@wonderland", &alice_key); - wsv.transactions.insert(tx.as_ref().hash(), 1); + let mut state_block = state.block(false); + state_block.transactions.insert(tx.as_ref().hash(), 1); + state_block.commit(); + let state_view = state.view(); let queue = Queue::from_config(config_factory()); assert!(matches!( - queue.push(tx, &wsv), + queue.push(tx, &state_view), Err(Failure { err: Error::InBlockchain, .. @@ -583,18 +607,20 @@ pub mod tests { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new( + let state = State::new( world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, ); let tx = accepted_tx("alice@wonderland", &alice_key); let queue = Queue::from_config(config_factory()); - queue.push(tx.clone(), &wsv).unwrap(); - wsv.transactions.insert(tx.as_ref().hash(), 1); + queue.push(tx.clone(), &state.view()).unwrap(); + let mut state_block = state.block(false); + state_block.transactions.insert(tx.as_ref().hash(), 1); + state_block.commit(); assert_eq!( queue - .collect_transactions_for_block(&wsv, max_txs_in_block) + .collect_transactions_for_block(&state.view(), max_txs_in_block) .len(), 0 ); @@ -607,40 +633,41 @@ pub mod tests { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = Arc::new(WorldStateView::new( + let state = Arc::new(State::new( world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, )); + let state_view = state.view(); let queue = Queue::from_config(Config { transaction_time_to_live: Duration::from_millis(300), ..config_factory() }); for _ in 0..(max_txs_in_block - 1) { queue - .push(accepted_tx("alice@wonderland", &alice_key), &wsv) + .push(accepted_tx("alice@wonderland", &alice_key), &state_view) .expect("Failed to push tx into queue"); thread::sleep(Duration::from_millis(100)); } queue - .push(accepted_tx("alice@wonderland", &alice_key), &wsv) + .push(accepted_tx("alice@wonderland", &alice_key), &state_view) .expect("Failed to push tx into queue"); std::thread::sleep(Duration::from_millis(201)); assert_eq!( queue - .collect_transactions_for_block(&wsv, max_txs_in_block) + .collect_transactions_for_block(&state_view, max_txs_in_block) .len(), 1 ); queue - .push(accepted_tx("alice@wonderland", &alice_key), &wsv) + .push(accepted_tx("alice@wonderland", &alice_key), &state_view) .expect("Failed to push tx into queue"); std::thread::sleep(Duration::from_millis(310)); assert_eq!( queue - .collect_transactions_for_block(&wsv, max_txs_in_block) + .collect_transactions_for_block(&state_view, max_txs_in_block) .len(), 0 ); @@ -654,23 +681,24 @@ pub mod tests { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = Arc::new(WorldStateView::new( + let state = Arc::new(State::new( world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, )); + let state_view = state.view(); let queue = Queue::from_config(config_factory()); queue - .push(accepted_tx("alice@wonderland", &alice_key), &wsv) + .push(accepted_tx("alice@wonderland", &alice_key), &state_view) .expect("Failed to push tx into queue"); let a = queue - .collect_transactions_for_block(&wsv, max_txs_in_block) + .collect_transactions_for_block(&state_view, max_txs_in_block) .into_iter() .map(|tx| tx.as_ref().hash()) .collect::>(); let b = queue - .collect_transactions_for_block(&wsv, max_txs_in_block) + .collect_transactions_for_block(&state_view, max_txs_in_block) .into_iter() .map(|tx| tx.as_ref().hash()) .collect::>(); @@ -688,11 +716,12 @@ pub mod tests { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = Arc::new(WorldStateView::new( + let state = Arc::new(State::new( world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, )); + let state_view = state.view(); let queue = Queue::from_config(config_factory()); let instructions = [Fail { message: "expired".to_owned(), @@ -711,12 +740,12 @@ pub mod tests { let tx = AcceptedTransaction::accept(tx, &chain_id, &limits) .expect("Failed to accept Transaction."); queue - .push(tx.clone(), &wsv) + .push(tx.clone(), &state_view) .expect("Failed to push tx into queue"); let mut txs = Vec::new(); let mut expired_txs = Vec::new(); thread::sleep(Duration::from_millis(TTL_MS)); - queue.get_transactions_for_block(&wsv, max_txs_in_block, &mut txs, &mut expired_txs); + queue.get_transactions_for_block(&state_view, max_txs_in_block, &mut txs, &mut expired_txs); assert!(txs.is_empty()); assert_eq!(expired_txs.len(), 1); assert_eq!(expired_txs[0], tx); @@ -728,11 +757,11 @@ pub mod tests { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = WorldStateView::new( + let state = Arc::new(State::new( world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, - ); + )); let queue = Arc::new(Queue::from_config(Config { transaction_time_to_live: Duration::from_secs(100), @@ -745,13 +774,13 @@ pub mod tests { let push_txs_handle = { let queue_arc_clone = Arc::clone(&queue); - let wsv_clone = wsv.clone(); + let state = state.clone(); // Spawn a thread where we push transactions thread::spawn(move || { while start_time.elapsed() < run_for { let tx = accepted_tx("alice@wonderland", &alice_key); - match queue_arc_clone.push(tx, &wsv_clone) { + match queue_arc_clone.push(tx, &state.view()) { Ok(()) | Err(Failure { err: Error::Full | Error::MaximumTransactionsPerUser, @@ -763,17 +792,17 @@ pub mod tests { }) }; - // Spawn a thread where we get_transactions_for_block and add them to WSV + // Spawn a thread where we get_transactions_for_block and add them to state let get_txs_handle = { - let queue_arc_clone = Arc::clone(&queue); - let mut wsv_clone = wsv; + let queue = Arc::clone(&queue); thread::spawn(move || { while start_time.elapsed() < run_for { - for tx in - queue_arc_clone.collect_transactions_for_block(&wsv_clone, max_txs_in_block) + for tx in queue.collect_transactions_for_block(&state.view(), max_txs_in_block) { - wsv_clone.transactions.insert(tx.as_ref().hash(), 1); + let mut state_block = state.block(false); + state_block.transactions.insert(tx.as_ref().hash(), 1); + state_block.commit(); } // Simulate random small delays thread::sleep(Duration::from_millis(rand::thread_rng().gen_range(0..25))); @@ -801,11 +830,12 @@ pub mod tests { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = Arc::new(WorldStateView::new( + let state = Arc::new(State::new( world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, )); + let state_view = state.view(); let queue = Queue::from_config(Config { future_threshold, @@ -813,7 +843,7 @@ pub mod tests { }); let tx = accepted_tx(alice_id, &alice_key); - assert!(queue.push(tx.clone(), &wsv).is_ok()); + assert!(queue.push(tx.clone(), &state_view).is_ok()); // create the same tx but with timestamp in the future let tx = { let chain_id = ChainId::from("0"); @@ -834,7 +864,7 @@ pub mod tests { .expect("Failed to accept Transaction.") }; assert!(matches!( - queue.push(tx, &wsv), + queue.push(tx, &state_view), Err(Failure { err: Error::InFuture, .. @@ -866,7 +896,7 @@ pub mod tests { World::with([domain], PeersIds::new()) }; let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world, kura, query_handle); + let state = State::new(world, kura, query_handle); let queue = Queue::from_config(Config { transaction_time_to_live: Duration::from_secs(100), @@ -877,11 +907,17 @@ pub mod tests { // First push by Alice should be fine queue - .push(accepted_tx("alice@wonderland", &alice_key_pair), &wsv) + .push( + accepted_tx("alice@wonderland", &alice_key_pair), + &state.view(), + ) .expect("Failed to push tx into queue"); // Second push by Alice excide limit and will be rejected - let result = queue.push(accepted_tx("alice@wonderland", &alice_key_pair), &wsv); + let result = queue.push( + accepted_tx("alice@wonderland", &alice_key_pair), + &state.view(), + ); assert!( matches!( result, @@ -895,26 +931,33 @@ pub mod tests { // First push by Bob should be fine despite previous Alice error queue - .push(accepted_tx("bob@wonderland", &bob_key_pair), &wsv) + .push(accepted_tx("bob@wonderland", &bob_key_pair), &state.view()) .expect("Failed to push tx into queue"); - let transactions = queue.collect_transactions_for_block(&wsv, 10); + let transactions = queue.collect_transactions_for_block(&state.view(), 10); assert_eq!(transactions.len(), 2); + let mut state_block = state.block(false); for transaction in transactions { - // Put transaction hashes into wsv as if they were in the blockchain - wsv.transactions.insert(transaction.as_ref().hash(), 1); + // Put transaction hashes into state as if they were in the blockchain + state_block + .transactions + .insert(transaction.as_ref().hash(), 1); } + state_block.commit(); // Cleanup transactions - let transactions = queue.collect_transactions_for_block(&wsv, 10); + let transactions = queue.collect_transactions_for_block(&state.view(), 10); assert!(transactions.is_empty()); // After cleanup Alice and Bob pushes should work fine queue - .push(accepted_tx("alice@wonderland", &alice_key_pair), &wsv) + .push( + accepted_tx("alice@wonderland", &alice_key_pair), + &state.view(), + ) .expect("Failed to push tx into queue"); queue - .push(accepted_tx("bob@wonderland", &bob_key_pair), &wsv) + .push(accepted_tx("bob@wonderland", &bob_key_pair), &state.view()) .expect("Failed to push tx into queue"); } } diff --git a/core/src/smartcontracts/isi/account.rs b/core/src/smartcontracts/isi/account.rs index 5060488a4d1..1e9124f9255 100644 --- a/core/src/smartcontracts/isi/account.rs +++ b/core/src/smartcontracts/isi/account.rs @@ -1,11 +1,11 @@ //! This module contains implementations of smart-contract traits and instructions for [`Account`] structure -//! and implementations of [`Query`]'s to [`WorldStateView`] about [`Account`]. +//! and implementations of [`Query`]'s about [`Account`]. use iroha_data_model::{prelude::*, query::error::FindError}; use iroha_telemetry::metrics; use super::prelude::*; -use crate::{ValidQuery, WorldStateView}; +use crate::ValidQuery; impl Registrable for iroha_data_model::account::NewAccount { type Target = Account; @@ -36,30 +36,39 @@ pub mod isi { use self::asset::isi::assert_numeric_spec; use super::*; - use crate::role::{AsRoleIdWithOwnerRef, RoleIdWithOwner, RoleIdWithOwnerRef}; + use crate::{role::RoleIdWithOwner, state::StateTransaction}; impl Execute for Register { #[metrics(+"register_asset")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_id = self.object.id; - match wsv.asset(&asset_id) { + match state_transaction.world.asset(&asset_id) { Err(err) => match err { QueryExecutionFail::Find(FindError::Asset(_)) => { - assert_can_register(&asset_id.definition_id, wsv, &self.object.value)?; - let asset = wsv + assert_can_register( + &asset_id.definition_id, + state_transaction, + &self.object.value, + )?; + let asset = state_transaction + .world .asset_or_insert(asset_id.clone(), self.object.value) .expect("Account exists"); match asset.value { AssetValue::Numeric(increment) => { - wsv.increase_asset_total_amount( + state_transaction.world.increase_asset_total_amount( &asset_id.definition_id, increment, )?; } AssetValue::Store(_) => { - wsv.increase_asset_total_amount( + state_transaction.world.increase_asset_total_amount( &asset_id.definition_id, Numeric::ONE, )?; @@ -80,31 +89,44 @@ pub mod isi { impl Execute for Unregister { #[metrics(+"unregister_asset")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_id = self.object_id; let account_id = asset_id.account_id.clone(); - let asset = wsv.account_mut(&account_id).and_then(|account| { - account - .remove_asset(&asset_id) - .ok_or_else(|| FindError::Asset(asset_id)) - })?; + let asset = state_transaction + .world + .account_mut(&account_id) + .and_then(|account| { + account + .remove_asset(&asset_id) + .ok_or_else(|| FindError::Asset(asset_id)) + })?; match asset.value { AssetValue::Numeric(increment) => { - wsv.decrease_asset_total_amount(&asset.id.definition_id, increment)?; + state_transaction + .world + .decrease_asset_total_amount(&asset.id.definition_id, increment)?; } AssetValue::Store(_) => { - wsv.decrease_asset_total_amount(&asset.id.definition_id, Numeric::ONE)?; + state_transaction + .world + .decrease_asset_total_amount(&asset.id.definition_id, Numeric::ONE)?; } } - wsv.emit_events(Some(AccountEvent::Asset(AssetEvent::Removed( - AssetChanged { - asset_id: asset.id, - amount: asset.value, - }, - )))); + state_transaction + .world + .emit_events(Some(AccountEvent::Asset(AssetEvent::Removed( + AssetChanged { + asset_id: asset.id, + amount: asset.value, + }, + )))); Ok(()) } @@ -112,11 +134,17 @@ pub mod isi { impl Execute for Mint { #[metrics(+"mint_account_public_key")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.destination_id; let public_key = self.object; - wsv.account_mut(&account_id) + state_transaction + .world + .account_mut(&account_id) .map_err(Error::from) .and_then(|account| { if account.contains_signatory(&public_key) { @@ -131,7 +159,9 @@ pub mod isi { Ok(()) })?; - wsv.emit_events(Some(AccountEvent::AuthenticationAdded(account_id.clone()))); + state_transaction + .world + .emit_events(Some(AccountEvent::AuthenticationAdded(account_id.clone()))); Ok(()) } @@ -139,11 +169,15 @@ pub mod isi { impl Execute for Burn { #[metrics(+"burn_account_public_key")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.destination_id; let public_key = self.object; - wsv.account_mut(&account_id) + state_transaction.world.account_mut(&account_id) .map_err(Error::from) .and_then(|account| { match account.remove_signatory(&public_key) { @@ -156,7 +190,9 @@ pub mod isi { } })?; - wsv.emit_events(Some(AccountEvent::AuthenticationRemoved(account_id))); + state_transaction + .world + .emit_events(Some(AccountEvent::AuthenticationRemoved(account_id))); Ok(()) } @@ -164,42 +200,57 @@ pub mod isi { impl Execute for Mint { #[metrics(+"mint_account_signature_check_condition")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.destination_id; let signature_check_condition = self.object; - wsv.account_mut(&account_id)?.signature_check_condition = signature_check_condition; + state_transaction + .world + .account_mut(&account_id)? + .signature_check_condition = signature_check_condition; - wsv.emit_events(Some(AccountEvent::AuthenticationAdded(account_id.clone()))); + state_transaction + .world + .emit_events(Some(AccountEvent::AuthenticationAdded(account_id.clone()))); Ok(()) } } impl Execute for Transfer { - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let Transfer { source_id, object, destination_id, } = self; - let _ = wsv.account(&source_id)?; - let _ = wsv.account(&destination_id)?; + let _ = state_transaction.world.account(&source_id)?; + let _ = state_transaction.world.account(&destination_id)?; - let asset_definition = wsv.asset_definition_mut(&object)?; + let asset_definition = state_transaction.world.asset_definition_mut(&object)?; if asset_definition.owned_by != source_id { return Err(Error::Find(FindError::Account(source_id))); } asset_definition.owned_by = destination_id.clone(); - wsv.emit_events(Some(AssetDefinitionEvent::OwnerChanged( - AssetDefinitionOwnerChanged { - asset_definition_id: object, - new_owner: destination_id, - }, - ))); + state_transaction + .world + .emit_events(Some(AssetDefinitionEvent::OwnerChanged( + AssetDefinitionOwnerChanged { + asset_definition_id: object, + new_owner: destination_id, + }, + ))); Ok(()) } @@ -207,12 +258,18 @@ pub mod isi { impl Execute for SetKeyValue { #[metrics(+"set_account_key_value")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.object_id; - let account_metadata_limits = wsv.config.account_metadata_limits; + let account_metadata_limits = state_transaction.config.account_metadata_limits; - wsv.account_mut(&account_id) + state_transaction + .world + .account_mut(&account_id) .map_err(Error::from) .and_then(|account| { account @@ -225,11 +282,13 @@ pub mod isi { .map_err(Error::from) })?; - wsv.emit_events(Some(AccountEvent::MetadataInserted(MetadataChanged { - target_id: account_id.clone(), - key: self.key.clone(), - value: self.value, - }))); + state_transaction + .world + .emit_events(Some(AccountEvent::MetadataInserted(MetadataChanged { + target_id: account_id.clone(), + key: self.key.clone(), + value: self.value, + }))); Ok(()) } @@ -237,21 +296,30 @@ pub mod isi { impl Execute for RemoveKeyValue { #[metrics(+"remove_account_key_value")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.object_id; - let value = wsv.account_mut(&account_id).and_then(|account| { - account - .metadata - .remove(&self.key) - .ok_or_else(|| FindError::MetadataKey(self.key.clone())) - })?; + let value = state_transaction + .world + .account_mut(&account_id) + .and_then(|account| { + account + .metadata + .remove(&self.key) + .ok_or_else(|| FindError::MetadataKey(self.key.clone())) + })?; - wsv.emit_events(Some(AccountEvent::MetadataRemoved(MetadataChanged { - target_id: account_id.clone(), - key: self.key, - value, - }))); + state_transaction + .world + .emit_events(Some(AccountEvent::MetadataRemoved(MetadataChanged { + target_id: account_id.clone(), + key: self.key, + value, + }))); Ok(()) } @@ -259,23 +327,31 @@ pub mod isi { impl Execute for Grant { #[metrics(+"grant_account_permission")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.destination_id; let permission = self.object; let permission_id = permission.definition_id.clone(); // Check if account exists - wsv.account_mut(&account_id)?; + state_transaction.world.account_mut(&account_id)?; - if !wsv - .permission_token_schema() + if !state_transaction + .world + .permission_token_schema .token_ids .contains(&permission_id) { return Err(FindError::PermissionToken(permission_id).into()); } - if wsv.account_contains_inherent_permission(&account_id, &permission) { + if state_transaction + .world + .account_contains_inherent_permission(&account_id, &permission) + { return Err(RepetitionError { instruction_type: InstructionType::Grant, id: permission.definition_id.into(), @@ -283,14 +359,18 @@ pub mod isi { .into()); } - wsv.add_account_permission(&account_id, permission); + state_transaction + .world + .add_account_permission(&account_id, permission); - wsv.emit_events(Some(AccountEvent::PermissionAdded( - AccountPermissionChanged { - account_id, - permission_id, - }, - ))); + state_transaction + .world + .emit_events(Some(AccountEvent::PermissionAdded( + AccountPermissionChanged { + account_id, + permission_id, + }, + ))); Ok(()) } @@ -298,23 +378,32 @@ pub mod isi { impl Execute for Revoke { #[metrics(+"revoke_account_permission")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.destination_id; let permission = self.object; // Check if account exists - wsv.account(&account_id)?; + state_transaction.world.account(&account_id)?; - if !wsv.remove_account_permission(&account_id, &permission) { + if !state_transaction + .world + .remove_account_permission(&account_id, &permission) + { return Err(FindError::PermissionToken(permission.definition_id).into()); } - wsv.emit_events(Some(AccountEvent::PermissionRemoved( - AccountPermissionChanged { - account_id, - permission_id: permission.definition_id, - }, - ))); + state_transaction + .world + .emit_events(Some(AccountEvent::PermissionRemoved( + AccountPermissionChanged { + account_id, + permission_id: permission.definition_id, + }, + ))); Ok(()) } @@ -322,12 +411,16 @@ pub mod isi { impl Execute for Grant { #[metrics(+"grant_account_role")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.destination_id; let role_id = self.object; - let permissions = wsv - .world() + let permissions = state_transaction + .world .roles .get(&role_id) .ok_or_else(|| FindError::Role(role_id.clone()))? @@ -336,12 +429,16 @@ pub mod isi { .into_iter() .map(|token| token.definition_id); - wsv.account(&account_id)?; + state_transaction.world.account(&account_id)?; - if !wsv + if state_transaction .world .account_roles - .insert(RoleIdWithOwner::new(account_id.clone(), role_id.clone())) + .insert( + RoleIdWithOwner::new(account_id.clone(), role_id.clone()), + (), + ) + .is_some() { return Err(RepetitionError { instruction_type: InstructionType::Grant, @@ -350,7 +447,7 @@ pub mod isi { .into()); } - wsv.emit_events({ + state_transaction.world.emit_events({ let account_id_clone = account_id.clone(); permissions .zip(core::iter::repeat_with(move || account_id.clone())) @@ -373,12 +470,16 @@ pub mod isi { impl Execute for Revoke { #[metrics(+"revoke_account_role")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.destination_id; let role_id = self.object; - let permissions = wsv - .world() + let permissions = state_transaction + .world .roles .get(&role_id) .ok_or_else(|| FindError::Role(role_id.clone()))? @@ -387,15 +488,19 @@ pub mod isi { .into_iter() .map(|token| token.definition_id); - if !wsv + if state_transaction .world .account_roles - .remove::(&RoleIdWithOwnerRef::new(&account_id, &role_id)) + .remove(RoleIdWithOwner { + account_id: account_id.clone(), + role_id: role_id.clone(), + }) + .is_none() { return Err(FindError::Role(role_id).into()); } - wsv.emit_events({ + state_transaction.world.emit_events({ let account_id_clone = account_id.clone(); permissions .zip(core::iter::repeat_with(move || account_id.clone())) @@ -419,15 +524,18 @@ pub mod isi { /// Assert that this asset can be registered to an account. fn assert_can_register( definition_id: &AssetDefinitionId, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, value: &AssetValue, ) -> Result<(), Error> { let expected_asset_value_type = match value.value_type() { AssetValueType::Numeric(_) => asset::isi::expected_asset_value_type_numeric, AssetValueType::Store => asset::isi::expected_asset_value_type_store, }; - let definition = - asset::isi::assert_asset_type(definition_id, wsv, expected_asset_value_type)?; + let definition = asset::isi::assert_asset_type( + definition_id, + state_transaction, + expected_asset_value_type, + )?; if let AssetValue::Numeric(numeric) = value { assert_numeric_spec(numeric, &definition)?; } @@ -437,11 +545,13 @@ pub mod isi { Mintable::Not => Err(Error::Mintability(MintabilityError::MintUnmintable)), Mintable::Once => { if !value.is_zero_value() { - let asset_definition = wsv.asset_definition_mut(definition_id)?; + let asset_definition = state_transaction + .world + .asset_definition_mut(definition_id)?; forbid_minting(asset_definition)?; - wsv.emit_events(Some(AssetDefinitionEvent::MintabilityChanged( - definition_id.clone(), - ))); + state_transaction.world.emit_events(Some( + AssetDefinitionEvent::MintabilityChanged(definition_id.clone()), + )); } Ok(()) } @@ -488,43 +598,48 @@ pub mod query { }; use super::*; + use crate::state::StateSnapshot; impl ValidQuery for FindRolesByAccountId { #[metrics(+"find_roles_by_account_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let account_id = &self.id; - iroha_logger::trace!(%account_id, roles=?wsv.world.roles); - wsv.account(account_id)?; - Ok(Box::new(wsv.account_roles(account_id).cloned())) + state_snapshot.world.account(account_id)?; + Ok(Box::new( + state_snapshot.world.account_roles(account_id).cloned(), + )) } } impl ValidQuery for FindPermissionTokensByAccountId { #[metrics(+"find_permission_tokens_by_account_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let account_id = &self.id; - iroha_logger::trace!(%account_id, accounts=?wsv.world.domains); Ok(Box::new( - wsv.account_permission_tokens(account_id)?.cloned(), + state_snapshot + .world + .account_permission_tokens(account_id)? + .cloned(), )) } } impl ValidQuery for FindAllAccounts { #[metrics(+"find_all_accounts")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { Ok(Box::new( - wsv.domains() - .values() + state_snapshot + .world + .domains() .flat_map(|domain| domain.accounts.values()) .cloned(), )) @@ -533,24 +648,28 @@ pub mod query { impl ValidQuery for FindAccountById { #[metrics(+"find_account_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; iroha_logger::trace!(%id); - wsv.map_account(id, Clone::clone).map_err(Into::into) + state_snapshot + .world + .map_account(id, Clone::clone) + .map_err(Into::into) } } impl ValidQuery for FindAccountsByName { #[metrics(+"find_account_by_name")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let name = self.name.clone(); iroha_logger::trace!(%name); Ok(Box::new( - wsv.domains() - .values() + state_snapshot + .world + .domains() .flat_map(move |domain| { let name = name.clone(); @@ -566,24 +685,28 @@ pub mod query { impl ValidQuery for FindAccountsByDomainId { #[metrics(+"find_accounts_by_domain_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let id = &self.domain_id; iroha_logger::trace!(%id); - Ok(Box::new(wsv.domain(id)?.accounts.values().cloned())) + Ok(Box::new( + state_snapshot.world.domain(id)?.accounts.values().cloned(), + )) } } impl ValidQuery for FindAccountKeyValueByIdAndKey { #[metrics(+"find_account_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; let key = &self.key; iroha_logger::trace!(%id, %key); - wsv.map_account(id, |account| account.metadata.get(key).cloned())? + state_snapshot + .world + .map_account(id, |account| account.metadata.get(key).cloned())? .ok_or_else(|| FindError::MetadataKey(key.clone()).into()) .map(Into::into) } @@ -591,22 +714,24 @@ pub mod query { impl ValidQuery for FindAccountsWithAsset { #[metrics(+"find_accounts_with_asset")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let asset_definition_id = self.asset_definition_id.clone(); iroha_logger::trace!(%asset_definition_id); Ok(Box::new( - wsv.map_domain(&asset_definition_id.domain_id.clone(), move |domain| { - domain.accounts.values().filter(move |account| { - let asset_id = - AssetId::new(asset_definition_id.clone(), account.id().clone()); - account.assets.get(&asset_id).is_some() - }) - })? - .cloned(), + state_snapshot + .world + .map_domain(&asset_definition_id.domain_id.clone(), move |domain| { + domain.accounts.values().filter(move |account| { + let asset_id = + AssetId::new(asset_definition_id.clone(), account.id().clone()); + account.assets.get(&asset_id).is_some() + }) + })? + .cloned(), )) } } diff --git a/core/src/smartcontracts/isi/asset.rs b/core/src/smartcontracts/isi/asset.rs index b1a8764ab3b..bc81839bd60 100644 --- a/core/src/smartcontracts/isi/asset.rs +++ b/core/src/smartcontracts/isi/asset.rs @@ -39,22 +39,33 @@ pub mod isi { impl Execute for SetKeyValue { #[metrics(+"set_asset_key_value")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_id = self.object_id; assert_asset_type( &asset_id.definition_id, - wsv, + state_transaction, expected_asset_value_type_store, )?; // Increase `Store` asset total quantity by 1 if asset was not present earlier - if matches!(wsv.asset(&asset_id), Err(QueryExecutionFail::Find(_))) { - wsv.increase_asset_total_amount(&asset_id.definition_id, Numeric::ONE)?; + if matches!( + state_transaction.world.asset(&asset_id), + Err(QueryExecutionFail::Find(_)) + ) { + state_transaction + .world + .increase_asset_total_amount(&asset_id.definition_id, Numeric::ONE)?; } - let asset_metadata_limits = wsv.config.asset_metadata_limits; - let asset = wsv.asset_or_insert(asset_id.clone(), Metadata::new())?; + let asset_metadata_limits = state_transaction.config.asset_metadata_limits; + let asset = state_transaction + .world + .asset_or_insert(asset_id.clone(), Metadata::new())?; { let AssetValue::Store(store) = &mut asset.value else { @@ -68,11 +79,13 @@ pub mod isi { )?; } - wsv.emit_events(Some(AssetEvent::MetadataInserted(MetadataChanged { - target_id: asset_id, - key: self.key, - value: self.value, - }))); + state_transaction + .world + .emit_events(Some(AssetEvent::MetadataInserted(MetadataChanged { + target_id: asset_id, + key: self.key, + value: self.value, + }))); Ok(()) } @@ -80,17 +93,21 @@ pub mod isi { impl Execute for RemoveKeyValue { #[metrics(+"remove_asset_key_value")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_id = self.object_id; assert_asset_type( &asset_id.definition_id, - wsv, + state_transaction, expected_asset_value_type_store, )?; let value = { - let asset = wsv.asset_mut(&asset_id)?; + let asset = state_transaction.world.asset_mut(&asset_id)?; let AssetValue::Store(store) = &mut asset.value else { return Err(Error::Conversion("Expected store asset type".to_owned())); @@ -101,11 +118,13 @@ pub mod isi { .ok_or_else(|| FindError::MetadataKey(self.key.clone()))? }; - wsv.emit_events(Some(AssetEvent::MetadataRemoved(MetadataChanged { - target_id: asset_id, - key: self.key, - value, - }))); + state_transaction + .world + .emit_events(Some(AssetEvent::MetadataRemoved(MetadataChanged { + target_id: asset_id, + key: self.key, + value, + }))); Ok(()) } @@ -113,31 +132,39 @@ pub mod isi { impl Execute for Transfer { #[metrics(+"transfer_store")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_id = self.source_id; assert_asset_type( &asset_id.definition_id, - wsv, + state_transaction, expected_asset_value_type_store, )?; let account_id = asset_id.account_id.clone(); - let asset = wsv.account_mut(&account_id).and_then(|account| { - account - .remove_asset(&asset_id) - .ok_or_else(|| FindError::Asset(asset_id.clone())) - })?; + let asset = state_transaction + .world + .account_mut(&account_id) + .and_then(|account| { + account + .remove_asset(&asset_id) + .ok_or_else(|| FindError::Asset(asset_id.clone())) + })?; let destination_store = { let destination_id = AssetId::new(asset_id.definition_id.clone(), self.destination_id.clone()); - let destination_store_asset = - wsv.asset_or_insert(destination_id.clone(), asset.value)?; + let destination_store_asset = state_transaction + .world + .asset_or_insert(destination_id.clone(), asset.value)?; destination_store_asset.clone() }; - wsv.emit_events([ + state_transaction.world.emit_events([ AssetEvent::Deleted(asset_id), AssetEvent::Created(destination_store), ]); @@ -147,18 +174,24 @@ pub mod isi { } impl Execute for Mint { - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_id = self.destination_id; let asset_definition = assert_asset_type( &asset_id.definition_id, - wsv, + state_transaction, expected_asset_value_type_numeric, )?; assert_numeric_spec(&self.object, &asset_definition)?; - assert_can_mint(&asset_definition, wsv)?; - let asset = wsv.asset_or_insert(asset_id.clone(), Numeric::ZERO)?; + assert_can_mint(&asset_definition, state_transaction)?; + let asset = state_transaction + .world + .asset_or_insert(asset_id.clone(), Numeric::ZERO)?; let AssetValue::Numeric(quantity) = &mut asset.value else { return Err(Error::Conversion("Expected numeric asset type".to_owned())); }; @@ -168,31 +201,42 @@ pub mod isi { #[allow(clippy::float_arithmetic)] { - wsv.new_tx_amounts.lock().push(self.object.to_f64()); - wsv.increase_asset_total_amount(&asset_id.definition_id, self.object)?; + state_transaction + .new_tx_amounts + .lock() + .push(self.object.to_f64()); + state_transaction + .world + .increase_asset_total_amount(&asset_id.definition_id, self.object)?; } - wsv.emit_events(Some(AssetEvent::Added(AssetChanged { - asset_id, - amount: self.object.into(), - }))); + state_transaction + .world + .emit_events(Some(AssetEvent::Added(AssetChanged { + asset_id, + amount: self.object.into(), + }))); Ok(()) } } impl Execute for Burn { - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_id = self.destination_id; let asset_definition = assert_asset_type( &asset_id.definition_id, - wsv, + state_transaction, expected_asset_value_type_numeric, )?; assert_numeric_spec(&self.object, &asset_definition)?; - let account = wsv.account_mut(&asset_id.account_id)?; + let account = state_transaction.world.account_mut(&asset_id.account_id)?; let asset = account .assets .get_mut(&asset_id) @@ -210,34 +254,45 @@ pub mod isi { #[allow(clippy::float_arithmetic)] { - wsv.new_tx_amounts.lock().push(self.object.to_f64()); - wsv.decrease_asset_total_amount(&asset_id.definition_id, self.object)?; + state_transaction + .new_tx_amounts + .lock() + .push(self.object.to_f64()); + state_transaction + .world + .decrease_asset_total_amount(&asset_id.definition_id, self.object)?; } - wsv.emit_events(Some(AssetEvent::Removed(AssetChanged { - asset_id: asset_id.clone(), - amount: self.object.into(), - }))); + state_transaction + .world + .emit_events(Some(AssetEvent::Removed(AssetChanged { + asset_id: asset_id.clone(), + amount: self.object.into(), + }))); Ok(()) } } impl Execute for Transfer { - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let source_id = self.source_id; let destination_id = AssetId::new(source_id.definition_id.clone(), self.destination_id.clone()); let asset_definition = assert_asset_type( &source_id.definition_id, - wsv, + state_transaction, expected_asset_value_type_numeric, )?; assert_numeric_spec(&self.object, &asset_definition)?; { - let account = wsv.account_mut(&source_id.account_id)?; + let account = state_transaction.world.account_mut(&source_id.account_id)?; let asset = account .assets .get_mut(&source_id) @@ -253,7 +308,9 @@ pub mod isi { } } - let destination_asset = wsv.asset_or_insert(destination_id.clone(), Numeric::ZERO)?; + let destination_asset = state_transaction + .world + .asset_or_insert(destination_id.clone(), Numeric::ZERO)?; { let AssetValue::Numeric(quantity) = &mut destination_asset.value else { return Err(Error::Conversion("Expected numeric asset type".to_owned())); @@ -265,10 +322,13 @@ pub mod isi { #[allow(clippy::float_arithmetic)] { - wsv.new_tx_amounts.lock().push(self.object.to_f64()); + state_transaction + .new_tx_amounts + .lock() + .push(self.object.to_f64()); } - wsv.emit_events([ + state_transaction.world.emit_events([ AssetEvent::Removed(AssetChanged { asset_id: source_id, amount: self.object.into(), @@ -312,10 +372,10 @@ pub mod isi { /// Asserts that asset definition with [`definition_id`] has asset type [`expected_value_type`]. pub(crate) fn assert_asset_type( definition_id: &AssetDefinitionId, - wsv: &WorldStateView, + state_transaction: &StateTransaction<'_, '_>, expected_value_type: impl Fn(&AssetValueType) -> Result<(), TypeError>, ) -> Result { - let asset_definition = wsv.asset_definition(definition_id)?; + let asset_definition = state_transaction.world.asset_definition(definition_id)?; expected_value_type(&asset_definition.value_type) .map(|()| asset_definition) .map_err(Into::into) @@ -324,18 +384,20 @@ pub mod isi { /// Assert that this asset is `mintable`. fn assert_can_mint( asset_definition: &AssetDefinition, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), Error> { match asset_definition.mintable { Mintable::Infinitely => Ok(()), Mintable::Not => Err(Error::Mintability(MintabilityError::MintUnmintable)), Mintable::Once => { let asset_definition_id = asset_definition.id.clone(); - let asset_definition = wsv.asset_definition_mut(&asset_definition_id)?; + let asset_definition = state_transaction + .world + .asset_definition_mut(&asset_definition_id)?; forbid_minting(asset_definition)?; - wsv.emit_events(Some(AssetDefinitionEvent::MintabilityChanged( - asset_definition_id, - ))); + state_transaction.world.emit_events(Some( + AssetDefinitionEvent::MintabilityChanged(asset_definition_id), + )); Ok(()) } } @@ -370,16 +432,18 @@ pub mod query { }; use super::*; + use crate::state::StateSnapshot; impl ValidQuery for FindAllAssets { #[metrics(+"find_all_assets")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { Ok(Box::new( - wsv.domains() - .values() + state_snapshot + .world + .domains() .flat_map(|domain| { domain .accounts @@ -393,13 +457,14 @@ pub mod query { impl ValidQuery for FindAllAssetsDefinitions { #[metrics(+"find_all_asset_definitions")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { Ok(Box::new( - wsv.domains() - .values() + state_snapshot + .world + .domains() .flat_map(|domain| domain.asset_definitions.values()) .cloned(), )) @@ -408,11 +473,13 @@ pub mod query { impl ValidQuery for FindAssetById { #[metrics(+"find_asset_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; iroha_logger::trace!(%id); - wsv.asset(id).map_err(|asset_err| { - if let Err(definition_err) = wsv.asset_definition(&id.definition_id) { + state_snapshot.world.asset(id).map_err(|asset_err| { + if let Err(definition_err) = + state_snapshot.world.asset_definition(&id.definition_id) + { definition_err.into() } else { asset_err @@ -423,10 +490,13 @@ pub mod query { impl ValidQuery for FindAssetDefinitionById { #[metrics(+"find_asset_defintion_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; - let entry = wsv.asset_definition(id).map_err(Error::from)?; + let entry = state_snapshot + .world + .asset_definition(id) + .map_err(Error::from)?; Ok(entry) } @@ -434,15 +504,16 @@ pub mod query { impl ValidQuery for FindAssetsByName { #[metrics(+"find_assets_by_name")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let name = self.name.clone(); iroha_logger::trace!(%name); Ok(Box::new( - wsv.domains() - .values() + state_snapshot + .world + .domains() .flat_map(move |domain| { let name = name.clone(); @@ -462,27 +533,28 @@ pub mod query { impl ValidQuery for FindAssetsByAccountId { #[metrics(+"find_assets_by_account_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let id = &self.account_id; iroha_logger::trace!(%id); - Ok(Box::new(wsv.account_assets(id)?.cloned())) + Ok(Box::new(state_snapshot.world.account_assets(id)?.cloned())) } } impl ValidQuery for FindAssetsByAssetDefinitionId { #[metrics(+"find_assets_by_asset_definition_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let id = self.asset_definition_id.clone(); iroha_logger::trace!(%id); Ok(Box::new( - wsv.domains() - .values() + state_snapshot + .world + .domains() .flat_map(move |domain| { let id = id.clone(); @@ -502,14 +574,16 @@ pub mod query { impl ValidQuery for FindAssetsByDomainId { #[metrics(+"find_assets_by_domain_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let id = &self.domain_id; iroha_logger::trace!(%id); Ok(Box::new( - wsv.domain(id)? + state_snapshot + .world + .domain(id)? .accounts .values() .flat_map(|account| account.assets.values()) @@ -520,13 +594,13 @@ pub mod query { impl ValidQuery for FindAssetsByDomainIdAndAssetDefinitionId { #[metrics(+"find_assets_by_domain_id_and_asset_definition_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { let domain_id = self.domain_id.clone(); let asset_definition_id = self.asset_definition_id.clone(); - let domain = wsv.domain(&domain_id)?; + let domain = state_snapshot.world.domain(&domain_id)?; let _definition = domain .asset_definitions .get(&asset_definition_id) @@ -552,13 +626,16 @@ pub mod query { impl ValidQuery for FindAssetQuantityById { #[metrics(+"find_asset_quantity_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; iroha_logger::trace!(%id); - let value = wsv + let value = state_snapshot + .world .asset(id) .map_err(|asset_err| { - if let Err(definition_err) = wsv.asset_definition(&id.definition_id) { + if let Err(definition_err) = + state_snapshot.world.asset_definition(&id.definition_id) + { Error::Find(definition_err) } else { asset_err @@ -577,21 +654,23 @@ pub mod query { impl ValidQuery for FindTotalAssetQuantityByAssetDefinitionId { #[metrics(+"find_total_asset_quantity_by_asset_definition_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; iroha_logger::trace!(%id); - let asset_value = wsv.asset_total_amount(id)?; + let asset_value = state_snapshot.world.asset_total_amount(id)?; Ok(asset_value) } } impl ValidQuery for FindAssetKeyValueByIdAndKey { #[metrics(+"find_asset_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; let key = &self.key; - let asset = wsv.asset(id).map_err(|asset_err| { - if let Err(definition_err) = wsv.asset_definition(&id.definition_id) { + let asset = state_snapshot.world.asset(id).map_err(|asset_err| { + if let Err(definition_err) = + state_snapshot.world.asset_definition(&id.definition_id) + { Error::Find(definition_err) } else { asset_err diff --git a/core/src/smartcontracts/isi/block.rs b/core/src/smartcontracts/isi/block.rs index 752624f09c5..ac44efe2a49 100644 --- a/core/src/smartcontracts/isi/block.rs +++ b/core/src/smartcontracts/isi/block.rs @@ -10,37 +10,47 @@ use iroha_data_model::{ use iroha_telemetry::metrics; use super::*; +use crate::state::StateSnapshot; impl ValidQuery for FindAllBlocks { #[metrics(+"find_all_blocks")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, QueryExecutionFail> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, QueryExecutionFail> { Ok(Box::new( - wsv.all_blocks().rev().map(|block| (*block).clone()), + state_snapshot + .all_blocks() + .rev() + .map(|block| (*block).clone()), )) } } impl ValidQuery for FindAllBlockHeaders { #[metrics(+"find_all_block_headers")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, QueryExecutionFail> { + staete_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, QueryExecutionFail> { Ok(Box::new( - wsv.all_blocks().rev().map(|block| block.header().clone()), + staete_snapshot + .all_blocks() + .rev() + .map(|block| block.header().clone()), )) } } impl ValidQuery for FindBlockHeaderByHash { #[metrics(+"find_block_header")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute( + &self, + state_snapshot: &StateSnapshot<'_>, + ) -> Result { let hash = self.hash; - let block = wsv + let block = state_snapshot .all_blocks() .find(|block| block.hash() == hash) .ok_or_else(|| QueryExecutionFail::Find(FindError::Block(hash)))?; diff --git a/core/src/smartcontracts/isi/domain.rs b/core/src/smartcontracts/isi/domain.rs index 7917bb07066..daeec356a3b 100644 --- a/core/src/smartcontracts/isi/domain.rs +++ b/core/src/smartcontracts/isi/domain.rs @@ -42,16 +42,20 @@ pub mod isi { impl Execute for Register { #[metrics(+"register_account")] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account: Account = self.object.build(authority); let account_id = account.id().clone(); account_id .name - .validate_len(wsv.config.ident_length_limits) + .validate_len(state_transaction.config.ident_length_limits) .map_err(Error::from)?; - let domain = wsv.domain_mut(&account_id.domain_id)?; + let domain = state_transaction.world.domain_mut(&account_id.domain_id)?; if domain.accounts.get(&account_id).is_some() { return Err(RepetitionError { instruction_type: InstructionType::Register, @@ -61,7 +65,9 @@ pub mod isi { } domain.add_account(account.clone()); - wsv.emit_events(Some(DomainEvent::Account(AccountEvent::Created(account)))); + state_transaction + .world + .emit_events(Some(DomainEvent::Account(AccountEvent::Created(account)))); Ok(()) } @@ -69,17 +75,23 @@ pub mod isi { impl Execute for Unregister { #[metrics(+"unregister_account")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let account_id = self.object_id; - let domain = wsv.domain_mut(&account_id.domain_id)?; + let domain = state_transaction.world.domain_mut(&account_id.domain_id)?; if domain.remove_account(&account_id).is_none() { return Err(FindError::Account(account_id).into()); } - wsv.emit_events(Some(DomainEvent::Account(AccountEvent::Deleted( - account_id, - )))); + state_transaction + .world + .emit_events(Some(DomainEvent::Account(AccountEvent::Deleted( + account_id, + )))); Ok(()) } @@ -87,16 +99,22 @@ pub mod isi { impl Execute for Register { #[metrics(+"register_asset_definition")] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_definition = self.object.build(authority); asset_definition .id() .name - .validate_len(wsv.config.ident_length_limits) + .validate_len(state_transaction.config.ident_length_limits) .map_err(Error::from)?; let asset_definition_id = asset_definition.id().clone(); - let domain = wsv.domain_mut(&asset_definition_id.domain_id)?; + let domain = state_transaction + .world + .domain_mut(&asset_definition_id.domain_id)?; if domain.asset_definitions.get(&asset_definition_id).is_some() { return Err(RepetitionError { instruction_type: InstructionType::Register, @@ -109,9 +127,11 @@ pub mod isi { domain.add_asset_definition(asset_definition.clone()); - wsv.emit_events(Some(DomainEvent::AssetDefinition( - AssetDefinitionEvent::Created(asset_definition), - ))); + state_transaction + .world + .emit_events(Some(DomainEvent::AssetDefinition( + AssetDefinitionEvent::Created(asset_definition), + ))); Ok(()) } @@ -119,11 +139,15 @@ pub mod isi { impl Execute for Unregister { #[metrics(+"unregister_asset_definition")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_definition_id = self.object_id; let mut assets_to_remove = Vec::new(); - for domain in wsv.domains().values() { + for domain in state_transaction.world.domains() { for account in domain.accounts.values() { assets_to_remove.extend( account @@ -144,7 +168,8 @@ pub mod isi { let mut events = Vec::with_capacity(assets_to_remove.len() + 1); for asset_id in assets_to_remove { let account_id = asset_id.account_id.clone(); - if wsv + if state_transaction + .world .account_mut(&account_id)? .remove_asset(&asset_id) .is_none() @@ -155,7 +180,9 @@ pub mod isi { events.push(AccountEvent::Asset(AssetEvent::Deleted(asset_id)).into()); } - let domain = wsv.domain_mut(&asset_definition_id.domain_id)?; + let domain = state_transaction + .world + .domain_mut(&asset_definition_id.domain_id)?; if domain .remove_asset_definition(&asset_definition_id) .is_none() @@ -169,7 +196,7 @@ pub mod isi { AssetDefinitionEvent::Deleted(asset_definition_id), ))); - wsv.emit_events(events); + state_transaction.world.emit_events(events); Ok(()) } @@ -177,11 +204,17 @@ pub mod isi { impl Execute for SetKeyValue { #[metrics(+"set_key_value_asset_definition")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_definition_id = self.object_id; - let metadata_limits = wsv.config.asset_definition_metadata_limits; - wsv.asset_definition_mut(&asset_definition_id) + let metadata_limits = state_transaction.config.asset_definition_metadata_limits; + state_transaction + .world + .asset_definition_mut(&asset_definition_id) .map_err(Error::from) .and_then(|asset_definition| { asset_definition @@ -190,13 +223,15 @@ pub mod isi { .map_err(Error::from) })?; - wsv.emit_events(Some(AssetDefinitionEvent::MetadataInserted( - MetadataChanged { - target_id: asset_definition_id, - key: self.key, - value: self.value, - }, - ))); + state_transaction + .world + .emit_events(Some(AssetDefinitionEvent::MetadataInserted( + MetadataChanged { + target_id: asset_definition_id, + key: self.key, + value: self.value, + }, + ))); Ok(()) } @@ -204,25 +239,32 @@ pub mod isi { impl Execute for RemoveKeyValue { #[metrics(+"remove_key_value_asset_definition")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let asset_definition_id = self.object_id; - let value = - wsv.asset_definition_mut(&asset_definition_id) - .and_then(|asset_definition| { - asset_definition - .metadata - .remove(&self.key) - .ok_or_else(|| FindError::MetadataKey(self.key.clone())) - })?; - - wsv.emit_events(Some(AssetDefinitionEvent::MetadataRemoved( - MetadataChanged { - target_id: asset_definition_id, - key: self.key, - value, - }, - ))); + let value = state_transaction + .world + .asset_definition_mut(&asset_definition_id) + .and_then(|asset_definition| { + asset_definition + .metadata + .remove(&self.key) + .ok_or_else(|| FindError::MetadataKey(self.key.clone())) + })?; + + state_transaction + .world + .emit_events(Some(AssetDefinitionEvent::MetadataRemoved( + MetadataChanged { + target_id: asset_definition_id, + key: self.key, + value, + }, + ))); Ok(()) } @@ -230,21 +272,27 @@ pub mod isi { impl Execute for SetKeyValue { #[metrics(+"set_domain_key_value")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let domain_id = self.object_id; - let limits = wsv.config.domain_metadata_limits; + let limits = state_transaction.config.domain_metadata_limits; - let domain = wsv.domain_mut(&domain_id)?; + let domain = state_transaction.world.domain_mut(&domain_id)?; domain .metadata .insert_with_limits(self.key.clone(), self.value.clone(), limits)?; - wsv.emit_events(Some(DomainEvent::MetadataInserted(MetadataChanged { - target_id: domain_id, - key: self.key, - value: self.value, - }))); + state_transaction + .world + .emit_events(Some(DomainEvent::MetadataInserted(MetadataChanged { + target_id: domain_id, + key: self.key, + value: self.value, + }))); Ok(()) } @@ -252,47 +300,59 @@ pub mod isi { impl Execute for RemoveKeyValue { #[metrics(+"remove_domain_key_value")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let domain_id = self.object_id; - let domain = wsv.domain_mut(&domain_id)?; + let domain = state_transaction.world.domain_mut(&domain_id)?; let value = domain .metadata .remove(&self.key) .ok_or_else(|| FindError::MetadataKey(self.key.clone()))?; - wsv.emit_events(Some(DomainEvent::MetadataRemoved(MetadataChanged { - target_id: domain_id, - key: self.key, - value, - }))); + state_transaction + .world + .emit_events(Some(DomainEvent::MetadataRemoved(MetadataChanged { + target_id: domain_id, + key: self.key, + value, + }))); Ok(()) } } impl Execute for Transfer { - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let Transfer { source_id, object, destination_id, } = self; - let _ = wsv.account(&source_id)?; - let _ = wsv.account(&destination_id)?; + let _ = state_transaction.world.account(&source_id)?; + let _ = state_transaction.world.account(&destination_id)?; - let domain = wsv.domain_mut(&object)?; + let domain = state_transaction.world.domain_mut(&object)?; if domain.owned_by != source_id { return Err(Error::Find(FindError::Account(source_id))); } domain.owned_by = destination_id.clone(); - wsv.emit_events(Some(DomainEvent::OwnerChanged(DomainOwnerChanged { - domain_id: object, - new_owner: destination_id, - }))); + state_transaction + .world + .emit_events(Some(DomainEvent::OwnerChanged(DomainOwnerChanged { + domain_id: object, + new_owner: destination_id, + }))); Ok(()) } @@ -307,33 +367,36 @@ pub mod query { }; use super::*; + use crate::state::StateSnapshot; impl ValidQuery for FindAllDomains { #[metrics(+"find_all_domains")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { - Ok(Box::new(wsv.domains().values().cloned())) + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { + Ok(Box::new(state_snapshot.world.domains().cloned())) } } impl ValidQuery for FindDomainById { #[metrics(+"find_domain_by_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; iroha_logger::trace!(%id); - Ok(wsv.domain(id)?.clone()) + Ok(state_snapshot.world.domain(id)?.clone()) } } impl ValidQuery for FindDomainKeyValueByIdAndKey { #[metrics(+"find_domain_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; let key = &self.key; iroha_logger::trace!(%id, %key); - wsv.map_domain(id, |domain| domain.metadata.get(key).cloned())? + state_snapshot + .world + .map_domain(id, |domain| domain.metadata.get(key).cloned())? .ok_or_else(|| FindError::MetadataKey(key.clone()).into()) .map(Into::into) } @@ -341,11 +404,12 @@ pub mod query { impl ValidQuery for FindAssetDefinitionKeyValueByIdAndKey { #[metrics(+"find_asset_definition_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; let key = &self.key; iroha_logger::trace!(%id, %key); - Ok(wsv + Ok(state_snapshot + .world .asset_definition(id)? .metadata .get(key) diff --git a/core/src/smartcontracts/isi/mod.rs b/core/src/smartcontracts/isi/mod.rs index 8218f0bb994..982ad34066e 100644 --- a/core/src/smartcontracts/isi/mod.rs +++ b/core/src/smartcontracts/isi/mod.rs @@ -18,7 +18,7 @@ use iroha_data_model::{ use iroha_logger::prelude::*; use super::Execute; -use crate::{prelude::*, wsv::WorldStateView}; +use crate::{prelude::*, state::StateTransaction}; /// Trait for proxy objects used for registration. pub trait Registrable { @@ -30,66 +30,82 @@ pub trait Registrable { } impl Execute for InstructionBox { - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { iroha_logger::debug!(isi=%self, "Executing"); match self { - Self::Register(isi) => isi.execute(authority, wsv), - Self::Unregister(isi) => isi.execute(authority, wsv), - Self::Mint(isi) => isi.execute(authority, wsv), - Self::Burn(isi) => isi.execute(authority, wsv), - Self::Transfer(isi) => isi.execute(authority, wsv), - Self::Fail(isi) => isi.execute(authority, wsv), - Self::SetKeyValue(isi) => isi.execute(authority, wsv), - Self::RemoveKeyValue(isi) => isi.execute(authority, wsv), - Self::Grant(isi) => isi.execute(authority, wsv), - Self::Revoke(isi) => isi.execute(authority, wsv), - Self::ExecuteTrigger(isi) => isi.execute(authority, wsv), - Self::SetParameter(isi) => isi.execute(authority, wsv), - Self::NewParameter(isi) => isi.execute(authority, wsv), - Self::Upgrade(isi) => isi.execute(authority, wsv), - Self::Log(isi) => isi.execute(authority, wsv), + Self::Register(isi) => isi.execute(authority, state_transaction), + Self::Unregister(isi) => isi.execute(authority, state_transaction), + Self::Mint(isi) => isi.execute(authority, state_transaction), + Self::Burn(isi) => isi.execute(authority, state_transaction), + Self::Transfer(isi) => isi.execute(authority, state_transaction), + Self::Fail(isi) => isi.execute(authority, state_transaction), + Self::SetKeyValue(isi) => isi.execute(authority, state_transaction), + Self::RemoveKeyValue(isi) => isi.execute(authority, state_transaction), + Self::Grant(isi) => isi.execute(authority, state_transaction), + Self::Revoke(isi) => isi.execute(authority, state_transaction), + Self::ExecuteTrigger(isi) => isi.execute(authority, state_transaction), + Self::SetParameter(isi) => isi.execute(authority, state_transaction), + Self::NewParameter(isi) => isi.execute(authority, state_transaction), + Self::Upgrade(isi) => isi.execute(authority, state_transaction), + Self::Log(isi) => isi.execute(authority, state_transaction), } } } impl Execute for RegisterBox { #[iroha_logger::log(name = "register", skip_all, fields(id))] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::Peer(isi) => isi.execute(authority, wsv), - Self::Domain(isi) => isi.execute(authority, wsv), - Self::Account(isi) => isi.execute(authority, wsv), - Self::AssetDefinition(isi) => isi.execute(authority, wsv), - Self::Asset(isi) => isi.execute(authority, wsv), - Self::Role(isi) => isi.execute(authority, wsv), - Self::Trigger(isi) => isi.execute(authority, wsv), + Self::Peer(isi) => isi.execute(authority, state_transaction), + Self::Domain(isi) => isi.execute(authority, state_transaction), + Self::Account(isi) => isi.execute(authority, state_transaction), + Self::AssetDefinition(isi) => isi.execute(authority, state_transaction), + Self::Asset(isi) => isi.execute(authority, state_transaction), + Self::Role(isi) => isi.execute(authority, state_transaction), + Self::Trigger(isi) => isi.execute(authority, state_transaction), } } } impl Execute for UnregisterBox { #[iroha_logger::log(name = "unregister", skip_all, fields(id))] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::Peer(isi) => isi.execute(authority, wsv), - Self::Domain(isi) => isi.execute(authority, wsv), - Self::Account(isi) => isi.execute(authority, wsv), - Self::AssetDefinition(isi) => isi.execute(authority, wsv), - Self::Asset(isi) => isi.execute(authority, wsv), - Self::Role(isi) => isi.execute(authority, wsv), - Self::Trigger(isi) => isi.execute(authority, wsv), + Self::Peer(isi) => isi.execute(authority, state_transaction), + Self::Domain(isi) => isi.execute(authority, state_transaction), + Self::Account(isi) => isi.execute(authority, state_transaction), + Self::AssetDefinition(isi) => isi.execute(authority, state_transaction), + Self::Asset(isi) => isi.execute(authority, state_transaction), + Self::Role(isi) => isi.execute(authority, state_transaction), + Self::Trigger(isi) => isi.execute(authority, state_transaction), } } } impl Execute for MintBox { #[iroha_logger::log(name = "Mint", skip_all, fields(destination))] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::Account(isi) => isi.execute(authority, wsv), - Self::Asset(isi) => isi.execute(authority, wsv), - Self::TriggerRepetitions(isi) => isi.execute(authority, wsv), + Self::Account(isi) => isi.execute(authority, state_transaction), + Self::Asset(isi) => isi.execute(authority, state_transaction), + Self::TriggerRepetitions(isi) => isi.execute(authority, state_transaction), } } } @@ -98,33 +114,41 @@ impl Execute for AccountMintBox { fn execute( self, authority: &AccountId, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, ) -> std::prelude::v1::Result<(), Error> { match self { - Self::PublicKey(isi) => isi.execute(authority, wsv), - Self::SignatureCheckCondition(isi) => isi.execute(authority, wsv), + Self::PublicKey(isi) => isi.execute(authority, state_transaction), + Self::SignatureCheckCondition(isi) => isi.execute(authority, state_transaction), } } } impl Execute for BurnBox { #[iroha_logger::log(name = "burn", skip_all, fields(destination))] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::AccountPublicKey(isi) => isi.execute(authority, wsv), - Self::Asset(isi) => isi.execute(authority, wsv), - Self::TriggerRepetitions(isi) => isi.execute(authority, wsv), + Self::AccountPublicKey(isi) => isi.execute(authority, state_transaction), + Self::Asset(isi) => isi.execute(authority, state_transaction), + Self::TriggerRepetitions(isi) => isi.execute(authority, state_transaction), } } } impl Execute for TransferBox { #[iroha_logger::log(name = "transfer", skip_all, fields(from, to))] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::Domain(isi) => isi.execute(authority, wsv), - Self::AssetDefinition(isi) => isi.execute(authority, wsv), - Self::Asset(isi) => isi.execute(authority, wsv), + Self::Domain(isi) => isi.execute(authority, state_transaction), + Self::AssetDefinition(isi) => isi.execute(authority, state_transaction), + Self::Asset(isi) => isi.execute(authority, state_transaction), } } } @@ -133,39 +157,51 @@ impl Execute for AssetTransferBox { fn execute( self, authority: &AccountId, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, ) -> std::prelude::v1::Result<(), Error> { match self { - Self::Numeric(isi) => isi.execute(authority, wsv), - Self::Store(isi) => isi.execute(authority, wsv), + Self::Numeric(isi) => isi.execute(authority, state_transaction), + Self::Store(isi) => isi.execute(authority, state_transaction), } } } impl Execute for SetKeyValueBox { - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::Domain(isi) => isi.execute(authority, wsv), - Self::Account(isi) => isi.execute(authority, wsv), - Self::AssetDefinition(isi) => isi.execute(authority, wsv), - Self::Asset(isi) => isi.execute(authority, wsv), + Self::Domain(isi) => isi.execute(authority, state_transaction), + Self::Account(isi) => isi.execute(authority, state_transaction), + Self::AssetDefinition(isi) => isi.execute(authority, state_transaction), + Self::Asset(isi) => isi.execute(authority, state_transaction), } } } impl Execute for RemoveKeyValueBox { - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::Domain(isi) => isi.execute(authority, wsv), - Self::Account(isi) => isi.execute(authority, wsv), - Self::AssetDefinition(isi) => isi.execute(authority, wsv), - Self::Asset(isi) => isi.execute(authority, wsv), + Self::Domain(isi) => isi.execute(authority, state_transaction), + Self::Account(isi) => isi.execute(authority, state_transaction), + Self::AssetDefinition(isi) => isi.execute(authority, state_transaction), + Self::Asset(isi) => isi.execute(authority, state_transaction), } } } impl Execute for Fail { - fn execute(self, _authority: &AccountId, _wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + _state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { iroha_logger::trace!(?self); Err(Error::Fail(self.message)) @@ -174,22 +210,30 @@ impl Execute for Fail { impl Execute for GrantBox { #[iroha_logger::log(name = "grant", skip_all, fields(object))] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::PermissionToken(sub_isi) => sub_isi.execute(authority, wsv), - Self::Role(sub_isi) => sub_isi.execute(authority, wsv), - Self::RolePermissionToken(sub_isi) => sub_isi.execute(authority, wsv), + Self::PermissionToken(sub_isi) => sub_isi.execute(authority, state_transaction), + Self::Role(sub_isi) => sub_isi.execute(authority, state_transaction), + Self::RolePermissionToken(sub_isi) => sub_isi.execute(authority, state_transaction), } } } impl Execute for RevokeBox { #[iroha_logger::log(name = "revoke", skip_all, fields(object))] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { match self { - Self::PermissionToken(sub_isi) => sub_isi.execute(authority, wsv), - Self::Role(sub_isi) => sub_isi.execute(authority, wsv), - Self::RolePermissionToken(sub_isi) => sub_isi.execute(authority, wsv), + Self::PermissionToken(sub_isi) => sub_isi.execute(authority, state_transaction), + Self::Role(sub_isi) => sub_isi.execute(authority, state_transaction), + Self::RolePermissionToken(sub_isi) => sub_isi.execute(authority, state_transaction), } } } @@ -209,29 +253,40 @@ mod tests { use tokio::test; use super::*; - use crate::{kura::Kura, query::store::LiveQueryStore, wsv::World, PeersIds}; - - fn wsv_with_test_domains(kura: &Arc) -> Result { + use crate::{ + kura::Kura, + query::store::LiveQueryStore, + state::{State, World}, + PeersIds, + }; + + fn state_with_test_domains(kura: &Arc) -> Result { let world = World::with([], PeersIds::new()); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world, kura.clone(), query_handle); + let state = State::new(world, kura.clone(), query_handle); let genesis_account_id = AccountId::from_str("genesis@genesis")?; let account_id = AccountId::from_str("alice@wonderland")?; let (public_key, _) = KeyPair::random().into_parts(); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; + let mut state_block = state.block(false); + let mut state_transaction = state_block.transaction(); Register::domain(Domain::new(DomainId::from_str("wonderland")?)) - .execute(&genesis_account_id, &mut wsv)?; + .execute(&genesis_account_id, &mut state_transaction)?; Register::account(Account::new(account_id, public_key)) - .execute(&genesis_account_id, &mut wsv)?; + .execute(&genesis_account_id, &mut state_transaction)?; Register::asset_definition(AssetDefinition::store(asset_definition_id)) - .execute(&genesis_account_id, &mut wsv)?; - Ok(wsv) + .execute(&genesis_account_id, &mut state_transaction)?; + state_transaction.apply(); + state_block.commit(); + Ok(state) } #[test] async fn asset_store() -> Result<()> { let kura = Kura::blank_kura_for_testing(); - let mut wsv = wsv_with_test_domains(&kura)?; + let state = state_with_test_domains(&kura)?; + let mut staet_block = state.block(false); + let mut state_transaction = staet_block.transaction(); let account_id = AccountId::from_str("alice@wonderland")?; let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let asset_id = AssetId::new(asset_definition_id, account_id.clone()); @@ -240,8 +295,8 @@ mod tests { Name::from_str("Bytes")?, vec![1_u32, 2_u32, 3_u32], ) - .execute(&account_id, &mut wsv)?; - let asset = wsv.asset(&asset_id)?; + .execute(&account_id, &mut state_transaction)?; + let asset = state_transaction.world.asset(&asset_id)?; let AssetValue::Store(store) = &asset.value else { panic!("expected store asset"); }; @@ -260,20 +315,24 @@ mod tests { #[test] async fn account_metadata() -> Result<()> { let kura = Kura::blank_kura_for_testing(); - let mut wsv = wsv_with_test_domains(&kura)?; + let state = state_with_test_domains(&kura)?; + let mut state_block = state.block(false); + let mut state_transaction = state_block.transaction(); let account_id = AccountId::from_str("alice@wonderland")?; SetKeyValue::account( account_id.clone(), Name::from_str("Bytes")?, vec![1_u32, 2_u32, 3_u32], ) - .execute(&account_id, &mut wsv)?; - let bytes = wsv.map_account(&account_id, |account| { - account - .metadata() - .get(&Name::from_str("Bytes").expect("Valid")) - .cloned() - })?; + .execute(&account_id, &mut state_transaction)?; + let bytes = state_transaction + .world + .map_account(&account_id, |account| { + account + .metadata() + .get(&Name::from_str("Bytes").expect("Valid")) + .cloned() + })?; assert_eq!( bytes, Some(MetadataValueBox::Vec(vec![ @@ -288,7 +347,9 @@ mod tests { #[test] async fn asset_definition_metadata() -> Result<()> { let kura = Kura::blank_kura_for_testing(); - let mut wsv = wsv_with_test_domains(&kura)?; + let state = state_with_test_domains(&kura)?; + let mut state_block = state.block(false); + let mut state_transaction = state_block.transaction(); let definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let account_id = AccountId::from_str("alice@wonderland")?; SetKeyValue::asset_definition( @@ -296,8 +357,9 @@ mod tests { Name::from_str("Bytes")?, vec![1_u32, 2_u32, 3_u32], ) - .execute(&account_id, &mut wsv)?; - let bytes = wsv + .execute(&account_id, &mut state_transaction)?; + let bytes = state_transaction + .world .asset_definition(&definition_id)? .metadata() .get(&Name::from_str("Bytes")?) @@ -316,7 +378,9 @@ mod tests { #[test] async fn domain_metadata() -> Result<()> { let kura = Kura::blank_kura_for_testing(); - let mut wsv = wsv_with_test_domains(&kura)?; + let state = state_with_test_domains(&kura)?; + let mut state_block = state.block(false); + let mut state_transaction = state_block.transaction(); let domain_id = DomainId::from_str("wonderland")?; let account_id = AccountId::from_str("alice@wonderland")?; SetKeyValue::domain( @@ -324,8 +388,9 @@ mod tests { Name::from_str("Bytes")?, vec![1_u32, 2_u32, 3_u32], ) - .execute(&account_id, &mut wsv)?; - let bytes = wsv + .execute(&account_id, &mut state_transaction)?; + let bytes = state_transaction + .world .domain(&domain_id)? .metadata() .get(&Name::from_str("Bytes")?) @@ -344,13 +409,15 @@ mod tests { #[test] async fn executing_unregistered_trigger_should_return_error() -> Result<()> { let kura = Kura::blank_kura_for_testing(); - let mut wsv = wsv_with_test_domains(&kura)?; + let state = state_with_test_domains(&kura)?; + let mut state_block = state.block(false); + let mut state_transaction = state_block.transaction(); let account_id = AccountId::from_str("alice@wonderland")?; let trigger_id = TriggerId::from_str("test_trigger_id")?; assert!(matches!( ExecuteTrigger::new(trigger_id) - .execute(&account_id, &mut wsv) + .execute(&account_id, &mut state_transaction) .expect_err("Error expected"), Error::Find(_) )); @@ -361,7 +428,9 @@ mod tests { #[test] async fn unauthorized_trigger_execution_should_return_error() -> Result<()> { let kura = Kura::blank_kura_for_testing(); - let mut wsv = wsv_with_test_domains(&kura)?; + let state = state_with_test_domains(&kura)?; + let mut state_block = state.block(false); + let mut state_transaction = state_block.transaction(); let account_id = AccountId::from_str("alice@wonderland")?; let fake_account_id = AccountId::from_str("fake@wonderland")?; let trigger_id = TriggerId::from_str("test_trigger_id")?; @@ -369,7 +438,7 @@ mod tests { // register fake account let (public_key, _) = KeyPair::random().into_parts(); let register_account = Register::account(Account::new(fake_account_id.clone(), public_key)); - register_account.execute(&account_id, &mut wsv)?; + register_account.execute(&account_id, &mut state_transaction)?; // register the trigger let register_trigger = Register::trigger(Trigger::new( @@ -386,15 +455,15 @@ mod tests { ), )); - register_trigger.execute(&account_id, &mut wsv)?; + register_trigger.execute(&account_id, &mut state_transaction)?; // execute with the valid account - ExecuteTrigger::new(trigger_id.clone()).execute(&account_id, &mut wsv)?; + ExecuteTrigger::new(trigger_id.clone()).execute(&account_id, &mut state_transaction)?; // execute with the fake account assert!(matches!( ExecuteTrigger::new(trigger_id) - .execute(&fake_account_id, &mut wsv) + .execute(&fake_account_id, &mut state_transaction) .expect_err("Error expected"), Error::InvariantViolation(_) )); diff --git a/core/src/smartcontracts/isi/query.rs b/core/src/smartcontracts/isi/query.rs index 88db4d6330b..3badc7ab6fa 100644 --- a/core/src/smartcontracts/isi/query.rs +++ b/core/src/smartcontracts/isi/query.rs @@ -7,7 +7,7 @@ use iroha_data_model::{ }; use parity_scale_codec::{Decode, Encode}; -use crate::{prelude::ValidQuery, WorldStateView}; +use crate::{prelude::ValidQuery, state::StateSnapshot}; /// Represents lazy evaluated query output pub trait Lazy { @@ -65,8 +65,12 @@ impl ValidQueryRequest { /// - Account doesn't exist /// - Account doesn't have the correct public key /// - Account has incorrect permissions - pub fn validate(query: SignedQuery, wsv: &WorldStateView) -> Result { - let account_has_public_key = wsv + pub fn validate( + query: SignedQuery, + state_snapshot: &StateSnapshot<'_>, + ) -> Result { + let account_has_public_key = state_snapshot + .world .map_account(query.authority(), |account| { account.contains_signatory(query.signature().public_key()) }) @@ -77,20 +81,23 @@ impl ValidQueryRequest { )) .into()); } - wsv.executor() - .validate_query(wsv, query.authority(), query.query().clone())?; + state_snapshot.world.executor.validate_query( + state_snapshot, + query.authority(), + query.query().clone(), + )?; Ok(Self(query)) } - /// Execute contained query on the [`WorldStateView`]. + /// Execute contained query on the [`StateSnapshot`]. /// /// # Errors /// Forwards `self.query.execute` error. - pub fn execute<'wsv>( - &'wsv self, - wsv: &'wsv WorldStateView, - ) -> Result, Error> { - let output = self.0.query().execute(wsv)?; + pub fn execute<'state>( + &'state self, + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result, Error> { + let output = self.0.query().execute(state_snapshot)?; Ok(if let LazyQueryOutput::Iter(iter) = output { LazyQueryOutput::Iter(Box::new(iter.filter(|val| self.0.filter().applies(val)))) @@ -109,14 +116,17 @@ impl ValidQueryRequest { } impl ValidQuery for QueryBox { - fn execute<'wsv>(&self, wsv: &'wsv WorldStateView) -> Result, Error> { + fn execute<'state>( + &self, + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result, Error> { iroha_logger::debug!(query=%self, "Executing"); macro_rules! match_all { ( non_iter: {$( $non_iter_query:ident ),+ $(,)?} $( $query:ident, )+ ) => { match self { $( - QueryBox::$non_iter_query(query) => query.execute(wsv).map(QueryOutputBox::from).map(LazyQueryOutput::QueryOutput), )+ $( - QueryBox::$query(query) => query.execute(wsv).map(|i| i.map(QueryOutputBox::from)).map(|iter| LazyQueryOutput::Iter(Box::new(iter))), )+ + QueryBox::$non_iter_query(query) => query.execute(state_snapshot).map(QueryOutputBox::from).map(LazyQueryOutput::QueryOutput), )+ $( + QueryBox::$query(query) => query.execute(state_snapshot).map(|i| i.map(QueryOutputBox::from)).map(|iter| LazyQueryOutput::Iter(Box::new(iter))), )+ } }; } @@ -183,8 +193,14 @@ mod tests { use super::*; use crate::{ - block::*, kura::Kura, query::store::LiveQueryStore, smartcontracts::isi::Registrable as _, - sumeragi::network_topology::Topology, tx::AcceptedTransaction, wsv::World, PeersIds, + block::*, + kura::Kura, + query::store::LiveQueryStore, + smartcontracts::isi::Registrable as _, + state::{State, World}, + sumeragi::network_topology::Topology, + tx::AcceptedTransaction, + PeersIds, }; static ALICE_KEYS: Lazy = Lazy::new(KeyPair::random); @@ -252,80 +268,83 @@ mod tests { Ok(World::with([domain], PeersIds::new())) } - fn wsv_with_test_blocks_and_transactions( + fn state_with_test_blocks_and_transactions( blocks: u64, valid_tx_per_block: usize, invalid_tx_per_block: usize, - ) -> Result { + ) -> Result { let chain_id = ChainId::from("0"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world_with_test_domains(), kura.clone(), query_handle); - - let limits = TransactionLimits { - max_instruction_number: 1, - max_wasm_size_bytes: 0, - }; - let huge_limits = TransactionLimits { - max_instruction_number: 1000, - max_wasm_size_bytes: 0, - }; - - wsv.config.transaction_limits = limits; - - let valid_tx = { - let instructions: [InstructionBox; 0] = []; - let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) - .with_instructions(instructions) - .sign(&ALICE_KEYS); - AcceptedTransaction::accept(tx, &chain_id, &limits)? - }; - let invalid_tx = { - let isi = Fail::new("fail".to_owned()); - let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) - .with_instructions([isi.clone(), isi]) - .sign(&ALICE_KEYS); - AcceptedTransaction::accept(tx, &chain_id, &huge_limits)? - }; + let state = State::new(world_with_test_domains(), kura.clone(), query_handle); + { + let mut state_block = state.block(false); + let limits = TransactionLimits { + max_instruction_number: 1, + max_wasm_size_bytes: 0, + }; + let huge_limits = TransactionLimits { + max_instruction_number: 1000, + max_wasm_size_bytes: 0, + }; - let mut transactions = vec![valid_tx; valid_tx_per_block]; - transactions.append(&mut vec![invalid_tx; invalid_tx_per_block]); + state_block.config.transaction_limits = limits; - let topology = Topology::new(UniqueVec::new()); - let first_block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) - .chain(0, &mut wsv) - .sign(&ALICE_KEYS) - .commit(&topology) - .expect("Block is valid"); + let valid_tx = { + let instructions: [InstructionBox; 0] = []; + let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) + .with_instructions(instructions) + .sign(&ALICE_KEYS); + AcceptedTransaction::accept(tx, &chain_id, &limits)? + }; + let invalid_tx = { + let isi = Fail::new("fail".to_owned()); + let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) + .with_instructions([isi.clone(), isi]) + .sign(&ALICE_KEYS); + AcceptedTransaction::accept(tx, &chain_id, &huge_limits)? + }; - wsv.apply(&first_block)?; - kura.store_block(first_block); + let mut transactions = vec![valid_tx; valid_tx_per_block]; + transactions.append(&mut vec![invalid_tx; invalid_tx_per_block]); - for _ in 1u64..blocks { - let block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) - .chain(0, &mut wsv) + let topology = Topology::new(UniqueVec::new()); + let first_block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) + .chain(0, &mut state_block) .sign(&ALICE_KEYS) .commit(&topology) .expect("Block is valid"); - wsv.apply(&block)?; - kura.store_block(block); + state_block.apply(&first_block)?; + kura.store_block(first_block); + + for _ in 1u64..blocks { + let block = BlockBuilder::new(transactions.clone(), topology.clone(), Vec::new()) + .chain(0, &mut state_block) + .sign(&ALICE_KEYS) + .commit(&topology) + .expect("Block is valid"); + + state_block.apply(&block)?; + kura.store_block(block); + } + state_block.commit(); } - Ok(wsv) + Ok(state) } #[test] async fn asset_store() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = WorldStateView::new(world_with_test_asset_with_metadata(), kura, query_handle); + let state = State::new(world_with_test_asset_with_metadata(), kura, query_handle); let asset_definition_id = AssetDefinitionId::from_str("rose#wonderland")?; let asset_id = AssetId::new(asset_definition_id, ALICE_ID.clone()); - let bytes = - FindAssetKeyValueByIdAndKey::new(asset_id, Name::from_str("Bytes")?).execute(&wsv)?; + let bytes = FindAssetKeyValueByIdAndKey::new(asset_id, Name::from_str("Bytes")?) + .execute(&state.view().to_snapshot())?; assert_eq!( MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), bytes, @@ -337,10 +356,10 @@ mod tests { async fn account_metadata() -> Result<()> { let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let wsv = WorldStateView::new(world_with_test_account_with_metadata()?, kura, query_handle); + let state = State::new(world_with_test_account_with_metadata()?, kura, query_handle); let bytes = FindAccountKeyValueByIdAndKey::new(ALICE_ID.clone(), Name::from_str("Bytes")?) - .execute(&wsv)?; + .execute(&state.view().to_snapshot())?; assert_eq!( MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), bytes, @@ -352,8 +371,10 @@ mod tests { async fn find_all_blocks() -> Result<()> { let num_blocks = 100; - let wsv = wsv_with_test_blocks_and_transactions(num_blocks, 1, 1)?; - let blocks = FindAllBlocks.execute(&wsv)?.collect::>(); + let state = state_with_test_blocks_and_transactions(num_blocks, 1, 1)?; + let blocks = FindAllBlocks + .execute(&state.view().to_snapshot())? + .collect::>(); assert_eq!(blocks.len() as u64, num_blocks); assert!(blocks.windows(2).all(|wnd| wnd[0] >= wnd[1])); @@ -365,8 +386,10 @@ mod tests { async fn find_all_block_headers() -> Result<()> { let num_blocks = 100; - let wsv = wsv_with_test_blocks_and_transactions(num_blocks, 1, 1)?; - let block_headers = FindAllBlockHeaders.execute(&wsv)?.collect::>(); + let state = state_with_test_blocks_and_transactions(num_blocks, 1, 1)?; + let block_headers = FindAllBlockHeaders + .execute(&state.view().to_snapshot())? + .collect::>(); assert_eq!(block_headers.len() as u64, num_blocks); assert!(block_headers.windows(2).all(|wnd| wnd[0] >= wnd[1])); @@ -376,17 +399,19 @@ mod tests { #[test] async fn find_block_header_by_hash() -> Result<()> { - let wsv = wsv_with_test_blocks_and_transactions(1, 1, 1)?; - let block = wsv.all_blocks().last().expect("WSV is empty"); + let state = state_with_test_blocks_and_transactions(1, 1, 1)?; + let state_view = state.view(); + let state_snapshot = state_view.to_snapshot(); + let block = state_snapshot.all_blocks().last().expect("state is empty"); assert_eq!( - FindBlockHeaderByHash::new(block.hash()).execute(&wsv)?, + FindBlockHeaderByHash::new(block.hash()).execute(&state_snapshot)?, *block.header() ); assert!( FindBlockHeaderByHash::new(HashOf::from_untyped_unchecked(Hash::new([42]))) - .execute(&wsv) + .execute(&state_snapshot) .is_err() ); @@ -397,8 +422,12 @@ mod tests { async fn find_all_transactions() -> Result<()> { let num_blocks = 100; - let wsv = wsv_with_test_blocks_and_transactions(num_blocks, 1, 1)?; - let txs = FindAllTransactions.execute(&wsv)?.collect::>(); + let state = state_with_test_blocks_and_transactions(num_blocks, 1, 1)?; + let state_view = state.view(); + let state_snapshot = state_view.to_snapshot(); + let txs = FindAllTransactions + .execute(&state_snapshot)? + .collect::>(); assert_eq!(txs.len() as u64, num_blocks * 2); assert_eq!( @@ -423,37 +452,43 @@ mod tests { let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world_with_test_domains(), kura.clone(), query_handle); + let state = State::new(world_with_test_domains(), kura.clone(), query_handle); + let mut state_block = state.block(false); let instructions: [InstructionBox; 0] = []; let tx = TransactionBuilder::new(chain_id.clone(), ALICE_ID.clone()) .with_instructions(instructions) .sign(&ALICE_KEYS); - let tx_limits = &wsv.transaction_executor().transaction_limits; + let tx_limits = &state_block.transaction_executor().transaction_limits; let va_tx = AcceptedTransaction::accept(tx, &chain_id, tx_limits)?; let topology = Topology::new(UniqueVec::new()); let vcb = BlockBuilder::new(vec![va_tx.clone()], topology.clone(), Vec::new()) - .chain(0, &mut wsv) + .chain(0, &mut state_block) .sign(&ALICE_KEYS) .commit(&topology) .expect("Block is valid"); - wsv.apply(&vcb)?; + state_block.apply(&vcb)?; kura.store_block(vcb); + state_block.commit(); + + let state_view = state.view(); + let state_snapshot = state_view.to_snapshot(); let unapplied_tx = TransactionBuilder::new(chain_id, ALICE_ID.clone()) .with_instructions([Unregister::account("account@domain".parse().unwrap())]) .sign(&ALICE_KEYS); let wrong_hash = unapplied_tx.hash(); - let not_found = FindTransactionByHash::new(wrong_hash).execute(&wsv); + let not_found = FindTransactionByHash::new(wrong_hash).execute(&state_snapshot); assert!(matches!( not_found, Err(Error::Find(FindError::Transaction(_))) )); - let found_accepted = FindTransactionByHash::new(va_tx.as_ref().hash()).execute(&wsv)?; + let found_accepted = + FindTransactionByHash::new(va_tx.as_ref().hash()).execute(&state_snapshot)?; if found_accepted.transaction.error.is_none() { assert_eq!( va_tx.as_ref().hash(), @@ -466,7 +501,7 @@ mod tests { #[test] async fn domain_metadata() -> Result<()> { let kura = Kura::blank_kura_for_testing(); - let wsv = { + let state = { let mut metadata = Metadata::new(); metadata.insert_with_limits( Name::from_str("Bytes")?, @@ -486,12 +521,13 @@ mod tests { ) .is_none()); let query_handle = LiveQueryStore::test().start(); - WorldStateView::new(World::with([domain], PeersIds::new()), kura, query_handle) + State::new(World::with([domain], PeersIds::new()), kura, query_handle) }; let domain_id = DomainId::from_str("wonderland")?; let key = Name::from_str("Bytes")?; - let bytes = FindDomainKeyValueByIdAndKey::new(domain_id, key).execute(&wsv)?; + let bytes = FindDomainKeyValueByIdAndKey::new(domain_id, key) + .execute(&state.view().to_snapshot())?; assert_eq!( MetadataValueBox::Vec(vec![1_u32.into(), 2_u32.into(), 3_u32.into()]), bytes, diff --git a/core/src/smartcontracts/isi/triggers/mod.rs b/core/src/smartcontracts/isi/triggers/mod.rs index 4205286f724..3a1f4cc1114 100644 --- a/core/src/smartcontracts/isi/triggers/mod.rs +++ b/core/src/smartcontracts/isi/triggers/mod.rs @@ -22,7 +22,11 @@ pub mod isi { impl Execute for Register> { #[metrics(+"register_trigger")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let new_trigger = self.object; if !new_trigger.action.filter.mintable() { @@ -34,8 +38,8 @@ pub mod isi { } } - let engine = wsv.engine.clone(); // Cloning engine is cheap - let triggers = wsv.triggers_mut(); + let engine = state_transaction.engine.clone(); // Cloning engine is cheap + let triggers = &mut state_transaction.world.triggers; let trigger_id = new_trigger.id().clone(); let success = match &new_trigger.action.filter { TriggeringEventFilterBox::Data(_) => triggers.add_data_trigger( @@ -73,7 +77,9 @@ pub mod isi { .into()); } - wsv.emit_events(Some(TriggerEvent::Created(trigger_id))); + state_transaction + .world + .emit_events(Some(TriggerEvent::Created(trigger_id))); Ok(()) } @@ -81,12 +87,18 @@ pub mod isi { impl Execute for Unregister> { #[metrics(+"unregister_trigger")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let trigger_id = self.object_id.clone(); - let triggers = wsv.triggers_mut(); + let triggers = &mut state_transaction.world.triggers; if triggers.remove(&trigger_id) { - wsv.emit_events(Some(TriggerEvent::Deleted(self.object_id))); + state_transaction + .world + .emit_events(Some(TriggerEvent::Deleted(self.object_id))); Ok(()) } else { Err(RepetitionError { @@ -100,10 +112,14 @@ pub mod isi { impl Execute for Mint> { #[metrics(+"mint_trigger_repetitions")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let id = self.destination_id; - let triggers = wsv.triggers_mut(); + let triggers = &mut state_transaction.world.triggers; triggers .inspect_by_id(&id, |action| -> Result<(), Error> { if action.mintable() { @@ -119,12 +135,14 @@ pub mod isi { .ok_or(super::set::RepeatsOverflowError) })?; - wsv.emit_events(Some(TriggerEvent::Extended( - TriggerNumberOfExecutionsChanged { - trigger_id: id, - by: self.object, - }, - ))); + state_transaction + .world + .emit_events(Some(TriggerEvent::Extended( + TriggerNumberOfExecutionsChanged { + trigger_id: id, + by: self.object, + }, + ))); Ok(()) } @@ -132,21 +150,27 @@ pub mod isi { impl Execute for Burn> { #[metrics(+"burn_trigger_repetitions")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let trigger = self.destination_id; - let triggers = wsv.triggers_mut(); + let triggers = &mut state_transaction.world.triggers; triggers.mod_repeats(&trigger, |n| { n.checked_sub(self.object) .ok_or(super::set::RepeatsOverflowError) })?; // TODO: Is it okay to remove triggers with 0 repeats count from `TriggerSet` only // when they will match some of the events? - wsv.emit_events(Some(TriggerEvent::Shortened( - TriggerNumberOfExecutionsChanged { - trigger_id: trigger, - by: self.object, - }, - ))); + state_transaction + .world + .emit_events(Some(TriggerEvent::Shortened( + TriggerNumberOfExecutionsChanged { + trigger_id: trigger, + by: self.object, + }, + ))); Ok(()) } @@ -154,10 +178,16 @@ pub mod isi { impl Execute for ExecuteTrigger { #[metrics(+"execute_trigger")] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let id = &self.trigger_id; - wsv.triggers() + state_transaction + .world + .triggers .inspect_by_id(id, |action| -> Result<(), Error> { let allow_execute = if let TriggeringEventFilterBox::ExecuteTrigger(filter) = action.clone_and_box().filter @@ -184,7 +214,9 @@ pub mod isi { .ok_or_else(|| Error::Find(FindError::Trigger(id.clone()))) .and_then(core::convert::identity)?; - wsv.execute_trigger(id.clone(), authority); + state_transaction + .world + .execute_trigger(id.clone(), authority); Ok(()) } @@ -201,15 +233,15 @@ pub mod query { }; use super::*; - use crate::prelude::*; + use crate::{prelude::*, state::StateSnapshot}; impl ValidQuery for FindAllActiveTriggerIds { #[metrics(+"find_all_active_triggers")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { - Ok(Box::new(wsv.triggers().ids().cloned())) + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { + Ok(Box::new(state_snapshot.world.triggers.ids().cloned())) } } @@ -217,18 +249,22 @@ pub mod query { #[metrics(+"find_trigger_by_id")] fn execute( &self, - wsv: &WorldStateView, + state_snapshot: &StateSnapshot<'_>, ) -> Result, Error> { let id = &self.id; iroha_logger::trace!(%id); // Can't use just `LoadedActionTrait::clone_and_box` cause this will trigger lifetime mismatch #[allow(clippy::redundant_closure_for_method_calls)] - let loaded_action = wsv - .triggers() + let loaded_action = state_snapshot + .world + .triggers .inspect_by_id(id, |action| action.clone_and_box()) .ok_or_else(|| Error::Find(FindError::Trigger(id.clone())))?; - let action = wsv.triggers().get_original_action(loaded_action); + let action = state_snapshot + .world + .triggers + .get_original_action(loaded_action); // TODO: Should we redact the metadata if the account is not the authority/owner? Ok(Trigger::new(id.clone(), action)) @@ -237,11 +273,13 @@ pub mod query { impl ValidQuery for FindTriggerKeyValueByIdAndKey { #[metrics(+"find_trigger_key_value_by_id_and_key")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let id = &self.id; let key = &self.key; iroha_logger::trace!(%id, %key); - wsv.triggers() + state_snapshot + .world + .triggers .inspect_by_id(id, |action| { action .metadata() @@ -256,20 +294,22 @@ pub mod query { impl ValidQuery for FindTriggersByDomainId { #[metrics(+"find_triggers_by_domain_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> eyre::Result> + 'wsv>, Error> + state_snapshot: &'state StateSnapshot<'state>, + ) -> eyre::Result> + 'state>, Error> { let domain_id = &self.domain_id; Ok(Box::new( - wsv.triggers() + state_snapshot + .world + .triggers .inspect_by_domain_id(domain_id, |trigger_id, action| { (trigger_id.clone(), action.clone_and_box()) }) .map(|(trigger_id, action)| { - let action = wsv.triggers().get_original_action(action); + let action = state_snapshot.world.triggers.get_original_action(action); Trigger::new(trigger_id, action) }), )) diff --git a/core/src/smartcontracts/isi/triggers/set.rs b/core/src/smartcontracts/isi/triggers/set.rs index dc1587848bb..5ac76856ab4 100644 --- a/core/src/smartcontracts/isi/triggers/set.rs +++ b/core/src/smartcontracts/isi/triggers/set.rs @@ -28,7 +28,7 @@ use serde::{ }; use thiserror::Error; -use crate::{smartcontracts::wasm, wsv::WasmSeed}; +use crate::{smartcontracts::wasm, state::deserialize::WasmSeed}; /// Error type for [`Set`] operations. #[derive(Debug, Error, displaydoc::Display)] diff --git a/core/src/smartcontracts/isi/tx.rs b/core/src/smartcontracts/isi/tx.rs index ac282bd37b3..c6287918db3 100644 --- a/core/src/smartcontracts/isi/tx.rs +++ b/core/src/smartcontracts/isi/tx.rs @@ -16,6 +16,7 @@ use iroha_data_model::{ use iroha_telemetry::metrics; use super::*; +use crate::state::StateSnapshot; pub(crate) struct BlockTransactionIter(Arc, usize); pub(crate) struct BlockTransactionRef(Arc, usize); @@ -65,12 +66,13 @@ impl BlockTransactionRef { impl ValidQuery for FindAllTransactions { #[metrics(+"find_all_transactions")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, QueryExecutionFail> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, QueryExecutionFail> { Ok(Box::new( - wsv.all_blocks() + state_snapshot + .all_blocks() .flat_map(BlockTransactionIter::new) .map(|tx| TransactionQueryOutput { block_hash: tx.block_hash(), @@ -82,14 +84,15 @@ impl ValidQuery for FindAllTransactions { impl ValidQuery for FindTransactionsByAccountId { #[metrics(+"find_transactions_by_account_id")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, QueryExecutionFail> { + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, QueryExecutionFail> { let account_id = self.account_id.clone(); Ok(Box::new( - wsv.all_blocks() + state_snapshot + .all_blocks() .flat_map(BlockTransactionIter::new) .filter(move |tx| *tx.authority() == account_id) .map(|tx| TransactionQueryOutput { @@ -102,13 +105,17 @@ impl ValidQuery for FindTransactionsByAccountId { impl ValidQuery for FindTransactionByHash { #[metrics(+"find_transaction_by_hash")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute( + &self, + state_snapshot: &StateSnapshot<'_>, + ) -> Result { let tx_hash = self.hash; + iroha_logger::trace!(%tx_hash); - if !wsv.has_transaction(tx_hash) { + if !state_snapshot.has_transaction(tx_hash) { return Err(FindError::Transaction(tx_hash).into()); }; - let block = wsv + let block = state_snapshot .block_with_tx(&tx_hash) .ok_or_else(|| FindError::Transaction(tx_hash))?; diff --git a/core/src/smartcontracts/isi/world.rs b/core/src/smartcontracts/isi/world.rs index 1b768138e14..b309e40a4bc 100644 --- a/core/src/smartcontracts/isi/world.rs +++ b/core/src/smartcontracts/isi/world.rs @@ -29,10 +29,14 @@ pub mod isi { impl Execute for Register { #[metrics(+"register_peer")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let peer_id = self.object.id; - let world = wsv.world_mut(); + let world = &mut state_transaction.world; if !world.trusted_peers_ids.push(peer_id.clone()) { return Err(RepetitionError { instruction_type: InstructionType::Register, @@ -41,7 +45,7 @@ pub mod isi { .into()); } - wsv.emit_events(Some(PeerEvent::Added(peer_id))); + world.emit_events(Some(PeerEvent::Added(peer_id))); Ok(()) } @@ -49,16 +53,20 @@ pub mod isi { impl Execute for Unregister { #[metrics(+"unregister_peer")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let peer_id = self.object_id; - let world = wsv.world_mut(); + let world = &mut state_transaction.world; let Some(index) = world.trusted_peers_ids.iter().position(|id| id == &peer_id) else { return Err(FindError::Peer(peer_id).into()); }; world.trusted_peers_ids.remove(index); - wsv.emit_events(Some(PeerEvent::Removed(peer_id))); + world.emit_events(Some(PeerEvent::Removed(peer_id))); Ok(()) } @@ -66,17 +74,21 @@ pub mod isi { impl Execute for Register { #[metrics("register_domain")] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let domain: Domain = self.object.build(authority); let domain_id = domain.id().clone(); domain_id .name - .validate_len(wsv.config.ident_length_limits) + .validate_len(state_transaction.config.ident_length_limits) .map_err(Error::from)?; - let world = wsv.world_mut(); - if world.domains.contains_key(&domain_id) { + let world = &mut state_transaction.world; + if world.domains.get(&domain_id).is_some() { return Err(RepetitionError { instruction_type: InstructionType::Register, id: IdBox::DomainId(domain_id), @@ -86,7 +98,7 @@ pub mod isi { world.domains.insert(domain_id, domain.clone()); - wsv.emit_events(Some(DomainEvent::Created(domain))); + world.emit_events(Some(DomainEvent::Created(domain))); Ok(()) } @@ -94,15 +106,19 @@ pub mod isi { impl Execute for Unregister { #[metrics("unregister_domain")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let domain_id = self.object_id; - let world = wsv.world_mut(); - if world.domains.remove(&domain_id).is_none() { + let world = &mut state_transaction.world; + if world.domains.remove(domain_id.clone()).is_none() { return Err(FindError::Domain(domain_id).into()); } - wsv.emit_events(Some(DomainEvent::Deleted(domain_id))); + world.emit_events(Some(DomainEvent::Deleted(domain_id))); Ok(()) } @@ -110,10 +126,14 @@ pub mod isi { impl Execute for Register { #[metrics(+"register_role")] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let role = self.object.build(authority); - if wsv.roles().contains_key(role.id()) { + if state_transaction.world.roles.get(role.id()).is_some() { return Err(RepetitionError { instruction_type: InstructionType::Register, id: IdBox::RoleId(role.id), @@ -122,8 +142,9 @@ pub mod isi { } for permission in &role.permissions { - if !wsv - .permission_token_schema() + if !state_transaction + .world + .permission_token_schema .token_ids .contains(&permission.definition_id) { @@ -131,11 +152,11 @@ pub mod isi { } } - let world = wsv.world_mut(); + let world = &mut state_transaction.world; let role_id = role.id().clone(); world.roles.insert(role_id, role.clone()); - wsv.emit_events(Some(RoleEvent::Created(role))); + world.emit_events(Some(RoleEvent::Created(role))); Ok(()) } @@ -143,13 +164,18 @@ pub mod isi { impl Execute for Unregister { #[metrics("unregister_role")] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let role_id = self.object_id; - let accounts_with_role = wsv + let accounts_with_role = state_transaction .world .account_roles .iter() + .map(|(role, ())| role) .filter(|role| role.role_id.eq(&role_id)) .map(|role| &role.account_id) .cloned() @@ -160,15 +186,15 @@ pub mod isi { object: role_id.clone(), destination_id: account_id, }; - revoke.execute(authority, wsv)? + revoke.execute(authority, state_transaction)? } - let world = wsv.world_mut(); - if world.roles.remove(&role_id).is_none() { + let world = &mut state_transaction.world; + if world.roles.remove(role_id.clone()).is_none() { return Err(FindError::Role(role_id).into()); } - wsv.emit_events(Some(RoleEvent::Deleted(role_id))); + world.emit_events(Some(RoleEvent::Deleted(role_id))); Ok(()) } @@ -176,20 +202,25 @@ pub mod isi { impl Execute for Grant { #[metrics(+"grant_role_permission")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let role_id = self.destination_id; let permission_token = self.object; let permission_token_id = permission_token.definition_id.clone(); - if !wsv - .permission_token_schema() + if !state_transaction + .world + .permission_token_schema .token_ids .contains(&permission_token_id) { return Err(FindError::PermissionToken(permission_token_id).into()); } - let Some(role) = wsv.world.roles.get_mut(&role_id) else { + let Some(role) = state_transaction.world.roles.get_mut(&role_id) else { return Err(FindError::Role(role_id).into()); }; @@ -201,10 +232,12 @@ pub mod isi { .into()); } - wsv.emit_events(Some(RoleEvent::PermissionAdded(RolePermissionChanged { - role_id, - permission_token_id, - }))); + state_transaction + .world + .emit_events(Some(RoleEvent::PermissionAdded(RolePermissionChanged { + role_id, + permission_token_id, + }))); Ok(()) } @@ -212,12 +245,16 @@ pub mod isi { impl Execute for Revoke { #[metrics(+"grant_role_permission")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let role_id = self.destination_id; let permission_token = self.object; let permission_token_id = permission_token.definition_id.clone(); - let Some(role) = wsv.world.roles.get_mut(&role_id) else { + let Some(role) = state_transaction.world.roles.get_mut(&role_id) else { return Err(FindError::Role(role_id).into()); }; @@ -225,10 +262,12 @@ pub mod isi { return Err(FindError::PermissionToken(permission_token_id).into()); } - wsv.emit_events(Some(RoleEvent::PermissionRemoved(RolePermissionChanged { - role_id, - permission_token_id, - }))); + state_transaction + .world + .emit_events(Some(RoleEvent::PermissionRemoved(RolePermissionChanged { + role_id, + permission_token_id, + }))); Ok(()) } @@ -236,18 +275,22 @@ pub mod isi { impl Execute for SetParameter { #[metrics(+"set_parameter")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let parameter = self.parameter; let parameter_id = parameter.id.clone(); - let world = wsv.world_mut(); + let world = &mut state_transaction.world; if !world.parameters.remove(¶meter) { return Err(FindError::Parameter(parameter_id).into()); } world.parameters.insert(parameter); - wsv.emit_events(Some(ConfigurationEvent::Changed(parameter_id))); + world.emit_events(Some(ConfigurationEvent::Changed(parameter_id))); Ok(()) } @@ -255,11 +298,15 @@ pub mod isi { impl Execute for NewParameter { #[metrics(+"new_parameter")] - fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + _authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let parameter = self.parameter; let parameter_id = parameter.id.clone(); - let world = wsv.world_mut(); + let world = &mut state_transaction.world; if !world.parameters.insert(parameter) { return Err(RepetitionError { instruction_type: InstructionType::NewParameter, @@ -268,7 +315,7 @@ pub mod isi { .into()); } - wsv.emit_events(Some(ConfigurationEvent::Created(parameter_id))); + world.emit_events(Some(ConfigurationEvent::Created(parameter_id))); Ok(()) } @@ -276,14 +323,18 @@ pub mod isi { impl Execute for Upgrade { #[metrics(+"upgrade_executor")] - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> { + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error> { let raw_executor = self.executor; - // Cloning executor to avoid multiple mutable borrows of `wsv`. + // Cloning executor to avoid multiple mutable borrows of `state_transaction`. // Also it's a cheap operation. - let mut upgraded_executor = wsv.executor().clone(); + let mut upgraded_executor = state_transaction.world.executor.clone(); upgraded_executor - .migrate(raw_executor, wsv, authority) + .migrate(raw_executor, state_transaction, authority) .map_err(|migration_error| { InvalidParameterError::Wasm(format!( "{:?}", @@ -291,9 +342,11 @@ pub mod isi { )) })?; - wsv.world_mut().executor = upgraded_executor; + *state_transaction.world.executor.get_mut() = upgraded_executor; - wsv.emit_events(std::iter::once(ExecutorEvent::Upgraded)); + state_transaction + .world + .emit_events(std::iter::once(ExecutorEvent::Upgraded)); Ok(()) } @@ -303,7 +356,7 @@ pub mod isi { fn execute( self, _authority: &AccountId, - _wsv: &mut WorldStateView, + _state_transaction: &mut StateTransaction<'_, '_>, ) -> std::result::Result<(), Error> { const TARGET: &str = "log_isi"; let Self { level, msg } = self; @@ -333,40 +386,51 @@ pub mod query { }; use super::*; + use crate::state::StateSnapshot; impl ValidQuery for FindAllRoles { #[metrics(+"find_all_roles")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { - Ok(Box::new(wsv.world.roles.values().cloned())) + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { + Ok(Box::new( + state_snapshot + .world + .roles + .iter() + .map(|(_, role)| role) + .cloned(), + )) } } impl ValidQuery for FindAllRoleIds { #[metrics(+"find_all_role_ids")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { - Ok(Box::new(wsv + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { + Ok(Box::new( + state_snapshot .world .roles - .values() + .iter() + .map(|(_, role)| role) // To me, this should probably be a method, not a field. .map(Role::id) - .cloned())) + .cloned(), + )) } } impl ValidQuery for FindRoleByRoleId { #[metrics(+"find_role_by_role_id")] - fn execute(&self, wsv: &WorldStateView) -> Result { + fn execute(&self, state_snapshot: &StateSnapshot<'_>) -> Result { let role_id = &self.id; iroha_logger::trace!(%role_id); - wsv.world.roles.get(role_id).map_or_else( + state_snapshot.world.roles.get(role_id).map_or_else( || Err(Error::Find(FindError::Role(role_id.clone()))), |role_ref| Ok(role_ref.clone()), ) @@ -375,28 +439,33 @@ pub mod query { impl ValidQuery for FindAllPeers { #[metrics("find_all_peers")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { - Ok(Box::new(wsv.peers().cloned().map(Peer::new))) + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { + Ok(Box::new( + state_snapshot.world.peers().cloned().map(Peer::new), + )) } } impl ValidQuery for FindPermissionTokenSchema { #[metrics("find_permission_token_schema")] - fn execute(&self, wsv: &WorldStateView) -> Result { - Ok(wsv.permission_token_schema().clone()) + fn execute( + &self, + state_snapshot: &StateSnapshot<'_>, + ) -> Result { + Ok(state_snapshot.world.permission_token_schema.clone()) } } impl ValidQuery for FindAllParameters { #[metrics("find_all_parameters")] - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result + 'wsv>, Error> { - Ok(Box::new(wsv.parameters().cloned())) + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result + 'state>, Error> { + Ok(Box::new(state_snapshot.world.parameters().cloned())) } } } diff --git a/core/src/smartcontracts/mod.rs b/core/src/smartcontracts/mod.rs index 35587c6580b..c724b9adb4c 100644 --- a/core/src/smartcontracts/mod.rs +++ b/core/src/smartcontracts/mod.rs @@ -1,6 +1,6 @@ //! Iroha smart contract functionality. Most of the traits mentioned //! [`isi`] or Iroha Special Instructions are the main way of -//! interacting with the [`WorldStateView`], even [`wasm`] based +//! interacting with the [`State`], even [`wasm`] based //! smart-contracts can only interact with the `world`, via //! instructions. @@ -13,15 +13,19 @@ use iroha_data_model::{ pub use isi::*; use self::query::Lazy; -use crate::wsv::WorldStateView; +use crate::state::{StateSnapshot, StateTransaction}; -/// Trait implementations should provide actions to apply changes on [`WorldStateView`]. +/// Trait implementations should provide actions to apply changes on [`StateTransaction`]. pub trait Execute { - /// Apply actions to `wsv` on behalf of `authority`. + /// Apply actions to `state_transaction` on behalf of `authority`. /// /// # Errors /// Concrete to each implementer. - fn execute(self, authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error>; + fn execute( + self, + authority: &AccountId, + state_transaction: &mut StateTransaction<'_, '_>, + ) -> Result<(), Error>; } /// This trait should be implemented for all Iroha Queries. @@ -29,15 +33,14 @@ pub trait ValidQuery: iroha_data_model::query::Query where Self::Output: Lazy, { - /// Execute query on the [`WorldStateView`]. - /// Should not mutate [`WorldStateView`]! + /// Execute query on the [`WorldSnapshot`]. /// /// Returns Ok(QueryResult) if succeeded and Err(String) if failed. /// /// # Errors /// Concrete to each implementer - fn execute<'wsv>( + fn execute<'state>( &self, - wsv: &'wsv WorldStateView, - ) -> Result<::Lazy<'wsv>, QueryExecutionFail>; + state_snapshot: &'state StateSnapshot<'state>, + ) -> Result<::Lazy<'state>, QueryExecutionFail>; } diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index 71ed2b8b92a..7f5f2efe739 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -2,6 +2,8 @@ //! `WebAssembly` VM Smartcontracts can be written in Rust, compiled //! to wasm format and submitted in a transaction +use std::borrow::Borrow; + use error::*; use import::traits::{ ExecuteOperations as _, GetExecutorPayloads as _, SetPermissionTokenSchema as _, @@ -30,7 +32,7 @@ use wasmtime::{ use crate::{ query::store::LiveQueryStoreHandle, smartcontracts::{wasm::state::ValidateQueryOperation, Execute}, - wsv::WorldStateView, + state::{StateSnapshot, StateTransaction}, ValidQuery as _, }; @@ -336,6 +338,7 @@ pub mod state { use derive_more::Constructor; use indexmap::IndexSet; + use self::chain_state::ConstState; use super::*; /// Construct [`StoreLimits`] from [`Configuration`] @@ -354,15 +357,15 @@ pub mod state { } /// State for most common operations. - /// Generic over borrowed [`WorldStateView`] type and specific executable state. + /// Generic over chain state type and specific executable state. pub struct CommonState { pub(super) authority: AccountId, pub(super) store_limits: StoreLimits, /// Span inside of which all logs are recorded for this smart contract pub(super) log_span: Span, pub(super) executed_queries: IndexSet, - /// Borrowed [`WorldStateView`] kind - pub(super) wsv: W, + /// State kind + pub(super) state: W, /// Concrete state for specific executable pub(super) specific_state: S, } @@ -373,7 +376,7 @@ pub mod state { authority: AccountId, config: Config, log_span: Span, - wsv: W, + state: W, specific_state: S, ) -> Self { Self { @@ -381,7 +384,7 @@ pub mod state { store_limits: store_limits_from_config(&config), log_span, executed_queries: IndexSet::new(), - wsv, + state, specific_state, } } @@ -406,34 +409,47 @@ pub mod state { ) -> Result<(), ValidationFail>; } - pub mod wsv { - //! Strongly typed kinds of borrowed [`WorldStateView`] + pub mod chain_state { + //! Strongly typed kinds of chain state + + use std::borrow::Borrow; use super::*; - /// Const reference to [`WorldStateView`]. - pub struct WithConst<'wrld>(pub(in super::super) &'wrld WorldStateView); + /// Read-only access to chain state. + pub struct WithConst<'wrld, 'state>(pub(in super::super) &'wrld StateSnapshot<'state>); - /// Mutable reference to [`WorldStateView`]. - pub struct WithMut<'wrld>(pub(in super::super) &'wrld mut WorldStateView); + /// Mutable access to chain state. + pub struct WithMut<'wrld, 'block, 'state>( + pub(in super::super) &'wrld mut StateTransaction<'block, 'state>, + ); - /// Trait to get immutable [`WorldStateView`] + /// Trait to get immutable [`StateSnapshot`] /// - /// Exists to write generic code for [`WithWsv`] and [`WithMutWsv`. - pub trait Wsv { - /// Get immutable [`WorldStateView`] - fn wsv(&self) -> &WorldStateView; + /// Exists to write generic code for [`WithMut`] and [`WithConst`]. + pub trait ConstState { + /// Type which can be borrowed into `[WorldStateSnapshot]` + type State<'wrld, 'state: 'wrld>: Borrow> + where + Self: 'state + 'wrld; + + /// Get immutable chain state. + fn state(&self) -> Self::State<'_, '_>; } - impl Wsv for WithConst<'_> { - fn wsv(&self) -> &WorldStateView { + impl ConstState for WithConst<'_, '_> { + type State<'wrld, 'state: 'wrld> = &'wrld StateSnapshot<'state> where Self: 'state; + + fn state(&self) -> &StateSnapshot<'_> { self.0 } } - impl Wsv for WithMut<'_> { - fn wsv(&self) -> &WorldStateView { - self.0 + impl ConstState for WithMut<'_, '_, '_> { + type State<'wrld, 'state: 'wrld> = StateSnapshot<'state> where Self: 'state; + + fn state(&self) -> StateSnapshot<'_> { + self.0.to_snapshot() } } } @@ -490,30 +506,38 @@ pub mod state { } /// State for smart contract execution - pub type SmartContract<'wrld> = CommonState, specific::SmartContract>; + pub type SmartContract<'wrld, 'block, 'state> = + CommonState, specific::SmartContract>; /// State for trigger execution - pub type Trigger<'wrld> = CommonState, specific::Trigger>; + pub type Trigger<'wrld, 'block, 'state> = + CommonState, specific::Trigger>; - impl ValidateQueryOperation for SmartContract<'_> { + impl ValidateQueryOperation for SmartContract<'_, '_, '_> { fn validate_query( &self, authority: &AccountId, query: QueryBox, ) -> Result<(), ValidationFail> { - let wsv: &WorldStateView = self.wsv.0; - wsv.executor().validate_query(wsv, authority, query) + let state_snapshot = self.state.state(); + state_snapshot + .world + .executor + .validate_query(&state_snapshot, authority, query) } } - impl ValidateQueryOperation for Trigger<'_> { + impl ValidateQueryOperation for Trigger<'_, '_, '_> { fn validate_query( &self, authority: &AccountId, query: QueryBox, ) -> Result<(), ValidationFail> { - let wsv: &WorldStateView = self.wsv.0; - wsv.executor().validate_query(wsv, authority, query) + let state_snapshot = self.state.state(); + state_snapshot + .world + .executor + .validate_query(&state_snapshot, authority, query) } } @@ -523,23 +547,28 @@ pub mod state { use super::*; /// State for executing `validate_transaction()` entrypoint - pub type ValidateTransaction<'wrld> = - CommonState, specific::executor::ValidateTransaction>; + pub type ValidateTransaction<'wrld, 'block, 'state> = CommonState< + chain_state::WithMut<'wrld, 'block, 'state>, + specific::executor::ValidateTransaction, + >; /// State for executing `validate_query()` entrypoint - pub type ValidateQuery<'wrld> = - CommonState, specific::executor::ValidateQuery>; + pub type ValidateQuery<'wrld, 'state> = + CommonState, specific::executor::ValidateQuery>; /// State for executing `validate_instruction()` entrypoint - pub type ValidateInstruction<'wrld> = - CommonState, specific::executor::ValidateInstruction>; + pub type ValidateInstruction<'wrld, 'block, 'state> = CommonState< + chain_state::WithMut<'wrld, 'block, 'state>, + specific::executor::ValidateInstruction, + >; /// State for executing `migrate()` entrypoint - pub type Migrate<'wrld> = CommonState, specific::executor::Migrate>; + pub type Migrate<'wrld, 'block, 'state> = + CommonState, specific::executor::Migrate>; macro_rules! impl_blank_validate_operations { - ($($t:ident),+ $(,)?) => { $( - impl ValidateQueryOperation for $t <'_> { + ($($t:ty),+ $(,)?) => { $( + impl ValidateQueryOperation for $t { fn validate_query( &self, _authority: &AccountId, @@ -552,10 +581,10 @@ pub mod state { } impl_blank_validate_operations!( - ValidateTransaction, - ValidateInstruction, - ValidateQuery, - Migrate, + ValidateTransaction<'_, '_, '_>, + ValidateInstruction<'_, '_, '_>, + ValidateQuery<'_, '_>, + Migrate<'_, '_, '_>, ); } } @@ -720,7 +749,7 @@ impl Runtime> { } } -impl Runtime> { +impl Runtime> { fn execute_executor_validate_internal( &self, module: &wasmtime::Module, @@ -748,14 +777,14 @@ impl Runtime> { let mut state = store.into_data(); let executed_queries = state.take_executed_queries(); - forget_all_executed_queries(state.wsv.wsv().query_handle(), executed_queries)?; + forget_all_executed_queries(state.state.state().borrow().query_handle, executed_queries)?; Ok(validation_res) } } impl Runtime> where - W: state::wsv::Wsv, + W: state::chain_state::ConstState, state::CommonState: state::ValidateQueryOperation, { fn default_execute_query( @@ -772,11 +801,13 @@ where fetch_size, }) => { let batched = { - let wsv = &state.wsv.wsv(); + let state_snapshot = state.state.state(); + let state_snapshot = state_snapshot.borrow(); state.validate_query(&state.authority, query.clone())?; - let output = query.execute(wsv)?; + let output = query.execute(state_snapshot)?; - wsv.query_handle() + state_snapshot + .query_handle .handle_query_output(output, &sorting, pagination, fetch_size) }?; match &batched { @@ -794,17 +825,24 @@ where if let Some(query_id) = &cursor.query_id { state.executed_queries.insert(query_id.clone()); } - state.wsv.wsv().query_handle().handle_query_cursor(cursor) + state + .state + .state() + .borrow() + .query_handle + .handle_query_cursor(cursor) } } .map_err(Into::into) } } -impl<'wrld, S> Runtime, S>> { +impl<'wrld, 'state, 'block, S> + Runtime, S>> +{ fn default_execute_instruction( instruction: InstructionBox, - state: &mut state::CommonState, S>, + state: &mut state::CommonState, S>, ) -> Result<(), ValidationFail> { debug!(%instruction, "Executing"); @@ -813,14 +851,14 @@ impl<'wrld, S> Runtime, S>> { // is validated and then it's executed. Here it's validating in both steps. // Add a flag indicating whether smart contract is being validated or executed let authority = state.authority.clone(); - let wsv: &mut WorldStateView = state.wsv.0; - wsv.executor() - .clone() // Cloning executor is a cheap operation - .validate_instruction(wsv, &authority, instruction) + state.state.0.world + .executor + .clone() // Cloning executor is a cheap operation + .validate_instruction(state.state.0, &authority, instruction) } } -impl<'wrld> Runtime> { +impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime> { /// Executes the given wasm smartcontract /// /// # Errors @@ -830,7 +868,7 @@ impl<'wrld> Runtime> { /// - if the execution of the smartcontract fails pub fn execute( &mut self, - wsv: &'wrld mut WorldStateView, + state_transaction: &'wrld mut StateTransaction<'block, 'state>, authority: AccountId, bytes: impl AsRef<[u8]>, ) -> Result<()> { @@ -839,7 +877,7 @@ impl<'wrld> Runtime> { authority, self.config, span, - state::wsv::WithMut(wsv), + state::chain_state::WithMut(state_transaction), state::specific::SmartContract::new(None), ); @@ -855,7 +893,7 @@ impl<'wrld> Runtime> { /// - if execution of the smartcontract fails (check [`Self::execute`]) pub fn validate( &mut self, - wsv: &'wrld mut WorldStateView, + state_transaction: &'wrld mut StateTransaction<'block, 'state>, authority: AccountId, bytes: impl AsRef<[u8]>, max_instruction_count: u64, @@ -865,7 +903,7 @@ impl<'wrld> Runtime> { authority, self.config, span, - state::wsv::WithMut(wsv), + state::chain_state::WithMut(state_transaction), state::specific::SmartContract::new(Some(LimitsExecutor::new(max_instruction_count))), ); @@ -875,7 +913,7 @@ impl<'wrld> Runtime> { fn execute_smart_contract_with_state( &mut self, bytes: impl AsRef<[u8]>, - state: state::SmartContract<'wrld>, + state: state::SmartContract<'wrld, 'block, 'state>, ) -> Result<()> { let mut store = self.create_store(state); let smart_contract = self.create_smart_contract(&mut store, bytes)?; @@ -889,7 +927,7 @@ impl<'wrld> Runtime> { .map_err(ExportFnCallError::from)?; let mut state = store.into_data(); let executed_queries = state.take_executed_queries(); - forget_all_executed_queries(state.wsv.0.query_handle(), executed_queries) + forget_all_executed_queries(state.state.0.query_handle, executed_queries) } #[codec::wrap] @@ -900,13 +938,14 @@ impl<'wrld> Runtime> { } } -impl<'wrld> import::traits::ExecuteOperations> - for Runtime> +impl<'wrld, 'block, 'state> + import::traits::ExecuteOperations> + for Runtime> { #[codec::wrap] fn execute_query( query_request: SmartContractQueryRequest, - state: &mut state::SmartContract<'wrld>, + state: &mut state::SmartContract<'wrld, 'block, 'state>, ) -> Result, ValidationFail> { Self::default_execute_query(query_request, state) } @@ -914,7 +953,7 @@ impl<'wrld> import::traits::ExecuteOperations> #[codec::wrap] fn execute_instruction( instruction: InstructionBox, - state: &mut state::SmartContract<'wrld>, + state: &mut state::SmartContract<'wrld, 'block, 'state>, ) -> Result<(), ValidationFail> { if let Some(limits_executor) = state.specific_state.limits_executor.as_mut() { limits_executor.check_instruction_limits()?; @@ -924,7 +963,7 @@ impl<'wrld> import::traits::ExecuteOperations> } } -impl<'wrld> Runtime> { +impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime> { /// Executes the given wasm trigger module /// /// # Errors @@ -933,7 +972,7 @@ impl<'wrld> Runtime> { /// - if the execution of the smartcontract fails pub fn execute_trigger_module( &mut self, - wsv: &'wrld mut WorldStateView, + state_transaction: &'wrld mut StateTransaction<'block, 'state>, id: &TriggerId, authority: AccountId, module: &wasmtime::Module, @@ -944,7 +983,7 @@ impl<'wrld> Runtime> { authority, self.config, span, - state::wsv::WithMut(wsv), + state::chain_state::WithMut(state_transaction), state::specific::Trigger::new(event), ); @@ -960,7 +999,7 @@ impl<'wrld> Runtime> { let mut state = store.into_data(); let executed_queries = state.take_executed_queries(); - forget_all_executed_queries(state.wsv.0.query_handle(), executed_queries) + forget_all_executed_queries(state.state.0.query_handle, executed_queries) } #[codec::wrap] @@ -972,13 +1011,13 @@ impl<'wrld> Runtime> { } } -impl<'wrld> import::traits::ExecuteOperations> - for Runtime> +impl<'wrld, 'block, 'state> import::traits::ExecuteOperations> + for Runtime> { #[codec::wrap] fn execute_query( query_request: SmartContractQueryRequest, - state: &mut state::Trigger<'wrld>, + state: &mut state::Trigger<'wrld, 'block, 'state>, ) -> Result, ValidationFail> { Self::default_execute_query(query_request, state) } @@ -986,7 +1025,7 @@ impl<'wrld> import::traits::ExecuteOperations> #[codec::wrap] fn execute_instruction( instruction: InstructionBox, - state: &mut state::Trigger<'wrld>, + state: &mut state::Trigger<'wrld, 'block, 'state>, ) -> Result<(), ValidationFail> { Self::default_execute_instruction(instruction, state) } @@ -995,19 +1034,24 @@ impl<'wrld> import::traits::ExecuteOperations> /// Marker trait to auto-implement [`import_traits::ExecuteOperations`] for a concrete /// *Executor* [`Runtime`]. /// -/// *Mut* means that [`WorldStateView`] will be mutated. +/// *Mut* means that chain state can be mutated. trait ExecuteOperationsAsExecutorMut {} -impl<'wrld, R, S> - import::traits::ExecuteOperations, S>> for R +impl<'wrld, 'block, 'state, R, S> + import::traits::ExecuteOperations< + state::CommonState, S>, + > for R where - R: ExecuteOperationsAsExecutorMut, S>>, - state::CommonState, S>: state::ValidateQueryOperation, + R: ExecuteOperationsAsExecutorMut< + state::CommonState, S>, + >, + state::CommonState, S>: + state::ValidateQueryOperation, { #[codec::wrap] fn execute_query( query_request: SmartContractQueryRequest, - state: &mut state::CommonState, S>, + state: &mut state::CommonState, S>, ) -> Result, ValidationFail> { debug!(%query_request, "Executing as executor"); @@ -1017,12 +1061,12 @@ where #[codec::wrap] fn execute_instruction( instruction: InstructionBox, - state: &mut state::CommonState, S>, + state: &mut state::CommonState, S>, ) -> Result<(), ValidationFail> { debug!(%instruction, "Executing as executor"); instruction - .execute(&state.authority.clone(), state.wsv.0) + .execute(&state.authority.clone(), state.state.0) .map_err(Into::into) } } @@ -1049,7 +1093,7 @@ where } } -impl<'wrld> Runtime> { +impl<'wrld, 'block, 'state> Runtime> { /// Execute `validate_transaction()` entrypoint of the given module of runtime executor /// /// # Errors @@ -1060,7 +1104,7 @@ impl<'wrld> Runtime> { /// - if unable to decode [`executor::Result`] pub fn execute_executor_validate_transaction( &self, - wsv: &'wrld mut WorldStateView, + state_transaction: &'wrld mut StateTransaction<'block, 'state>, authority: &AccountId, module: &wasmtime::Module, transaction: SignedTransaction, @@ -1073,7 +1117,7 @@ impl<'wrld> Runtime> { authority.clone(), self.config, span, - state::wsv::WithMut(wsv), + state::chain_state::WithMut(state_transaction), state::specific::executor::ValidateTransaction::new(transaction), ), import::EXECUTOR_VALIDATE_TRANSACTION, @@ -1081,54 +1125,55 @@ impl<'wrld> Runtime> { } } -impl<'wrld> ExecuteOperationsAsExecutorMut> - for Runtime> +impl<'wrld> ExecuteOperationsAsExecutorMut> + for Runtime> { } -impl<'wrld> import::traits::GetExecutorPayloads> - for Runtime> +impl<'wrld, 'block, 'state> + import::traits::GetExecutorPayloads> + for Runtime> { #[codec::wrap] fn get_migrate_payload( - _state: &state::executor::ValidateTransaction<'wrld>, + _state: &state::executor::ValidateTransaction<'wrld, 'block, 'state>, ) -> payloads::Migrate { panic!("Executor `validate_transaction()` entrypoint should not query payload for `migrate()` entrypoint") } #[codec::wrap] fn get_validate_transaction_payload( - state: &state::executor::ValidateTransaction<'wrld>, + state: &state::executor::ValidateTransaction<'wrld, 'block, 'state>, ) -> Validate { Validate { authority: state.authority.clone(), - block_height: state.wsv.0.height(), + block_height: state.state.0.height(), target: state.specific_state.to_validate.clone(), } } #[codec::wrap] fn get_validate_instruction_payload( - _state: &state::executor::ValidateTransaction<'wrld>, + _state: &state::executor::ValidateTransaction<'wrld, 'block, 'state>, ) -> Validate { panic!("Executor `validate_transaction()` entrypoint should not query payload for `validate_instruction()` entrypoint") } #[codec::wrap] fn get_validate_query_payload( - _state: &state::executor::ValidateTransaction<'wrld>, + _state: &state::executor::ValidateTransaction<'wrld, 'block, 'state>, ) -> Validate { panic!("Executor `validate_transaction()` entrypoint should not query payload for `validate_query()` entrypoint") } } -impl<'wrld> FakeSetPermissionTokenSchema> - for Runtime> +impl<'wrld> FakeSetPermissionTokenSchema> + for Runtime> { const ENTRYPOINT_FN_NAME: &'static str = "validate_transaction"; } -impl<'wrld> Runtime> { +impl<'wrld, 'block, 'state> Runtime> { /// Execute `validate_instruction()` entrypoint of the given module of runtime executor /// /// # Errors @@ -1139,7 +1184,7 @@ impl<'wrld> Runtime> { /// - if unable to decode [`executor::Result`] pub fn execute_executor_validate_instruction( &self, - wsv: &'wrld mut WorldStateView, + state_transaction: &'wrld mut StateTransaction<'block, 'state>, authority: &AccountId, module: &wasmtime::Module, instruction: InstructionBox, @@ -1152,7 +1197,7 @@ impl<'wrld> Runtime> { authority.clone(), self.config, span, - state::wsv::WithMut(wsv), + state::chain_state::WithMut(state_transaction), state::specific::executor::ValidateInstruction::new(instruction), ), import::EXECUTOR_VALIDATE_INSTRUCTION, @@ -1160,54 +1205,55 @@ impl<'wrld> Runtime> { } } -impl<'wrld> ExecuteOperationsAsExecutorMut> - for Runtime> +impl<'wrld> ExecuteOperationsAsExecutorMut> + for Runtime> { } -impl<'wrld> import::traits::GetExecutorPayloads> - for Runtime> +impl<'wrld, 'block, 'state> + import::traits::GetExecutorPayloads> + for Runtime> { #[codec::wrap] fn get_migrate_payload( - _state: &state::executor::ValidateInstruction<'wrld>, + _state: &state::executor::ValidateInstruction<'wrld, 'block, 'state>, ) -> payloads::Migrate { panic!("Executor `validate_instruction()` entrypoint should not query payload for `migrate()` entrypoint") } #[codec::wrap] fn get_validate_transaction_payload( - _state: &state::executor::ValidateInstruction<'wrld>, + _state: &state::executor::ValidateInstruction<'wrld, 'block, 'state>, ) -> Validate { panic!("Executor `validate_instruction()` entrypoint should not query payload for `validate_transaction()` entrypoint") } #[codec::wrap] fn get_validate_instruction_payload( - state: &state::executor::ValidateInstruction<'wrld>, + state: &state::executor::ValidateInstruction<'wrld, 'block, 'state>, ) -> Validate { Validate { authority: state.authority.clone(), - block_height: state.wsv.0.height(), + block_height: state.state.0.height(), target: state.specific_state.to_validate.clone(), } } #[codec::wrap] fn get_validate_query_payload( - _state: &state::executor::ValidateInstruction<'wrld>, + _state: &state::executor::ValidateInstruction<'wrld, 'block, 'state>, ) -> Validate { panic!("Executor `validate_instruction()` entrypoint should not query payload for `validate_query()` entrypoint") } } -impl<'wrld> FakeSetPermissionTokenSchema> - for Runtime> +impl<'wrld> FakeSetPermissionTokenSchema> + for Runtime> { const ENTRYPOINT_FN_NAME: &'static str = "validate_instruction"; } -impl<'wrld> Runtime> { +impl<'wrld, 'state> Runtime> { /// Execute `validate_query()` entrypoint of the given module of runtime executor /// /// # Errors @@ -1218,7 +1264,7 @@ impl<'wrld> Runtime> { /// - if unable to decode [`executor::Result`] pub fn execute_executor_validate_query( &self, - wsv: &'wrld WorldStateView, + state_transaction: &'wrld StateSnapshot<'state>, authority: &AccountId, module: &wasmtime::Module, query: QueryBox, @@ -1231,7 +1277,7 @@ impl<'wrld> Runtime> { authority.clone(), self.config, span, - state::wsv::WithConst(wsv), + state::chain_state::WithConst(state_transaction), state::specific::executor::ValidateQuery::new(query), ), import::EXECUTOR_VALIDATE_QUERY, @@ -1239,13 +1285,13 @@ impl<'wrld> Runtime> { } } -impl<'wrld> import::traits::ExecuteOperations> - for Runtime> +impl<'wrld, 'state> import::traits::ExecuteOperations> + for Runtime> { #[codec::wrap] fn execute_query( query_request: SmartContractQueryRequest, - state: &mut state::executor::ValidateQuery<'wrld>, + state: &mut state::executor::ValidateQuery<'wrld, 'state>, ) -> Result, ValidationFail> { debug!(%query_request, "Executing as executor"); @@ -1255,53 +1301,56 @@ impl<'wrld> import::traits::ExecuteOperations, + _state: &mut state::executor::ValidateQuery<'wrld, 'state>, ) -> Result<(), ValidationFail> { panic!("Executor `validate_query()` entrypoint should not execute instructions") } } -impl<'wrld> import::traits::GetExecutorPayloads> - for Runtime> +impl<'wrld, 'state> + import::traits::GetExecutorPayloads> + for Runtime> { #[codec::wrap] - fn get_migrate_payload(_state: &state::executor::ValidateQuery<'wrld>) -> payloads::Migrate { + fn get_migrate_payload( + _state: &state::executor::ValidateQuery<'wrld, 'state>, + ) -> payloads::Migrate { panic!("Executor `validate_query()` entrypoint should not query payload for `migrate()` entrypoint") } #[codec::wrap] fn get_validate_transaction_payload( - _state: &state::executor::ValidateQuery<'wrld>, + _state: &state::executor::ValidateQuery<'wrld, 'state>, ) -> Validate { panic!("Executor `validate_query()` entrypoint should not query payload for `validate_transaction()` entrypoint") } #[codec::wrap] fn get_validate_instruction_payload( - _state: &state::executor::ValidateQuery<'wrld>, + _state: &state::executor::ValidateQuery<'wrld, 'state>, ) -> Validate { panic!("Executor `validate_query()` entrypoint should not query payload for `validate_instruction()` entrypoint") } #[codec::wrap] fn get_validate_query_payload( - state: &state::executor::ValidateQuery<'wrld>, + state: &state::executor::ValidateQuery<'wrld, 'state>, ) -> Validate { Validate { authority: state.authority.clone(), - block_height: state.wsv.0.height(), + block_height: state.state.0.height(), target: state.specific_state.to_validate.clone(), } } } -impl<'wrld> FakeSetPermissionTokenSchema> - for Runtime> +impl<'wrld, 'state> FakeSetPermissionTokenSchema> + for Runtime> { const ENTRYPOINT_FN_NAME: &'static str = "validate_query"; } -impl<'wrld> Runtime> { +impl<'wrld, 'block, 'state> Runtime> { /// Execute `migrate()` entrypoint of *Executor* /// /// # Errors @@ -1312,7 +1361,7 @@ impl<'wrld> Runtime> { /// - if failed to decode [`MigrationResult`] pub fn execute_executor_migration( &self, - wsv: &'wrld mut WorldStateView, + state_transaction: &'wrld mut StateTransaction<'block, 'state>, authority: &AccountId, module: &wasmtime::Module, ) -> Result { @@ -1321,7 +1370,7 @@ impl<'wrld> Runtime> { authority.clone(), self.config, span, - state::wsv::WithMut(wsv), + state::chain_state::WithMut(state_transaction), state::specific::executor::Migrate, ); @@ -1344,8 +1393,8 @@ impl<'wrld> Runtime> { } } -impl<'wrld> ExecuteOperationsAsExecutorMut> - for Runtime> +impl<'wrld> ExecuteOperationsAsExecutorMut> + for Runtime> { } @@ -1358,47 +1407,53 @@ impl<'wrld> ExecuteOperationsAsExecutorMut> /// /// Panics with error message if called, because it should never be called from /// `migrate()` entrypoint. -impl<'wrld> import::traits::GetExecutorPayloads> - for Runtime> +impl<'wrld, 'block, 'state> + import::traits::GetExecutorPayloads> + for Runtime> { #[codec::wrap] - fn get_migrate_payload(state: &state::executor::Migrate<'wrld>) -> payloads::Migrate { + fn get_migrate_payload( + state: &state::executor::Migrate<'wrld, 'block, 'state>, + ) -> payloads::Migrate { payloads::Migrate { - block_height: state.wsv.0.height(), + block_height: state.state.0.height(), } } #[codec::wrap] fn get_validate_transaction_payload( - _state: &state::executor::Migrate<'wrld>, + _state: &state::executor::Migrate<'wrld, 'block, 'state>, ) -> Validate { panic!("Executor `migrate()` entrypoint should not query payload for `validate_transaction()` entrypoint") } #[codec::wrap] fn get_validate_instruction_payload( - _state: &state::executor::Migrate<'wrld>, + _state: &state::executor::Migrate<'wrld, 'block, 'state>, ) -> Validate { panic!("Executor `migrate()` entrypoint should not query payload for `validate_instruction()` entrypoint") } #[codec::wrap] - fn get_validate_query_payload(_state: &state::executor::Migrate<'wrld>) -> Validate { + fn get_validate_query_payload( + _state: &state::executor::Migrate<'wrld, 'block, 'state>, + ) -> Validate { panic!("Executor `migrate()` entrypoint should not query payload for `validate_query()` entrypoint") } } -impl<'wrld> import::traits::SetPermissionTokenSchema> - for Runtime> +impl<'wrld, 'block, 'state> + import::traits::SetPermissionTokenSchema> + for Runtime> { #[codec::wrap] fn set_permission_token_schema( schema: PermissionTokenSchema, - state: &mut state::executor::Migrate<'wrld>, + state: &mut state::executor::Migrate<'wrld, 'block, 'state>, ) { debug!(%schema, "Setting permission token schema"); - state.wsv.0.set_permission_token_schema(schema) + state.state.0.world.set_permission_token_schema(schema) } } @@ -1456,132 +1511,142 @@ impl RuntimeBuilder { macro_rules! create_imports { ( $linker:ident, - $(export::$name:ident => $fn_path:path),* $(,)? + $ty:ty, + $(export::$name:ident => $fn:expr),* $(,)? ) => { $linker.func_wrap( WASM_MODULE, export::LOG, - Runtime::log, + |caller: ::wasmtime::Caller<$ty>, offset, len| Runtime::log(caller, offset, len), ) .and_then(|l| { l.func_wrap( WASM_MODULE, export::DBG, - Runtime::dbg, + |caller: ::wasmtime::Caller<$ty>, offset, len| Runtime::dbg(caller, offset, len), ) }) $(.and_then(|l| { l.func_wrap( WASM_MODULE, export::$name, - $fn_path, + $fn, ) }))* .map_err(Error::Initialization) }; } -impl<'wrld> RuntimeBuilder> { +impl<'wrld, 'block, 'state> RuntimeBuilder> { /// Builds the [`Runtime`] for *Smart Contract* execution /// /// # Errors /// /// Fails if failed to create default linker. - pub fn build(self) -> Result>> { + pub fn build(self) -> Result>> { self.finalize(|engine| { let mut linker = Linker::new(engine); - create_imports!(linker, - export::EXECUTE_ISI => Runtime::>::execute_instruction, - export::EXECUTE_QUERY => Runtime::>::execute_query, - export::GET_SMART_CONTRACT_PAYLOAD => Runtime::get_smart_contract_payload, + create_imports!(linker, state::SmartContract<'wrld, 'block, 'state>, + export::EXECUTE_ISI => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_instruction(caller, offset, len), + export::EXECUTE_QUERY => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_query(caller, offset, len), + export::GET_SMART_CONTRACT_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_smart_contract_payload(caller), )?; Ok(linker) }) } } -impl<'wrld> RuntimeBuilder> { +impl<'wrld, 'block, 'state> RuntimeBuilder> { /// Builds the [`Runtime`] for *Trigger* execution /// /// # Errors /// /// Fails if failed to create default linker. - pub fn build(self) -> Result>> { + pub fn build(self) -> Result>> { self.finalize(|engine| { let mut linker = Linker::new(engine); - create_imports!(linker, - export::EXECUTE_ISI => Runtime::>::execute_instruction, - export::EXECUTE_QUERY => Runtime::>::execute_query, - export::GET_TRIGGER_PAYLOAD => Runtime::get_trigger_payload, + create_imports!(linker, state::Trigger<'wrld, 'block, 'state>, + export::EXECUTE_ISI => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_instruction(caller, offset, len), + export::EXECUTE_QUERY => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_query(caller, offset, len), + export::GET_TRIGGER_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_trigger_payload(caller), )?; Ok(linker) }) } } -impl<'wrld> RuntimeBuilder> { +impl<'wrld, 'block, 'state> + RuntimeBuilder> +{ /// Builds the [`Runtime`] for *Executor* `validate_transaction()` execution /// /// # Errors /// /// Fails if failed to create default linker. - pub fn build(self) -> Result>> { + pub fn build( + self, + ) -> Result>> { self.finalize(|engine| { let mut linker = Linker::new(engine); - create_imports!(linker, - export::EXECUTE_ISI => Runtime::>::execute_instruction, - export::EXECUTE_QUERY => Runtime::>::execute_query, - export::GET_MIGRATE_PAYLOAD => Runtime::get_migrate_payload, - export::GET_VALIDATE_TRANSACTION_PAYLOAD => Runtime::get_validate_transaction_payload, - export::GET_VALIDATE_INSTRUCTION_PAYLOAD => Runtime::get_validate_instruction_payload, - export::GET_VALIDATE_QUERY_PAYLOAD => Runtime::get_validate_query_payload, - export::SET_PERMISSION_TOKEN_SCHEMA => Runtime::set_permission_token_schema, + create_imports!(linker, state::executor::ValidateTransaction<'wrld, 'block, 'state>, + export::EXECUTE_ISI => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_instruction(caller, offset, len), + export::EXECUTE_QUERY => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_query(caller, offset, len), + export::GET_MIGRATE_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_migrate_payload(caller), + export::GET_VALIDATE_TRANSACTION_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_transaction_payload(caller), + export::GET_VALIDATE_INSTRUCTION_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_instruction_payload(caller), + export::GET_VALIDATE_QUERY_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_query_payload(caller), + export::SET_PERMISSION_TOKEN_SCHEMA => |caller: ::wasmtime::Caller>, offset, len| Runtime::set_permission_token_schema(caller, offset, len), )?; Ok(linker) }) } } -impl<'wrld> RuntimeBuilder> { +impl<'wrld, 'block, 'state> + RuntimeBuilder> +{ /// Builds the [`Runtime`] for *Executor* `validate_instruction()` execution /// /// # Errors /// /// Fails if failed to create default linker. - pub fn build(self) -> Result>> { + pub fn build( + self, + ) -> Result>> { self.finalize(|engine| { let mut linker = Linker::new(engine); - create_imports!(linker, - export::EXECUTE_ISI => Runtime::>::execute_instruction, - export::EXECUTE_QUERY => Runtime::>::execute_query, - export::GET_MIGRATE_PAYLOAD => Runtime::get_migrate_payload, - export::GET_VALIDATE_TRANSACTION_PAYLOAD => Runtime::get_validate_transaction_payload, - export::GET_VALIDATE_INSTRUCTION_PAYLOAD => Runtime::get_validate_instruction_payload, - export::GET_VALIDATE_QUERY_PAYLOAD => Runtime::get_validate_query_payload, - export::SET_PERMISSION_TOKEN_SCHEMA => Runtime::set_permission_token_schema, + create_imports!(linker, state::executor::ValidateInstruction<'wrld, 'block, 'state>, + export::EXECUTE_ISI => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_instruction(caller, offset, len), + export::EXECUTE_QUERY => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_query(caller, offset, len), + export::GET_MIGRATE_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_migrate_payload(caller), + export::GET_VALIDATE_TRANSACTION_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_transaction_payload(caller), + export::GET_VALIDATE_INSTRUCTION_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_instruction_payload(caller), + export::GET_VALIDATE_QUERY_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_query_payload(caller), + export::SET_PERMISSION_TOKEN_SCHEMA => |caller: ::wasmtime::Caller>, offset, len| Runtime::set_permission_token_schema(caller, offset, len), )?; Ok(linker) }) } } -impl<'wrld> RuntimeBuilder> { +impl<'wrld, 'state> RuntimeBuilder> { /// Builds the [`Runtime`] for *Executor* `validate_query()` execution /// /// # Errors /// /// Fails if failed to create default linker. - pub fn build(self) -> Result>> { + pub fn build(self) -> Result>> { self.finalize(|engine| { let mut linker = Linker::new(engine); - create_imports!(linker, - export::EXECUTE_ISI => Runtime::>::execute_instruction, - export::EXECUTE_QUERY => Runtime::>::execute_query, + // NOTE: doesn't need closure here because `ValidateQuery` is covariant over 'wrld so 'static can be used and substituted with appropriate lifetime + create_imports!(linker, state::executor::ValidateQuery<'_, '_>, + export::EXECUTE_ISI => Runtime::execute_instruction, + export::EXECUTE_QUERY => Runtime::execute_query, export::GET_MIGRATE_PAYLOAD => Runtime::get_migrate_payload, export::GET_VALIDATE_TRANSACTION_PAYLOAD => Runtime::get_validate_transaction_payload, export::GET_VALIDATE_INSTRUCTION_PAYLOAD => Runtime::get_validate_instruction_payload, @@ -1593,24 +1658,24 @@ impl<'wrld> RuntimeBuilder> { } } -impl<'wrld> RuntimeBuilder> { +impl<'wrld, 'block, 'state> RuntimeBuilder> { /// Builds the [`Runtime`] to execute `permission_tokens()` entrypoint of *Executor* /// /// # Errors /// /// Fails if failed to create default linker. - pub fn build(self) -> Result>> { + pub fn build(self) -> Result>> { self.finalize(|engine| { let mut linker = Linker::new(engine); - create_imports!(linker, - export::EXECUTE_ISI => Runtime::>::execute_instruction, - export::EXECUTE_QUERY => Runtime::>::execute_query, - export::GET_MIGRATE_PAYLOAD => Runtime::get_migrate_payload, - export::GET_VALIDATE_TRANSACTION_PAYLOAD => Runtime::get_validate_transaction_payload, - export::GET_VALIDATE_INSTRUCTION_PAYLOAD => Runtime::get_validate_instruction_payload, - export::GET_VALIDATE_QUERY_PAYLOAD => Runtime::get_validate_query_payload, - export::SET_PERMISSION_TOKEN_SCHEMA => Runtime::set_permission_token_schema, + create_imports!(linker, state::executor::Migrate<'wrld, 'block, 'state>, + export::EXECUTE_ISI => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_instruction(caller, offset, len), + export::EXECUTE_QUERY => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_query(caller, offset, len), + export::GET_MIGRATE_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_migrate_payload(caller), + export::GET_VALIDATE_TRANSACTION_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_transaction_payload(caller), + export::GET_VALIDATE_INSTRUCTION_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_instruction_payload(caller), + export::GET_VALIDATE_QUERY_PAYLOAD => |caller: ::wasmtime::Caller>| Runtime::get_validate_query_payload(caller), + export::SET_PERMISSION_TOKEN_SCHEMA => |caller: ::wasmtime::Caller>, offset, len| Runtime::set_permission_token_schema(caller, offset, len), )?; Ok(linker) }) @@ -1645,8 +1710,8 @@ mod tests { use super::*; use crate::{ - kura::Kura, query::store::LiveQueryStore, smartcontracts::isi::Registrable as _, PeersIds, - World, + kura::Kura, query::store::LiveQueryStore, smartcontracts::isi::Registrable as _, + state::State, PeersIds, World, }; fn world_with_test_account(authority: &AccountId) -> World { @@ -1708,7 +1773,7 @@ mod tests { let authority = AccountId::from_str("alice@wonderland").expect("Valid"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world_with_test_account(&authority), kura, query_handle); + let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); @@ -1742,7 +1807,7 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; runtime - .execute(&mut wsv, authority, wat) + .execute(&mut state.block(false).transaction(), authority, wat) .expect("Execution failed"); Ok(()) @@ -1753,7 +1818,7 @@ mod tests { let authority = AccountId::from_str("alice@wonderland").expect("Valid"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world_with_test_account(&authority), kura, query_handle); + let state = State::new(world_with_test_account(&authority), kura, query_handle); let query_hex = encode_hex(SmartContractQueryRequest(QueryRequest::Query( QueryWithParameters::new( FindAccountById::new(authority.clone()).into(), @@ -1787,7 +1852,7 @@ mod tests { let mut runtime = RuntimeBuilder::::new().build()?; runtime - .execute(&mut wsv, authority, wat) + .execute(&mut state.block(false).transaction(), authority, wat) .expect("Execution failed"); Ok(()) @@ -1799,7 +1864,7 @@ mod tests { let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world_with_test_account(&authority), kura, query_handle); + let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); @@ -1833,7 +1898,7 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; - let res = runtime.validate(&mut wsv, authority, wat, 1); + let res = runtime.validate(&mut state.block(false).transaction(), authority, wat, 1); if let Error::ExportFnCall(ExportFnCallError::Other(report)) = res.expect_err("Execution should fail") @@ -1851,7 +1916,7 @@ mod tests { let authority = AccountId::from_str("alice@wonderland").expect("Valid"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world_with_test_account(&authority), kura, query_handle); + let state = State::new(world_with_test_account(&authority), kura, query_handle); let isi_hex = { let new_authority = AccountId::from_str("mad_hatter@wonderland").expect("Valid"); @@ -1885,7 +1950,7 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; - let res = runtime.validate(&mut wsv, authority, wat, 1); + let res = runtime.validate(&mut state.block(false).transaction(), authority, wat, 1); if let Error::ExportFnCall(ExportFnCallError::HostExecution(report)) = res.expect_err("Execution should fail") @@ -1903,7 +1968,7 @@ mod tests { let authority = AccountId::from_str("alice@wonderland").expect("Valid"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world_with_test_account(&authority), kura, query_handle); + let state = State::new(world_with_test_account(&authority), kura, query_handle); let query_hex = encode_hex(QueryBox::from(FindAccountById::new(authority.clone()))); let wat = format!( @@ -1929,7 +1994,7 @@ mod tests { ); let mut runtime = RuntimeBuilder::::new().build()?; - let res = runtime.validate(&mut wsv, authority, wat, 1); + let res = runtime.validate(&mut state.block(false).transaction(), authority, wat, 1); if let Error::ExportFnCall(ExportFnCallError::HostExecution(report)) = res.expect_err("Execution should fail") @@ -1945,7 +2010,7 @@ mod tests { let authority = AccountId::from_str("alice@wonderland").expect("Valid"); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world_with_test_account(&authority), kura, query_handle); + let state = State::new(world_with_test_account(&authority), kura, query_handle); let query_hex = encode_hex(QueryBox::from(FindAccountById::new(authority.clone()))); let wat = format!( @@ -1971,7 +2036,7 @@ mod tests { let mut runtime = RuntimeBuilder::::new().build()?; let err = runtime - .execute(&mut wsv, authority, wat) + .execute(&mut state.block(false).transaction(), authority, wat) .expect_err("Execution should fail"); assert!(matches!( diff --git a/core/src/snapshot.rs b/core/src/snapshot.rs index ec83e0f0da4..e6c5b892eb3 100644 --- a/core/src/snapshot.rs +++ b/core/src/snapshot.rs @@ -1,4 +1,4 @@ -//! This module contains [`WorldStateView`] snapshot actor service. +//! This module contains [`State`] snapshot actor service. use std::{ io::Read, path::{Path, PathBuf}, @@ -16,13 +16,12 @@ use tokio::sync::mpsc; use crate::{ kura::{BlockCount, Kura}, query::store::LiveQueryStoreHandle, - sumeragi::SumeragiHandle, - wsv::{KuraSeed, WorldStateView}, + state::{deserialize::KuraSeed, State}, }; -/// Name of the [`WorldStateView`] snapshot file. +/// Name of the [`State`] snapshot file. const SNAPSHOT_FILE_NAME: &str = "snapshot.data"; -/// Name of the temporary [`WorldStateView`] snapshot file. +/// Name of the temporary [`State`] snapshot file. const SNAPSHOT_TMP_FILE_NAME: &str = "snapshot.tmp"; // /// Errors produced by [`SnapshotMaker`] actor. @@ -35,15 +34,15 @@ pub struct SnapshotMakerHandle { _message_sender: mpsc::Sender<()>, } -/// Actor responsible for [`WorldStateView`] snapshot reading and writing. +/// Actor responsible for [`State`] snapshot reading and writing. pub struct SnapshotMaker { - sumeragi: SumeragiHandle, + state: Arc, /// Frequency at which snapshot is made create_every: Duration, /// Path to the directory where snapshots are stored store_dir: PathBuf, - /// Flag to signal that new wsv is available for taking snapshot - new_wsv_available: bool, + /// Hash of the latest block stored in the state + latest_block_hash: Option>, } impl SnapshotMaker { @@ -65,18 +64,13 @@ impl SnapshotMaker { loop { tokio::select! { - _ = snapshot_create_every.tick(), if self.new_wsv_available => { + _ = snapshot_create_every.tick() => { // Offload snapshot creation into blocking thread self.create_snapshot().await; }, - () = self.sumeragi.finalized_wsv_updated() => { - self.sumeragi.apply_finalized_wsv(|finalized_wsv| self.new_wsv_available = finalized_wsv.height() > 0); - } _ = message_receiver.recv() => { info!("All handler to SnapshotMaker are dropped. Saving latest snapshot and shutting down..."); - if self.new_wsv_available { - self.create_snapshot().await; - } + self.create_snapshot().await; break; } } @@ -86,25 +80,32 @@ impl SnapshotMaker { /// Invoke snapshot creation task async fn create_snapshot(&mut self) { - let sumeragi = self.sumeragi.clone(); let store_dir = self.store_dir.clone(); - let handle = tokio::task::spawn_blocking(move || -> Result { - sumeragi.apply_finalized_wsv(|wsv| { - try_write_snapshot(wsv, store_dir)?; - Ok(wsv.height()) - }) - }); + let latest_block_hash; + let at_height; + { + let state_view = self.state.view(); + latest_block_hash = state_view.latest_block_hash(); + at_height = state_view.height(); + } - match handle.await { - Ok(Ok(at_height)) => { - iroha_logger::info!(at_height, "Successfully created a snapshot of WSV"); - self.new_wsv_available = false; - } - Ok(Err(error)) => { - iroha_logger::error!(%error, "Failed to create a snapshot of WSV"); - } - Err(panic) => { - iroha_logger::error!(%panic, "Task panicked during creation of WSV snapshot"); + if latest_block_hash != self.latest_block_hash { + let state = self.state.clone(); + let handle = tokio::task::spawn_blocking(move || -> Result<(), TryWriteError> { + try_write_snapshot(&state, &store_dir) + }); + + match handle.await { + Ok(Ok(())) => { + iroha_logger::info!(at_height, "Successfully created a snapshot of state"); + self.latest_block_hash = latest_block_hash; + } + Ok(Err(error)) => { + iroha_logger::error!(%error, "Failed to create a snapshot of state"); + } + Err(panic) => { + iroha_logger::error!(%panic, "Task panicked during creation of state snapshot"); + } } } } @@ -112,13 +113,14 @@ impl SnapshotMaker { /// Create from [`Config`]. /// /// Might return [`None`] if the configuration is not suitable for _making_ snapshots. - pub fn from_config(config: &Config, sumeragi: &SumeragiHandle) -> Option { + pub fn from_config(config: &Config, state: Arc) -> Option { if let Mode::ReadWrite = config.mode { + let latest_block_hash = state.view().latest_block_hash(); Some(Self { - sumeragi: sumeragi.clone(), + state, create_every: config.create_every, store_dir: config.store_dir.clone(), - new_wsv_available: false, + latest_block_hash, }) } else { None @@ -126,7 +128,7 @@ impl SnapshotMaker { } } -/// Try to deserialize [`WorldStateView`] from a snapshot file. +/// Try to deserialize [`State`] from a snapshot file. /// /// # Errors /// - IO errors @@ -136,7 +138,7 @@ pub fn try_read_snapshot( kura: &Arc, query_handle: LiveQueryStoreHandle, BlockCount(block_count): BlockCount, -) -> Result { +) -> Result { let mut bytes = Vec::new(); let path = store_dir.as_ref().join(SNAPSHOT_FILE_NAME); let mut file = match std::fs::OpenOptions::new().read(true).open(&path) { @@ -156,8 +158,9 @@ pub fn try_read_snapshot( kura: Arc::clone(kura), query_handle, }; - let wsv = seed.deserialize(&mut deserializer)?; - let snapshot_height = wsv.block_hashes.len(); + let state = seed.deserialize(&mut deserializer)?; + let state_view = state.view(); + let snapshot_height = state_view.block_hashes.len(); if snapshot_height > block_count { return Err(TryReadError::MismatchedHeight { snapshot_height, @@ -167,8 +170,8 @@ pub fn try_read_snapshot( for height in 1..snapshot_height { let kura_block_hash = kura .get_block_hash(height as u64) - .expect("Kura has height at least as large as wsv_height"); - let snapshot_block_hash = wsv.block_hashes[height - 1]; + .expect("Kura has height at least as large as state height"); + let snapshot_block_hash = state_view.block_hashes[height - 1]; if kura_block_hash != snapshot_block_hash { return Err(TryReadError::MismatchedHash { height, @@ -177,7 +180,7 @@ pub fn try_read_snapshot( }); } } - Ok(wsv) + Ok(state) } /// Serialize and write snapshot to file, @@ -186,10 +189,7 @@ pub fn try_read_snapshot( /// # Errors /// - IO errors /// - Serialization errors -fn try_write_snapshot( - wsv: &WorldStateView, - store_dir: impl AsRef, -) -> Result<(), TryWriteError> { +fn try_write_snapshot(state: &State, store_dir: impl AsRef) -> Result<(), TryWriteError> { std::fs::create_dir_all(store_dir.as_ref()) .map_err(|err| TryWriteError::IO(err, store_dir.as_ref().to_path_buf()))?; let path_to_file = store_dir.as_ref().join(SNAPSHOT_FILE_NAME); @@ -201,7 +201,7 @@ fn try_write_snapshot( .open(&path_to_tmp_file) .map_err(|err| TryWriteError::IO(err, path_to_tmp_file.clone()))?; let mut serializer = serde_json::Serializer::new(file); - wsv.serialize(&mut serializer)?; + state.serialize(&mut serializer)?; std::fs::rename(path_to_tmp_file, &path_to_file) .map_err(|err| TryWriteError::IO(err, path_to_file.clone()))?; Ok(()) @@ -214,7 +214,7 @@ pub enum TryReadError { NotFound, /// Failed reading/writing {1:?} from disk IO(#[source] std::io::Error, PathBuf), - /// Error (de)serializing World State View snapshot + /// Error (de)serializing state snapshot Serialization(#[from] serde_json::Error), /// Snapshot is in a non-consistent state. Snapshot has greater height ({snapshot_height}) than kura block store ({kura_height}) MismatchedHeight { @@ -254,11 +254,11 @@ mod tests { use super::*; use crate::query::store::LiveQueryStore; - fn wsv_factory() -> WorldStateView { + fn state_factory() -> State { let alice_key = KeyPair::random(); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - WorldStateView::new( + State::new( crate::queue::tests::world_with_test_domains([alice_key.public_key().clone()]), kura, query_handle, @@ -269,9 +269,9 @@ mod tests { async fn creates_all_dirs_while_writing_snapshots() { let tmp_root = tempdir().unwrap(); let snapshot_store_dir = tmp_root.path().join("path/to/snapshot/dir"); - let wsv = wsv_factory(); + let state = state_factory(); - try_write_snapshot(&wsv, &snapshot_store_dir).unwrap(); + try_write_snapshot(&state, &snapshot_store_dir).unwrap(); assert!(Path::exists(snapshot_store_dir.as_path())) } @@ -280,14 +280,14 @@ mod tests { async fn can_read_snapshot_after_writing() { let tmp_root = tempdir().unwrap(); let store_dir = tmp_root.path().join("snapshot"); - let wsv = wsv_factory(); + let state = state_factory(); - try_write_snapshot(&wsv, &store_dir).unwrap(); + try_write_snapshot(&state, &store_dir).unwrap(); let _wsv = try_read_snapshot( &store_dir, &Kura::blank_kura_for_testing(), LiveQueryStore::test().start(), - BlockCount(usize::try_from(wsv.height()).unwrap()), + BlockCount(usize::try_from(state.view().height()).unwrap()), ) .unwrap(); } @@ -328,10 +328,7 @@ mod tests { panic!("should not be ok") }; - assert_eq!( - format!("{error}"), - "Error (de)serializing World State View snapshot" - ); + assert_eq!(format!("{error}"), "Error (de)serializing state snapshot"); } // TODO: test block count comparison diff --git a/core/src/state.rs b/core/src/state.rs new file mode 100644 index 00000000000..5d493632f62 --- /dev/null +++ b/core/src/state.rs @@ -0,0 +1,1888 @@ +//! This module provides the [`State`] — an in-memory representation of the current blockchain state. +use std::{borrow::Borrow, collections::BTreeSet, marker::PhantomData, sync::Arc, time::Duration}; + +use eyre::Result; +use iroha_config::parameters::actual::ChainWide as Config; +use iroha_crypto::HashOf; +use iroha_data_model::{ + account::AccountId, + block::SignedBlock, + events::trigger_completed::{TriggerCompletedEvent, TriggerCompletedOutcome}, + isi::error::{InstructionExecutionError as Error, MathError}, + parameter::{Parameter, ParameterValueBox}, + permission::{PermissionTokenSchema, Permissions}, + prelude::*, + query::error::{FindError, QueryExecutionFail}, + role::RoleId, +}; +use iroha_logger::prelude::*; +use iroha_primitives::{numeric::Numeric, small::SmallVec}; +use parking_lot::Mutex; +use range_bounds::RoleIdByAccountBounds; +use serde::{ + de::{DeserializeSeed, MapAccess, Visitor}, + Deserializer, Serialize, +}; +use storage::{ + cell::{Block as CellBlock, Cell, Transaction as CellTransaction, View as CellView}, + storage::{ + Block as StorageBlock, RangeIter, Snapshot as StorageSnapshot, Storage, + Transaction as StorageTransaction, View as StorageView, + }, +}; + +use crate::{ + block::CommittedBlock, + executor::Executor, + kura::Kura, + query::store::LiveQueryStoreHandle, + role::RoleIdWithOwner, + smartcontracts::{ + triggers::{ + self, + set::{LoadedActionTrait, LoadedWasm, Set as TriggerSet}, + }, + wasm, Execute, + }, + tx::TransactionExecutor, + Parameters, PeersIds, +}; + +/// The global entity consisting of `domains`, `triggers` and etc. +/// For example registration of domain, will have this as an ISI target. +#[derive(Default, Serialize)] +pub struct World { + /// Iroha config parameters. + pub(crate) parameters: Cell, + /// Identifications of discovered trusted peers. + pub(crate) trusted_peers_ids: Cell, + /// Registered domains. + pub(crate) domains: Storage, + /// Roles. [`Role`] pairs. + pub(crate) roles: Storage, + /// Permission tokens of an account. + pub(crate) account_permission_tokens: Storage, + /// Roles of an account. + pub(crate) account_roles: Storage, + /// Registered permission token ids. + pub(crate) permission_token_schema: Cell, + /// Triggers + // TODO: refactor `TriggerSet` to use storage inside + pub(crate) triggers: Cell, + /// Runtime Executor + pub(crate) executor: Cell, +} + +/// Struct for block's aggregated changes +pub struct WorldBlock<'world> { + /// Iroha config parameters. + pub(crate) parameters: CellBlock<'world, Parameters>, + /// Identifications of discovered trusted peers. + pub(crate) trusted_peers_ids: CellBlock<'world, PeersIds>, + /// Registered domains. + pub(crate) domains: StorageBlock<'world, DomainId, Domain>, + /// Roles. [`Role`] pairs. + pub(crate) roles: StorageBlock<'world, RoleId, Role>, + /// Permission tokens of an account. + pub(crate) account_permission_tokens: StorageBlock<'world, AccountId, Permissions>, + /// Roles of an account. + pub(crate) account_roles: StorageBlock<'world, RoleIdWithOwner, ()>, + /// Registered permission token ids. + pub(crate) permission_token_schema: CellBlock<'world, PermissionTokenSchema>, + /// Triggers + pub(crate) triggers: CellBlock<'world, TriggerSet>, + /// Runtime Executor + pub(crate) executor: CellBlock<'world, Executor>, + /// Events produced during execution of block + pub(crate) events_buffer: Vec, +} + +/// Struct for single transaction's aggregated changes +pub struct WorldTransaction<'block, 'world> { + /// Iroha config parameters. + pub(crate) parameters: CellTransaction<'block, 'world, Parameters>, + /// Identifications of discovered trusted peers. + pub(crate) trusted_peers_ids: CellTransaction<'block, 'world, PeersIds>, + /// Registered domains. + pub(crate) domains: StorageTransaction<'block, 'world, DomainId, Domain>, + /// Roles. [`Role`] pairs. + pub(crate) roles: StorageTransaction<'block, 'world, RoleId, Role>, + /// Permission tokens of an account. + pub(crate) account_permission_tokens: + StorageTransaction<'block, 'world, AccountId, Permissions>, + /// Roles of an account. + pub(crate) account_roles: StorageTransaction<'block, 'world, RoleIdWithOwner, ()>, + /// Registered permission token ids. + pub(crate) permission_token_schema: CellTransaction<'block, 'world, PermissionTokenSchema>, + /// Triggers + pub(crate) triggers: CellTransaction<'block, 'world, TriggerSet>, + /// Runtime Executor + pub(crate) executor: CellTransaction<'block, 'world, Executor>, + /// Events produced during execution of a transaction + events_buffer: TransactionEventBuffer<'block>, +} + +/// Wrapper for event's buffer to apply transaction rollback +struct TransactionEventBuffer<'block> { + /// Events produced during execution of block + events_buffer: &'block mut Vec, + /// Number of events produced during execution current transaction + events_created_in_transaction: usize, +} + +/// Consistent point in time view of the [`World`] +pub struct WorldView<'world> { + /// Iroha config parameters. + pub(crate) parameters: CellView<'world, Parameters>, + /// Identifications of discovered trusted peers. + pub(crate) trusted_peers_ids: CellView<'world, PeersIds>, + /// Registered domains. + pub(crate) domains: StorageView<'world, DomainId, Domain>, + /// Roles. [`Role`] pairs. + pub(crate) roles: StorageView<'world, RoleId, Role>, + /// Permission tokens of an account. + pub(crate) account_permission_tokens: StorageView<'world, AccountId, Permissions>, + /// Roles of an account. + pub(crate) account_roles: StorageView<'world, RoleIdWithOwner, ()>, + /// Registered permission token ids. + pub(crate) permission_token_schema: CellView<'world, PermissionTokenSchema>, + /// Triggers + pub(crate) triggers: CellView<'world, TriggerSet>, + /// Runtime Executor + pub(crate) executor: CellView<'world, Executor>, +} + +/// Consistent point in time view of the [`World`] +/// Used in places where [`WorldBlock`], [`WorldTransaction`], [`WorldView`] need to be converted to the same type for immutable operations +pub struct WorldSnapshot<'world> { + /// Iroha config parameters. + pub(crate) parameters: &'world Parameters, + /// Identifications of discovered trusted peers. + pub(crate) trusted_peers_ids: &'world PeersIds, + /// Registered domains. + pub(crate) domains: StorageSnapshot<'world, DomainId, Domain>, + /// Roles. [`Role`] pairs. + pub(crate) roles: StorageSnapshot<'world, RoleId, Role>, + /// Permission tokens of an account. + pub(crate) account_permission_tokens: StorageSnapshot<'world, AccountId, Permissions>, + /// Roles of an account. + pub(crate) account_roles: StorageSnapshot<'world, RoleIdWithOwner, ()>, + /// Registered permission token ids. + pub(crate) permission_token_schema: &'world PermissionTokenSchema, + /// Triggers + pub(crate) triggers: &'world TriggerSet, + /// Runtime Executor + pub(crate) executor: &'world Executor, +} + +/// Current state of the blockchain +#[derive(Serialize)] +pub struct State { + /// The world. Contains `domains`, `triggers`, `roles` and other data representing the current state of the blockchain. + pub world: World, + /// Configuration of World State View. + pub config: Cell, + /// Blockchain. + // TODO: Cell is redundant here since block_hashes is very easy to rollback by just popping the last element + pub block_hashes: Cell>>, + /// Hashes of transactions mapped onto block height where they stored + pub transactions: Storage, u64>, + /// Engine for WASM [`Runtime`](wasm::Runtime) to execute triggers. + #[serde(skip)] + pub engine: wasmtime::Engine, + + /// Reference to Kura subsystem. + #[serde(skip)] + kura: Arc, + /// Handle to the [`LiveQueryStore`]. + #[serde(skip)] + pub query_handle: LiveQueryStoreHandle, + /// Temporary metrics buffer of amounts of any asset that has been transacted. + /// TODO: this should be done through events + #[serde(skip)] + pub new_tx_amounts: Arc>>, +} + +/// Struct for block's aggregated changes +pub struct StateBlock<'state> { + /// The world. Contains `domains`, `triggers`, `roles` and other data representing the current state of the blockchain. + pub world: WorldBlock<'state>, + /// Configuration of World State View. + pub config: CellBlock<'state, Config>, + /// Blockchain. + pub block_hashes: CellBlock<'state, Vec>>, + /// Hashes of transactions mapped onto block height where they stored + pub transactions: StorageBlock<'state, HashOf, u64>, + /// Engine for WASM [`Runtime`](wasm::Runtime) to execute triggers. + pub engine: &'state wasmtime::Engine, + + /// Reference to Kura subsystem. + kura: &'state Kura, + /// Handle to the [`LiveQueryStore`]. + pub query_handle: &'state LiveQueryStoreHandle, + /// Temporary metrics buffer of amounts of any asset that has been transacted. + /// TODO: this should be done through events + pub new_tx_amounts: &'state Mutex>, +} + +/// Struct for single transaction's aggregated changes +pub struct StateTransaction<'block, 'state> { + /// The world. Contains `domains`, `triggers`, `roles` and other data representing the current state of the blockchain. + pub world: WorldTransaction<'block, 'state>, + /// Configuration of World State View. + pub config: CellTransaction<'block, 'state, Config>, + /// Blockchain. + pub block_hashes: CellTransaction<'block, 'state, Vec>>, + /// Hashes of transactions mapped onto block height where they stored + pub transactions: StorageTransaction<'block, 'state, HashOf, u64>, + /// Engine for WASM [`Runtime`](wasm::Runtime) to execute triggers. + pub engine: &'state wasmtime::Engine, + + /// Reference to Kura subsystem. + kura: &'state Kura, + /// Handle to the [`LiveQueryStore`]. + pub query_handle: &'state LiveQueryStoreHandle, + /// Temporary metrics buffer of amounts of any asset that has been transacted. + /// TODO: this should be done through events + pub new_tx_amounts: &'state Mutex>, +} + +/// Consistent point in time view of the [`State`] +pub struct StateView<'state> { + /// The world. Contains `domains`, `triggers`, `roles` and other data representing the current state of the blockchain. + pub world: WorldView<'state>, + /// Configuration of World State View. + pub config: CellView<'state, Config>, + /// Blockchain. + pub block_hashes: CellView<'state, Vec>>, + /// Hashes of transactions mapped onto block height where they stored + pub transactions: StorageView<'state, HashOf, u64>, + /// Engine for WASM [`Runtime`](wasm::Runtime) to execute triggers. + pub engine: &'state wasmtime::Engine, + + /// Reference to Kura subsystem. + kura: &'state Kura, + /// Handle to the [`LiveQueryStore`]. + pub query_handle: &'state LiveQueryStoreHandle, + /// Temporary metrics buffer of amounts of any asset that has been transacted. + /// TODO: this should be done through events + pub new_tx_amounts: &'state Mutex>, +} + +/// Consistent point in time view of the [`State`] +/// Used in places where [`StateBlock`], [`StateTransaction`], [`StateView`] need to be converted to the same type for immutable operations +pub struct StateSnapshot<'state> { + /// The world. Contains `domains`, `triggers`, `roles` and other data representing the current state of the blockchain. + pub world: WorldSnapshot<'state>, + /// Configuration of World State View. + pub config: &'state Config, + /// Blockchain. + pub block_hashes: &'state Vec>, + /// Hashes of transactions mapped onto block height where they stored + pub transactions: StorageSnapshot<'state, HashOf, u64>, + /// Engine for WASM [`Runtime`](wasm::Runtime) to execute triggers. + pub engine: &'state wasmtime::Engine, + + /// Reference to Kura subsystem. + kura: &'state Kura, + /// Handle to the [`LiveQueryStore`]. + pub query_handle: &'state LiveQueryStoreHandle, + /// Temporary metrics buffer of amounts of any asset that has been transacted. + /// TODO: this should be done through events + pub new_tx_amounts: &'state Mutex>, +} + +impl World { + /// Creates an empty `World`. + pub fn new() -> Self { + Self::default() + } + + /// Creates a [`World`] with these [`Domain`]s and trusted [`PeerId`]s. + pub fn with(domains: D, trusted_peers_ids: PeersIds) -> Self + where + D: IntoIterator, + { + let domains = domains + .into_iter() + .map(|domain| (domain.id().clone(), domain)) + .collect(); + World { + trusted_peers_ids: Cell::new(trusted_peers_ids), + domains, + ..World::new() + } + } + + /// Create struct to apply block's changes + pub fn block(&self, rollback_latest_block: bool) -> WorldBlock { + WorldBlock { + parameters: self.parameters.block(rollback_latest_block), + trusted_peers_ids: self.trusted_peers_ids.block(rollback_latest_block), + domains: self.domains.block(rollback_latest_block), + roles: self.roles.block(rollback_latest_block), + account_permission_tokens: self.account_permission_tokens.block(rollback_latest_block), + account_roles: self.account_roles.block(rollback_latest_block), + permission_token_schema: self.permission_token_schema.block(rollback_latest_block), + triggers: self.triggers.block(rollback_latest_block), + executor: self.executor.block(rollback_latest_block), + events_buffer: Vec::new(), + } + } + + /// Create point in time view of the [`World`] + pub fn view(&self) -> WorldView { + WorldView { + parameters: self.parameters.view(), + trusted_peers_ids: self.trusted_peers_ids.view(), + domains: self.domains.view(), + roles: self.roles.view(), + account_permission_tokens: self.account_permission_tokens.view(), + account_roles: self.account_roles.view(), + permission_token_schema: self.permission_token_schema.view(), + triggers: self.triggers.view(), + executor: self.executor.view(), + } + } +} + +macro_rules! world_read_only_methods { + ($($tt:tt)*) => { + macro_rules! insert_world_read_only_methods { + () => { + $($tt)* + } + } + } +} + +world_read_only_methods! { + // Domain-related methods + + /// Get `Domain` without an ability to modify it. + /// + /// # Errors + /// Fails if there is no domain + pub fn domain(&self, id: &DomainId) -> Result<&Domain, FindError> { + let domain = self + .domains + .get(id) + .ok_or_else(|| FindError::Domain(id.clone()))?; + Ok(domain) + } + + /// Get `Domain` and pass it to closure. + /// + /// # Errors + /// Fails if there is no domain + pub fn map_domain<'slf, T>( + &'slf self, + id: &DomainId, + f: impl FnOnce(&'slf Domain) -> T, + ) -> Result { + let domain = self.domain(id)?; + let value = f(domain); + Ok(value) + } + + /// Returns reference for domains map + #[inline] + pub fn domains(&self) -> impl Iterator { + self.domains.iter().map(|(_, domain)| domain) + } + + // Account-related methods + + /// Get `Account` and return reference to it. + /// + /// # Errors + /// Fails if there is no domain or account + pub fn account(&self, id: &AccountId) -> Result<&Account, FindError> { + self.domain(&id.domain_id).and_then(|domain| { + domain + .accounts + .get(id) + .ok_or_else(|| FindError::Account(id.clone())) + }) + } + + /// Get `Account` and pass it to closure. + /// + /// # Errors + /// Fails if there is no domain or account + pub fn map_account<'slf, T>( + &'slf self, + id: &AccountId, + f: impl FnOnce(&'slf Account) -> T, + ) -> Result { + let domain = self.domain(&id.domain_id)?; + let account = domain + .accounts + .get(id) + .ok_or(FindError::Account(id.clone()))?; + Ok(f(account)) + } + + /// Get `Account`'s `Asset`s + /// + /// # Errors + /// Fails if there is no domain or account + pub fn account_assets( + &self, + id: &AccountId, + ) -> Result, QueryExecutionFail> { + self.map_account(id, |account| account.assets.values()) + } + + /// Get [`Account`]'s [`RoleId`]s + // NOTE: have to use concreate type because don't want to capture lifetme of `id` + pub fn account_roles<'slf>(&'slf self, id: &AccountId) -> core::iter::Map, fn((&'slf RoleIdWithOwner, &'slf ())) -> &'slf RoleId> { + self.account_roles + .range(RoleIdByAccountBounds::new(id)) + .map(|(role, _)| &role.role_id) + } + + /// Return a set of all permission tokens granted to this account. + /// + /// # Errors + /// + /// - if `account_id` is not found in `self` + pub fn account_permission_tokens<'slf>( + &'slf self, + account_id: &AccountId, + ) -> Result, FindError> { + self.account(account_id)?; + + let mut tokens = self + .account_inherent_permission_tokens(account_id) + .collect::>(); + + for role_id in self.account_roles(account_id) { + if let Some(role) = self.roles.get(role_id) { + tokens.extend(role.permissions.iter()); + } + } + + Ok(tokens.into_iter()) + } + + /// Return a set of permission tokens granted to this account not as part of any role. + /// + /// # Errors + /// + /// - `account_id` is not found in `self.world`. + pub fn account_inherent_permission_tokens<'slf>( + &'slf self, + account_id: &AccountId, + ) -> std::collections::btree_set::Iter<'slf, PermissionToken> { + self.account_permission_tokens + .get(account_id) + .map_or_else(Default::default, std::collections::BTreeSet::iter) + } + + /// Return `true` if [`Account`] contains a permission token not associated with any role. + #[inline] + pub fn account_contains_inherent_permission( + &self, + account: &AccountId, + token: &PermissionToken, + ) -> bool { + self.account_permission_tokens + .get(account) + .map_or(false, |permissions| permissions.contains(token)) + } + + // Asset-related methods + + /// Get `Asset` by its id + /// + /// # Errors + /// - No such [`Asset`] + /// - The [`Account`] with which the [`Asset`] is associated doesn't exist. + /// - The [`Domain`] with which the [`Account`] is associated doesn't exist. + pub fn asset(&self, id: &AssetId) -> Result { + self.map_account( + &id.account_id, + |account| -> Result { + account + .assets + .get(id) + .ok_or_else(|| QueryExecutionFail::from(FindError::Asset(id.clone()))) + .map(Clone::clone) + }, + )? + } + + // AssetDefinition-related methods + + /// Get `AssetDefinition` immutable view. + /// + /// # Errors + /// - Asset definition entry not found + pub fn asset_definition( + &self, + asset_id: &AssetDefinitionId, + ) -> Result { + self.domain(&asset_id.domain_id)? + .asset_definitions + .get(asset_id) + .ok_or_else(|| FindError::AssetDefinition(asset_id.clone())) + .map(Clone::clone) + } + + /// Get total amount of [`Asset`]. + /// + /// # Errors + /// - Asset definition not found + pub fn asset_total_amount( + &self, + definition_id: &AssetDefinitionId, + ) -> Result { + self.domain(&definition_id.domain_id)? + .asset_total_quantities + .get(definition_id) + .ok_or_else(|| FindError::AssetDefinition(definition_id.clone())) + .copied() + } + + /// Get an immutable iterator over the [`PeerId`]s. + pub fn peers(&self) -> impl ExactSizeIterator { + self.trusted_peers_ids.iter() + } + + /// Get all `Parameter`s registered in the world. + pub fn parameters(&self) -> impl Iterator { + self.parameters.iter() + } + + /// Query parameter and convert it to a proper type + pub fn query_param, P: core::hash::Hash + Eq + ?Sized>( + &self, + param: &P, + ) -> Option + where + Parameter: Borrow

, + { + Parameters::get(&self.parameters, param) + .as_ref() + .map(|param| ¶m.val) + .cloned() + .and_then(|param_val| param_val.try_into().ok()) + } + + /// Returns reference for trusted peer ids + #[inline] + pub fn peers_ids(&self) -> &PeersIds { + &self.trusted_peers_ids + } +} + +impl<'world> WorldBlock<'world> { + /// Create struct to apply transaction's changes + pub fn trasaction(&mut self) -> WorldTransaction<'_, 'world> { + WorldTransaction { + parameters: self.parameters.transaction(), + trusted_peers_ids: self.trusted_peers_ids.transaction(), + domains: self.domains.transaction(), + roles: self.roles.transaction(), + account_permission_tokens: self.account_permission_tokens.transaction(), + account_roles: self.account_roles.transaction(), + permission_token_schema: self.permission_token_schema.transaction(), + triggers: self.triggers.transaction(), + executor: self.executor.transaction(), + events_buffer: TransactionEventBuffer { + events_buffer: &mut self.events_buffer, + events_created_in_transaction: 0, + }, + } + } + + /// Commit block's changes + pub fn commit(self) { + // IMPORTANT!!! Commit fields in reverse order, this way consistent results are insured + self.executor.commit(); + self.triggers.commit(); + self.permission_token_schema.commit(); + self.account_roles.commit(); + self.account_permission_tokens.commit(); + self.roles.commit(); + self.domains.commit(); + self.trusted_peers_ids.commit(); + self.parameters.commit(); + } + + /// Convert [`Self`] to [`WorldSnapshot`] + pub fn to_snapshot(&self) -> WorldSnapshot<'_> { + WorldSnapshot { + parameters: &self.parameters, + trusted_peers_ids: &self.trusted_peers_ids, + domains: self.domains.to_snapshot(), + roles: self.roles.to_snapshot(), + account_permission_tokens: self.account_permission_tokens.to_snapshot(), + account_roles: self.account_roles.to_snapshot(), + permission_token_schema: &self.permission_token_schema, + triggers: &self.triggers, + executor: &self.executor, + } + } + + insert_world_read_only_methods! {} +} + +impl WorldTransaction<'_, '_> { + /// Apply transaction's changes + pub fn apply(mut self) { + self.executor.apply(); + self.triggers.apply(); + self.permission_token_schema.apply(); + self.account_roles.apply(); + self.account_permission_tokens.apply(); + self.roles.apply(); + self.domains.apply(); + self.trusted_peers_ids.apply(); + self.parameters.apply(); + self.events_buffer.events_created_in_transaction = 0; + } + + /// Convert [`Self`] to [`WorldSnapshot`] + pub fn to_snapshot(&self) -> WorldSnapshot<'_> { + WorldSnapshot { + parameters: &self.parameters, + trusted_peers_ids: &self.trusted_peers_ids, + domains: self.domains.to_snapshot(), + roles: self.roles.to_snapshot(), + account_permission_tokens: self.account_permission_tokens.to_snapshot(), + account_roles: self.account_roles.to_snapshot(), + permission_token_schema: &self.permission_token_schema, + triggers: &self.triggers, + executor: &self.executor, + } + } + + /// Get `Domain` with an ability to modify it. + /// + /// # Errors + /// Fails if there is no domain + pub fn domain_mut(&mut self, id: &DomainId) -> Result<&mut Domain, FindError> { + let domain = self + .domains + .get_mut(id) + .ok_or_else(|| FindError::Domain(id.clone()))?; + Ok(domain) + } + + /// Get mutable reference to [`Account`] + /// + /// # Errors + /// Fail if domain or account not found + pub fn account_mut(&mut self, id: &AccountId) -> Result<&mut Account, FindError> { + self.domain_mut(&id.domain_id).and_then(move |domain| { + domain + .accounts + .get_mut(id) + .ok_or_else(|| FindError::Account(id.clone())) + }) + } + + /// Add [`permission`](PermissionToken) to the [`Account`] if the account does not have this permission yet. + /// + /// Return a Boolean value indicating whether or not the [`Account`] already had this permission. + pub fn add_account_permission(&mut self, account: &AccountId, token: PermissionToken) -> bool { + // `match` here instead of `map_or_else` to avoid cloning token into each closure + match self.account_permission_tokens.get_mut(account) { + None => { + self.account_permission_tokens + .insert(account.clone(), BTreeSet::from([token])); + true + } + Some(permissions) => { + if permissions.contains(&token) { + return true; + } + permissions.insert(token); + false + } + } + } + + /// Remove a [`permission`](PermissionToken) from the [`Account`] if the account has this permission. + /// Return a Boolean value indicating whether the [`Account`] had this permission. + pub fn remove_account_permission( + &mut self, + account: &AccountId, + token: &PermissionToken, + ) -> bool { + self.account_permission_tokens + .get_mut(account) + .map_or(false, |permissions| permissions.remove(token)) + } + + /// Get mutable reference to [`Asset`] + /// + /// # Errors + /// If domain, account or asset not found + pub fn asset_mut(&mut self, id: &AssetId) -> Result<&mut Asset, FindError> { + self.account_mut(&id.account_id).and_then(move |account| { + account + .assets + .get_mut(id) + .ok_or_else(|| FindError::Asset(id.clone())) + }) + } + + /// Get asset or inserts new with `default_asset_value`. + /// + /// # Errors + /// - There is no account with such name. + #[allow(clippy::missing_panics_doc)] + pub fn asset_or_insert( + &mut self, + asset_id: AssetId, + default_asset_value: impl Into, + ) -> Result<&mut Asset, Error> { + // Check that asset definition exists + { + let asset_definition_id = &asset_id.definition_id; + let asset_definition_domain_id = &asset_id.definition_id.domain_id; + let asset_definition_domain = self + .domains + .get(asset_definition_domain_id) + .ok_or(FindError::Domain(asset_definition_domain_id.clone()))?; + asset_definition_domain + .asset_definitions + .get(asset_definition_id) + .ok_or(FindError::AssetDefinition(asset_definition_id.clone()))?; + } + + let account_id = &asset_id.account_id; + let account_domain = self + .domains + .get_mut(&asset_id.account_id.domain_id) + .ok_or(FindError::Domain(asset_id.account_id.domain_id.clone()))?; + let account = account_domain + .accounts + .get_mut(account_id) + .ok_or(FindError::Account(account_id.clone()))?; + + Ok(account.assets.entry(asset_id.clone()).or_insert_with(|| { + let asset = Asset::new(asset_id, default_asset_value.into()); + Self::emit_events_impl( + &mut self.triggers, + &mut self.events_buffer, + Some(AccountEvent::Asset(AssetEvent::Created(asset.clone()))), + ); + asset + })) + } + + /// Get mutable reference to [`AssetDefinition`] + /// + /// # Errors + /// If domain or asset definition not found + pub fn asset_definition_mut( + &mut self, + id: &AssetDefinitionId, + ) -> Result<&mut AssetDefinition, FindError> { + self.domain_mut(&id.domain_id).and_then(|domain| { + domain + .asset_definitions + .get_mut(id) + .ok_or_else(|| FindError::AssetDefinition(id.clone())) + }) + } + + /// Increase [`Asset`] total amount by given value + /// + /// # Errors + /// - [`AssetDefinition`], [`Domain`] not found + /// - Overflow + pub fn increase_asset_total_amount( + &mut self, + definition_id: &AssetDefinitionId, + increment: Numeric, + ) -> Result<(), Error> { + let domain = self.domain_mut(&definition_id.domain_id)?; + let asset_total_amount: &mut Numeric = domain + .asset_total_quantities.get_mut(definition_id) + .expect("Asset total amount not being found is a bug: check `Register` to insert initial total amount"); + *asset_total_amount = asset_total_amount + .checked_add(increment) + .ok_or(MathError::Overflow)?; + let asset_total_amount = *asset_total_amount; + + self.emit_events({ + Some(DomainEvent::AssetDefinition( + AssetDefinitionEvent::TotalQuantityChanged(AssetDefinitionTotalQuantityChanged { + asset_definition_id: definition_id.clone(), + total_amount: asset_total_amount, + }), + )) + }); + + Ok(()) + } + + /// Decrease [`Asset`] total amount by given value + /// + /// # Errors + /// - [`AssetDefinition`], [`Domain`] not found + /// - Not enough quantity + pub fn decrease_asset_total_amount( + &mut self, + definition_id: &AssetDefinitionId, + decrement: Numeric, + ) -> Result<(), Error> { + let domain = self.domain_mut(&definition_id.domain_id)?; + let asset_total_amount: &mut Numeric = domain + .asset_total_quantities.get_mut(definition_id) + .expect("Asset total amount not being found is a bug: check `Register` to insert initial total amount"); + *asset_total_amount = asset_total_amount + .checked_sub(decrement) + .ok_or(MathError::NotEnoughQuantity)?; + let asset_total_amount = *asset_total_amount; + + self.emit_events({ + Some(DomainEvent::AssetDefinition( + AssetDefinitionEvent::TotalQuantityChanged(AssetDefinitionTotalQuantityChanged { + asset_definition_id: definition_id.clone(), + total_amount: asset_total_amount, + }), + )) + }); + + Ok(()) + } + + /// Set new permission token schema. + /// + /// Produces [`PermissionTokenSchemaUpdateEvent`]. + pub fn set_permission_token_schema(&mut self, schema: PermissionTokenSchema) { + let old_schema: PermissionTokenSchema = + std::mem::replace(&mut self.permission_token_schema, schema.clone()); + self.emit_events(std::iter::once(DataEvent::PermissionToken( + PermissionTokenSchemaUpdateEvent { + old_schema, + new_schema: schema, + }, + ))) + } + + /// Execute trigger with `trigger_id` as id and `authority` as owner + /// + /// Produces [`ExecuteTriggerEvent`]. + /// + /// Trigger execution time: + /// - If this method is called by ISI inside *transaction*, + /// then *trigger* will be executed on the **current** block + /// - If this method is called by ISI inside *trigger*, + /// then *trigger* will be executed on the **next** block + pub fn execute_trigger(&mut self, trigger_id: TriggerId, authority: &AccountId) { + let event = ExecuteTriggerEvent { + trigger_id, + authority: authority.clone(), + }; + + self.triggers.handle_execute_trigger_event(event.clone()); + self.events_buffer.push(event.into()); + } + + /// The function puts events produced by iterator into `events_buffer`. + /// Events should be produced in the order of expanding scope: from specific to general. + /// Example: account events before domain events. + pub fn emit_events, T: Into>(&mut self, world_events: I) { + Self::emit_events_impl(&mut self.triggers, &mut self.events_buffer, world_events) + } + + /// Implementation of [`Self::emit_events()`]. + /// + /// Usable when you can't call [`Self::emit_events()`] due to mutable reference to self. + fn emit_events_impl, T: Into>( + triggers: &mut TriggerSet, + events_buffer: &mut TransactionEventBuffer<'_>, + world_events: I, + ) { + let data_events: SmallVec<[DataEvent; 3]> = world_events + .into_iter() + .map(Into::into) + .map(Into::into) + .collect(); + + for event in data_events.iter() { + triggers.handle_data_event(event.clone()); + } + events_buffer.extend(data_events.into_iter().map(Into::into)); + } + + insert_world_read_only_methods! {} +} + +impl TransactionEventBuffer<'_> { + fn push(&mut self, event: Event) { + self.events_created_in_transaction += 1; + self.events_buffer.push(event); + } +} + +impl Extend for TransactionEventBuffer<'_> { + fn extend>(&mut self, iter: T) { + let len_before = self.events_buffer.len(); + self.events_buffer.extend(iter); + let len_after = self.events_buffer.len(); + self.events_created_in_transaction += len_after - len_before; + } +} + +impl Drop for TransactionEventBuffer<'_> { + fn drop(&mut self) { + // remove events produced by current transaction + self.events_buffer + .truncate(self.events_buffer.len() - self.events_created_in_transaction); + } +} + +impl WorldView<'_> { + insert_world_read_only_methods! {} + + /// Convert [`Self`] to [`WorldSnapshot`] + pub fn to_snapshot(&self) -> WorldSnapshot<'_> { + WorldSnapshot { + parameters: &self.parameters, + trusted_peers_ids: &self.trusted_peers_ids, + domains: self.domains.to_snapshot(), + roles: self.roles.to_snapshot(), + account_permission_tokens: self.account_permission_tokens.to_snapshot(), + account_roles: self.account_roles.to_snapshot(), + permission_token_schema: &self.permission_token_schema, + triggers: &self.triggers, + executor: &self.executor, + } + } +} + +impl WorldSnapshot<'_> { + insert_world_read_only_methods! {} +} + +impl State { + /// Construct [`State`] with given [`World`]. + #[must_use] + #[inline] + pub fn new(world: World, kura: Arc, query_handle: LiveQueryStoreHandle) -> Self { + // Added to remain backward compatible with other code primary in tests + Self::from_config(Config::default(), world, kura, query_handle) + } + + /// Construct [`State`] with specific [`Configuration`]. + #[inline] + pub fn from_config( + config: Config, + world: World, + kura: Arc, + query_handle: LiveQueryStoreHandle, + ) -> Self { + Self { + world, + config: Cell::new(config), + transactions: Storage::new(), + block_hashes: Cell::new(Vec::new()), + new_tx_amounts: Arc::new(Mutex::new(Vec::new())), + engine: wasm::create_engine(), + kura, + query_handle, + } + } + + /// Create structure to execute block + pub fn block(&self, rollback_latest_block: bool) -> StateBlock<'_> { + StateBlock { + world: self.world.block(rollback_latest_block), + config: self.config.block(rollback_latest_block), + block_hashes: self.block_hashes.block(rollback_latest_block), + transactions: self.transactions.block(rollback_latest_block), + engine: &self.engine, + kura: &self.kura, + query_handle: &self.query_handle, + new_tx_amounts: &self.new_tx_amounts, + } + } + + /// Create point in time view of [`WorldState`] + pub fn view(&self) -> StateView<'_> { + StateView { + world: self.world.view(), + config: self.config.view(), + block_hashes: self.block_hashes.view(), + transactions: self.transactions.view(), + engine: &self.engine, + kura: &self.kura, + query_handle: &self.query_handle, + new_tx_amounts: &self.new_tx_amounts, + } + } +} + +macro_rules! world_state_read_only_methods { + ($($tt:tt)*) => { + macro_rules! insert_world_state_read_only_methods { + () => { + $($tt)* + } + } + } +} + +// Read-only methods reused across `StateBlock`, `StateTransaction`, `StateView` +world_state_read_only_methods! { + // Block-related methods + + /// Get a reference to the latest block. Returns none if genesis is not committed. + #[inline] + pub fn latest_block_ref(&self) -> Option> { + self.kura + .get_block_by_height(self.block_hashes.len() as u64) + } + + /// Return the hash of the latest block + pub fn latest_block_hash(&self) -> Option> { + self.block_hashes.iter().nth_back(0).copied() + } + + /// Return the view change index of the latest block + pub fn latest_block_view_change_index(&self) -> u64 { + self.kura + .get_block_by_height(self.height()) + .map_or(0, |block| block.header().view_change_index) + } + + /// Return the hash of the block one before the latest block + pub fn previous_block_hash(&self) -> Option> { + self.block_hashes.iter().nth_back(1).copied() + } + + /// Load all blocks in the block chain from disc + pub fn all_blocks(&self) -> impl DoubleEndedIterator> + '_ { + let block_count = self.block_hashes.len() as u64; + (1..=block_count).map(|height| { + self.kura + .get_block_by_height(height) + .expect("Failed to load block.") + }) + } + + /// Return a vector of blockchain blocks after the block with the given `hash` + pub fn block_hashes_after_hash( + &self, + hash: Option>, + ) -> Vec> { + hash.map_or_else( + || self.block_hashes.clone(), + |block_hash| { + self.block_hashes + .iter() + .skip_while(|&x| *x != block_hash) + .skip(1) + .copied() + .collect() + }, + ) + } + + /// Return an iterator over blockchain block hashes starting with the block of the given `height` + pub fn block_hashes_from_height(&self, height: usize) -> Vec> { + self.block_hashes + .iter() + .skip(height.saturating_sub(1)) + .copied() + .collect() + } + + /// Height of blockchain + #[inline] + pub fn height(&self) -> u64 { + self.block_hashes.len() as u64 + } + + /// Find a [`SignedBlock`] by hash. + pub fn block_with_tx(&self, hash: &HashOf) -> Option> { + let height = *self.transactions.get(hash)?; + self.kura.get_block_by_height(height) + } + + /// Returns [`Some`] milliseconds since the genesis block was + /// committed, or [`None`] if it wasn't. + #[inline] + pub fn genesis_timestamp(&self) -> Option { + if self.block_hashes.is_empty() { + None + } else { + let opt = self + .kura + .get_block_by_height(1) + .map(|genesis_block| genesis_block.header().timestamp()); + + if opt.is_none() { + error!("Failed to get genesis block from Kura."); + } + opt + } + } + + /// Check if this [`SignedTransaction`] is already committed or rejected. + #[inline] + pub fn has_transaction(&self, hash: HashOf) -> bool { + self.transactions.get(&hash).is_some() + } + + /// Get transaction executor + pub fn transaction_executor(&self) -> TransactionExecutor { + TransactionExecutor::new(self.config.transaction_limits) + } +} + +impl<'state> StateBlock<'state> { + /// Create struct to store changes during transaction or trigger execution + pub fn transaction(&mut self) -> StateTransaction<'_, 'state> { + StateTransaction { + world: self.world.trasaction(), + config: self.config.transaction(), + block_hashes: self.block_hashes.transaction(), + transactions: self.transactions.transaction(), + engine: self.engine, + kura: self.kura, + query_handle: self.query_handle, + new_tx_amounts: self.new_tx_amounts, + } + } + + /// Commit changes aggregated during application of block + pub fn commit(self) { + self.transactions.commit(); + self.block_hashes.commit(); + self.config.commit(); + self.world.commit(); + } + + /// Convert [`Self`] to [`WorldStateSnapshot`] + pub fn to_snapshot(&self) -> StateSnapshot<'_> { + StateSnapshot { + world: self.world.to_snapshot(), + config: &self.config, + block_hashes: &self.block_hashes, + transactions: self.transactions.to_snapshot(), + engine: self.engine, + kura: self.kura, + query_handle: self.query_handle, + new_tx_amounts: self.new_tx_amounts, + } + } + + /// Commit `CommittedBlock` with changes in form of **Iroha Special + /// Instructions** to `self`. + /// + /// Order of execution: + /// 1) Transactions + /// 2) Triggers + /// + /// # Errors + /// + /// - (RARE) if applying transaction after validation fails. + /// - If trigger execution fails + /// - If timestamp conversion to `u64` fails + #[cfg_attr( + not(debug_assertions), + deprecated(note = "This function is to be used in testing only. ") + )] + #[iroha_logger::log(skip_all, fields(block_height))] + pub fn apply(&mut self, block: &CommittedBlock) -> Result<()> { + self.execute_transactions(block)?; + debug!("All block transactions successfully executed"); + + self.apply_without_execution(block)?; + + Ok(()) + } + + /// Execute `block` transactions and store their hashes as well as + /// `rejected_transactions` hashes + /// + /// # Errors + /// Fails if transaction instruction execution fails + fn execute_transactions(&mut self, block: &CommittedBlock) -> Result<()> { + // TODO: Should this block panic instead? + for tx in block.as_ref().transactions() { + if tx.error.is_none() { + // Execute every tx in it's own transaction + let mut transaction = self.transaction(); + transaction.process_executable( + tx.as_ref().instructions(), + tx.as_ref().authority().clone(), + )?; + transaction.apply(); + } + } + + Ok(()) + } + + /// Apply transactions without actually executing them. + /// It's assumed that block's transaction was already executed (as part of validation for example). + #[iroha_logger::log(skip_all, fields(block_height = block.as_ref().header().height))] + pub fn apply_without_execution(&mut self, block: &CommittedBlock) -> Result<()> { + let block_hash = block.as_ref().hash(); + trace!(%block_hash, "Applying block"); + + let time_event = self.create_time_event(block); + self.world.events_buffer.push(Event::Time(time_event)); + + let block_height = block.as_ref().header().height; + block + .as_ref() + .transactions() + .map(|tx| &tx.value) + .map(SignedTransaction::hash) + .for_each(|tx_hash| { + self.transactions.insert(tx_hash, block_height); + }); + + self.world.triggers.handle_time_event(time_event); + + let res = self.process_triggers(); + + if let Err(errors) = res { + warn!( + ?errors, + "The following errors have occurred during trigger execution" + ); + } + + self.block_hashes.push(block_hash); + + self.apply_parameters(); + + Ok(()) + } + + /// Create time event using previous and current blocks + fn create_time_event(&self, block: &CommittedBlock) -> TimeEvent { + let prev_interval = self.latest_block_ref().map(|latest_block| { + let header = &latest_block.as_ref().header(); + + TimeInterval { + since: header.timestamp(), + length: header.consensus_estimation(), + } + }); + + let interval = TimeInterval { + since: block.as_ref().header().timestamp(), + length: block.as_ref().header().consensus_estimation(), + }; + + TimeEvent { + prev_interval, + interval, + } + } + + /// Process every trigger in `matched_ids` + fn process_triggers(&mut self) -> Result<(), Vec> { + // Cloning and clearing `self.matched_ids` so that `handle_` call won't deadlock + let matched_ids = self.world.triggers.extract_matched_ids(); + let mut succeed = Vec::::with_capacity(matched_ids.len()); + let mut errors = Vec::new(); + for (event, id) in matched_ids { + // Eliding the closure triggers a lifetime mismatch + #[allow(clippy::redundant_closure_for_method_calls)] + let action = self + .world + .triggers + .inspect_by_id(&id, |action| action.clone_and_box()); + if let Some(action) = action { + if let Repeats::Exactly(repeats) = action.repeats() { + if *repeats == 0 { + continue; + } + } + // Execute every trigger in it's own transaction + let event = { + let mut transaction = self.transaction(); + match transaction.process_trigger(&id, &action, event) { + Ok(()) => { + transaction.apply(); + succeed.push(id.clone()); + TriggerCompletedEvent::new(id, TriggerCompletedOutcome::Success) + } + Err(error) => { + let event = TriggerCompletedEvent::new( + id, + TriggerCompletedOutcome::Failure(error.to_string()), + ); + errors.push(error); + event + } + } + }; + self.world + .events_buffer + .push(event.into()); + } + } + + self.world.triggers.decrease_repeats(&succeed); + + errors.is_empty().then_some(()).ok_or(errors) + } + + fn apply_parameters(&mut self) { + use iroha_data_model::parameter::default::*; + + macro_rules! update_params { + ($($param:expr => $config:expr),+ $(,)?) => { + $(if let Some(param) = self.world.query_param($param) { + $config = param; + })+ + }; + } + + update_params! { + WSV_ASSET_METADATA_LIMITS => self.config.asset_metadata_limits, + WSV_ASSET_DEFINITION_METADATA_LIMITS => self.config.asset_definition_metadata_limits, + WSV_ACCOUNT_METADATA_LIMITS => self.config.account_metadata_limits, + WSV_DOMAIN_METADATA_LIMITS => self.config.domain_metadata_limits, + WSV_IDENT_LENGTH_LIMITS => self.config.ident_length_limits, + EXECUTOR_FUEL_LIMIT => self.config.executor_runtime.fuel_limit, + EXECUTOR_MAX_MEMORY => self.config.executor_runtime.max_memory_bytes, + WASM_FUEL_LIMIT => self.config.wasm_runtime.fuel_limit, + WASM_MAX_MEMORY => self.config.wasm_runtime.max_memory_bytes, + TRANSACTION_LIMITS => self.config.transaction_limits, + } + } + + insert_world_state_read_only_methods! {} +} + +impl StateTransaction<'_, '_> { + /// Apply transaction making it's changes visible + pub fn apply(self) { + self.transactions.apply(); + self.block_hashes.apply(); + self.config.apply(); + self.world.apply(); + } + + /// Convert [`Self`] to [`WorldStateSnapshot`] + pub fn to_snapshot(&self) -> StateSnapshot<'_> { + StateSnapshot { + world: self.world.to_snapshot(), + config: &self.config, + block_hashes: &self.block_hashes, + transactions: self.transactions.to_snapshot(), + engine: self.engine, + kura: self.kura, + query_handle: self.query_handle, + new_tx_amounts: self.new_tx_amounts, + } + } + + fn process_executable(&mut self, executable: &Executable, authority: AccountId) -> Result<()> { + match executable { + Executable::Instructions(instructions) => { + self.process_instructions(instructions.iter().cloned(), &authority) + } + Executable::Wasm(bytes) => { + let mut wasm_runtime = wasm::RuntimeBuilder::::new() + .with_config(self.config.wasm_runtime) + .with_engine(self.engine.clone()) // Cloning engine is cheap + .build()?; + wasm_runtime + .execute(self, authority, bytes) + .map_err(Into::into) + } + } + } + + fn process_instructions( + &mut self, + instructions: impl IntoIterator, + authority: &AccountId, + ) -> Result<()> { + instructions.into_iter().try_for_each(|instruction| { + instruction.execute(authority, self)?; + Ok::<_, eyre::Report>(()) + }) + } + + fn process_trigger( + &mut self, + id: &TriggerId, + action: &dyn LoadedActionTrait, + event: Event, + ) -> Result<()> { + use triggers::set::LoadedExecutable::*; + let authority = action.authority(); + + match action.executable() { + Instructions(instructions) => { + self.process_instructions(instructions.iter().cloned(), authority) + } + Wasm(LoadedWasm { module, .. }) => { + let mut wasm_runtime = wasm::RuntimeBuilder::::new() + .with_config(self.config.wasm_runtime) + .with_engine(self.engine.clone()) // Cloning engine is cheap + .build()?; + wasm_runtime + .execute_trigger_module(self, id, authority.clone(), module, event) + .map_err(Into::into) + } + } + } + + insert_world_state_read_only_methods! {} +} + +impl StateView<'_> { + /// Convert [`Self`] to [`WorldStateSnapshot`] + pub fn to_snapshot(&self) -> StateSnapshot<'_> { + StateSnapshot { + world: self.world.to_snapshot(), + config: &self.config, + block_hashes: &self.block_hashes, + transactions: self.transactions.to_snapshot(), + engine: self.engine, + kura: self.kura, + query_handle: self.query_handle, + new_tx_amounts: self.new_tx_amounts, + } + } + + insert_world_state_read_only_methods! {} +} + +impl StateSnapshot<'_> { + insert_world_state_read_only_methods! {} +} + +/// Bounds for `range` queries +mod range_bounds { + use core::ops::{Bound, RangeBounds}; + + use iroha_primitives::{cmpext::MinMaxExt, impl_as_dyn_key}; + + use super::*; + use crate::role::RoleIdWithOwner; + + /// Key for range queries over account for roles + #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] + pub struct RoleIdByAccount<'role> { + account_id: &'role AccountId, + role_id: MinMaxExt<&'role RoleId>, + } + + /// Bounds for range quired over account for roles + pub struct RoleIdByAccountBounds<'role> { + start: RoleIdByAccount<'role>, + end: RoleIdByAccount<'role>, + } + + impl<'role> RoleIdByAccountBounds<'role> { + /// Create range bounds for range quires of roles over account + pub fn new(account_id: &'role AccountId) -> Self { + Self { + start: RoleIdByAccount { + account_id, + role_id: MinMaxExt::Min, + }, + end: RoleIdByAccount { + account_id, + role_id: MinMaxExt::Max, + }, + } + } + } + + impl<'role> RangeBounds for RoleIdByAccountBounds<'role> { + fn start_bound(&self) -> Bound<&(dyn AsRoleIdByAccount + 'role)> { + Bound::Excluded(&self.start) + } + + fn end_bound(&self) -> Bound<&(dyn AsRoleIdByAccount + 'role)> { + Bound::Excluded(&self.end) + } + } + + impl AsRoleIdByAccount for RoleIdWithOwner { + fn as_key(&self) -> RoleIdByAccount<'_> { + RoleIdByAccount { + account_id: &self.account_id, + role_id: (&self.role_id).into(), + } + } + } + + impl_as_dyn_key! { + target: RoleIdWithOwner, + key: RoleIdByAccount<'_>, + trait: AsRoleIdByAccount + } +} + +pub(crate) mod deserialize { + use storage::serde::CellSeeded; + + use super::*; + + // Loader for [`Set`] + #[derive(Clone, Copy)] + pub struct WasmSeed<'e, T> { + pub engine: &'e wasmtime::Engine, + _marker: PhantomData, + } + + impl<'e, T> WasmSeed<'e, T> { + pub fn cast(&self) -> WasmSeed<'e, U> { + WasmSeed { + engine: self.engine, + _marker: PhantomData, + } + } + } + + impl<'e, 'de, T> DeserializeSeed<'de> for WasmSeed<'e, Option> + where + WasmSeed<'e, T>: DeserializeSeed<'de, Value = T>, + { + type Value = Option; + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct OptionVisitor<'l, T> { + loader: WasmSeed<'l, T>, + _marker: PhantomData, + } + + impl<'e, 'de, T> Visitor<'de> for OptionVisitor<'e, T> + where + WasmSeed<'e, T>: DeserializeSeed<'de, Value = T>, + { + type Value = Option; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct World") + } + + fn visit_none(self) -> Result + where + E: serde::de::Error, + { + Ok(None) + } + + fn visit_some(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Some(self.loader.deserialize(deserializer)).transpose() + } + } + + let visitor = OptionVisitor { + loader: self.cast::(), + _marker: PhantomData, + }; + deserializer.deserialize_option(visitor) + } + } + + impl<'de> DeserializeSeed<'de> for WasmSeed<'_, World> { + type Value = World; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct WorldVisitor<'l> { + loader: &'l WasmSeed<'l, World>, + } + + impl<'de> Visitor<'de> for WorldVisitor<'_> { + type Value = World; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct World") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + let mut parameters = None; + let mut trusted_peers_ids = None; + let mut domains = None; + let mut roles = None; + let mut account_permission_tokens = None; + let mut account_roles = None; + let mut permission_token_schema = None; + let mut triggers = None; + let mut executor = None; + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "parameters" => { + parameters = Some(map.next_value()?); + } + "trusted_peers_ids" => { + trusted_peers_ids = Some(map.next_value()?); + } + "domains" => { + domains = Some(map.next_value()?); + } + "roles" => { + roles = Some(map.next_value()?); + } + "account_permission_tokens" => { + account_permission_tokens = Some(map.next_value()?); + } + "account_roles" => { + account_roles = Some(map.next_value()?); + } + "permission_token_schema" => { + permission_token_schema = Some(map.next_value()?); + } + "triggers" => { + triggers = Some(map.next_value_seed(CellSeeded { + seed: self.loader.cast::(), + })?); + } + "executor" => { + executor = Some(map.next_value_seed(CellSeeded { + seed: self.loader.cast::(), + })?); + } + _ => { /* Skip unknown fields */ } + } + } + + Ok(World { + parameters: parameters + .ok_or_else(|| serde::de::Error::missing_field("parameters"))?, + trusted_peers_ids: trusted_peers_ids + .ok_or_else(|| serde::de::Error::missing_field("trusted_peers_ids"))?, + domains: domains + .ok_or_else(|| serde::de::Error::missing_field("domains"))?, + roles: roles.ok_or_else(|| serde::de::Error::missing_field("roles"))?, + account_permission_tokens: account_permission_tokens.ok_or_else(|| { + serde::de::Error::missing_field("account_permission_tokens") + })?, + account_roles: account_roles + .ok_or_else(|| serde::de::Error::missing_field("account_roles"))?, + permission_token_schema: permission_token_schema.ok_or_else(|| { + serde::de::Error::missing_field("permission_token_schema") + })?, + triggers: triggers + .ok_or_else(|| serde::de::Error::missing_field("triggers"))?, + executor: executor + .ok_or_else(|| serde::de::Error::missing_field("executor"))?, + }) + } + } + + deserializer.deserialize_struct( + "World", + &[ + "parameters", + "trusted_peers_ids", + "domains", + "roles", + "account_permission_tokens", + "account_roles", + "permission_token_schema", + "triggers", + "executor", + ], + WorldVisitor { loader: &self }, + ) + } + } + + /// Context necessary for deserializing [`State`] + pub struct KuraSeed { + /// Kura subsystem reference + pub kura: Arc, + /// Handle to the [`LiveQueryStore`](crate::query::store::LiveQueryStore). + pub query_handle: LiveQueryStoreHandle, + } + + impl<'de> DeserializeSeed<'de> for KuraSeed { + type Value = State; + + fn deserialize(self, deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct StateVisitor { + loader: KuraSeed, + } + + impl<'de> Visitor<'de> for StateVisitor { + type Value = State; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct WorldState") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'de>, + { + let mut world = None; + let mut config = None; + let mut block_hashes = None; + let mut transactions = None; + + let engine = wasm::create_engine(); + + let wasm_seed: WasmSeed<()> = WasmSeed { + engine: &engine, + _marker: PhantomData, + }; + + while let Some(key) = map.next_key::()? { + match key.as_str() { + "world" => { + world = Some(map.next_value_seed(wasm_seed.cast::())?); + } + "config" => { + config = Some(map.next_value()?); + } + "block_hashes" => { + block_hashes = Some(map.next_value()?); + } + "transactions" => { + transactions = Some(map.next_value()?); + } + _ => { /* Skip unknown fields */ } + } + } + + Ok(State { + world: world.ok_or_else(|| serde::de::Error::missing_field("world"))?, + config: config.ok_or_else(|| serde::de::Error::missing_field("config"))?, + block_hashes: block_hashes + .ok_or_else(|| serde::de::Error::missing_field("block_hashes"))?, + transactions: transactions + .ok_or_else(|| serde::de::Error::missing_field("transactions"))?, + kura: self.loader.kura, + query_handle: self.loader.query_handle, + engine, + new_tx_amounts: Arc::new(Mutex::new(Vec::new())), + }) + } + } + + deserializer.deserialize_struct( + "WorldState", + &["world", "config", "block_hashes", "transactions"], + StateVisitor { loader: self }, + ) + } + } +} + +#[cfg(test)] +mod tests { + use iroha_data_model::block::BlockPayload; + use iroha_primitives::unique_vec::UniqueVec; + + use super::*; + use crate::{ + block::ValidBlock, query::store::LiveQueryStore, role::RoleIdWithOwner, + sumeragi::network_topology::Topology, + }; + + /// Used to inject faulty payload for testing + fn payload_mut(block: &mut CommittedBlock) -> &mut BlockPayload { + let SignedBlock::V1(signed) = &mut block.0 .0; + &mut signed.payload + } + + #[tokio::test] + async fn get_block_hashes_after_hash() { + const BLOCK_CNT: usize = 10; + + let topology = Topology::new(UniqueVec::new()); + let block = ValidBlock::new_dummy().commit(&topology).unwrap(); + let kura = Kura::blank_kura_for_testing(); + let query_handle = LiveQueryStore::test().start(); + let state = State::new(World::default(), kura, query_handle); + let mut state_block = state.block(false); + + let mut block_hashes = vec![]; + for i in 1..=BLOCK_CNT { + let mut block = block.clone(); + + payload_mut(&mut block).header.height = i as u64; + payload_mut(&mut block).header.previous_block_hash = block_hashes.last().copied(); + + block_hashes.push(block.as_ref().hash()); + state_block.apply(&block).unwrap(); + } + + assert!(state_block + .block_hashes_after_hash(Some(block_hashes[6])) + .into_iter() + .eq(block_hashes.into_iter().skip(7))); + } + + #[tokio::test] + async fn get_blocks_from_height() { + const BLOCK_CNT: usize = 10; + + let topology = Topology::new(UniqueVec::new()); + let block = ValidBlock::new_dummy().commit(&topology).unwrap(); + let kura = Kura::blank_kura_for_testing(); + let query_handle = LiveQueryStore::test().start(); + let state = State::new(World::default(), kura.clone(), query_handle); + let mut state_block = state.block(false); + + for i in 1..=BLOCK_CNT { + let mut block = block.clone(); + payload_mut(&mut block).header.height = i as u64; + + state_block.apply(&block).unwrap(); + kura.store_block(block); + } + + assert_eq!( + &state_block + .all_blocks() + .skip(7) + .map(|block| *block.header().height()) + .collect::>(), + &[8, 9, 10] + ); + } + + #[test] + fn role_account_range() { + let account_id: AccountId = "alice@wonderland".parse().unwrap(); + let roles = [ + RoleIdWithOwner::new(account_id.clone(), "1".parse().unwrap()), + RoleIdWithOwner::new(account_id.clone(), "2".parse().unwrap()), + RoleIdWithOwner::new("bob@wonderland".parse().unwrap(), "3".parse().unwrap()), + RoleIdWithOwner::new("a@wonderland".parse().unwrap(), "4".parse().unwrap()), + RoleIdWithOwner::new("0@0".parse().unwrap(), "5".parse().unwrap()), + RoleIdWithOwner::new("1@1".parse().unwrap(), "6".parse().unwrap()), + ]; + let map = BTreeSet::from(roles); + + let range = map + .range(RoleIdByAccountBounds::new(&account_id)) + .collect::>(); + assert_eq!(range.len(), 2); + for role in range { + assert_eq!(&role.account_id, &account_id); + } + } +} diff --git a/core/src/sumeragi/main_loop.rs b/core/src/sumeragi/main_loop.rs index 90a4a5bef66..2c7aea4f0d0 100644 --- a/core/src/sumeragi/main_loop.rs +++ b/core/src/sumeragi/main_loop.rs @@ -24,10 +24,6 @@ pub struct Sumeragi { pub peer_id: PeerId, /// An actor that sends events pub events_sender: EventsSender, - /// The world state view instance that is used in public contexts - pub public_wsv_sender: watch::Sender, - /// The finalized world state view instance that is used in public contexts - pub public_finalized_wsv_sender: watch::Sender, /// Time by which a newly created block should be committed. Prevents malicious nodes /// from stalling the network by not participating in consensus pub commit_time: Duration, @@ -49,18 +45,6 @@ pub struct Sumeragi { pub debug_force_soft_fork: bool, /// The current network topology. pub current_topology: Topology, - /// The sumeragi internal [`WorldStateView`]. This will probably - /// morph into a wsv + various patches as we attempt to - /// multithread isi execution. In the future we might also once - /// again merge the internal wsv with the public facing one. But - /// as of now we keep them separate for greater flexibility when - /// optimizing. - pub wsv: WorldStateView, - /// A copy of wsv that is kept one block behind at all times. Because - /// we currently don't support rolling back wsv block application we - /// reset to a copy of the finalized_wsv instead. This is expensive but - /// enables us to handle soft-forks. - pub finalized_wsv: WorldStateView, /// In order to *be fast*, we must minimize communication with /// other subsystems where we can. This way the performance of /// sumeragi is more dependent on the code that is internal to the @@ -147,6 +131,7 @@ impl Sumeragi { fn receive_network_packet( &self, + state_view: &StateView<'_>, view_change_proof_chain: &mut ProofChain, ) -> (Option, bool) { const MAX_CONTROL_MSG_IN_A_ROW: usize = 25; @@ -180,7 +165,7 @@ impl Sumeragi { msg.view_change_proofs, &self.current_topology.ordered_peers, self.current_topology.max_faults(), - self.wsv.latest_block_hash(), + state_view.latest_block_hash(), ) { trace!(%error, "Failed to add proofs into view change proof chain") } @@ -188,7 +173,7 @@ impl Sumeragi { let current_view_change_index = view_change_proof_chain.verify_with_state( &self.current_topology.ordered_peers, self.current_topology.max_faults(), - self.wsv.latest_block_hash(), + state_view.latest_block_hash(), ) as u64; let mut should_prune = false; @@ -224,6 +209,7 @@ impl Sumeragi { fn init_listen_for_genesis( &mut self, + state: &State, shutdown_receiver: &mut tokio::sync::oneshot::Receiver<()>, ) -> Result<(), EarlyReturn> { info!(addr = %self.peer_id.address, "Listen for genesis"); @@ -237,8 +223,6 @@ impl Sumeragi { match self.message_receiver.try_recv() { Ok(message) => { - let mut new_wsv = self.wsv.clone(); - let block = match message { BlockMessage::BlockCreated(BlockCreated { block }) | BlockMessage::BlockSyncUpdate(BlockSyncUpdate { block }) => block, @@ -248,11 +232,12 @@ impl Sumeragi { } }; + let mut state_block = state.block(false); let block = match ValidBlock::validate( block, &self.current_topology, &self.chain_id, - &mut new_wsv, + &mut state_block, ) .and_then(|block| { block @@ -266,9 +251,8 @@ impl Sumeragi { } }; - new_wsv.world_mut().trusted_peers_ids = - block.as_ref().commit_topology().clone(); - self.commit_block(block, new_wsv); + *state_block.world.trusted_peers_ids = block.as_ref().commit_topology().clone(); + self.commit_block(block, state_block); return Err(EarlyReturn::GenesisBlockReceivedAndCommitted); } Err(mpsc::TryRecvError::Disconnected) => return Err(EarlyReturn::Disconnected), @@ -277,11 +261,14 @@ impl Sumeragi { } } - fn sumeragi_init_commit_genesis(&mut self, genesis_network: GenesisNetwork) { + fn sumeragi_init_commit_genesis(&mut self, genesis_network: GenesisNetwork, state: &State) { std::thread::sleep(Duration::from_millis(250)); - assert_eq!(self.wsv.height(), 0); - assert_eq!(self.wsv.latest_block_hash(), None); + { + let state_view = state.view(); + assert_eq!(state_view.height(), 0); + assert_eq!(state_view.latest_block_hash(), None); + } let transactions: Vec<_> = genesis_network .into_transactions() @@ -290,9 +277,9 @@ impl Sumeragi { .collect::>() .expect("Genesis invalid"); - let mut new_wsv = self.wsv.clone(); + let mut state_block = state.block(false); let genesis = BlockBuilder::new(transactions, self.current_topology.clone(), vec![]) - .chain(0, &mut new_wsv) + .chain(0, &mut state_block) .sign(&self.key_pair); let genesis_msg = BlockCreated::from(genesis.clone()).into(); @@ -312,107 +299,99 @@ impl Sumeragi { "Genesis block created", ); - self.commit_block(genesis, new_wsv); + self.commit_block(genesis, state_block); self.broadcast_packet(genesis_msg); } - fn commit_block(&mut self, block: CommittedBlock, new_wsv: WorldStateView) { - self.update_state::(block, new_wsv); + fn commit_block(&mut self, block: CommittedBlock, state_block: StateBlock<'_>) { + self.update_state::(block, state_block); } - fn replace_top_block(&mut self, block: CommittedBlock, new_wsv: WorldStateView) { - self.update_state::(block, new_wsv); + fn replace_top_block(&mut self, block: CommittedBlock, state_block: StateBlock<'_>) { + self.update_state::(block, state_block); } fn update_state( &mut self, block: CommittedBlock, - mut new_wsv: WorldStateView, + mut state_block: StateBlock<'_>, ) { info!( addr=%self.peer_id.address, role=%self.current_topology.role(&self.peer_id), - block_height=%block.as_ref().header().height(), + block_height=%state_block.height(), block_hash=%block.as_ref().hash(), "{}", Strategy::LOG_MESSAGE, ); - Strategy::before_update_hook(self); - - new_wsv + state_block .apply_without_execution(&block) - .expect("Failed to apply block on WSV. Bailing."); - self.wsv = new_wsv; + .expect("Failed to apply block on state. Bailing."); - let wsv_events = core::mem::take(&mut self.wsv.events_buffer); - self.send_events(wsv_events); + let state_events = core::mem::take(&mut state_block.world.events_buffer); + self.send_events(state_events); - // Parameters are updated before updating public copy of sumeragi - self.update_params(); - - let new_topology = - Topology::recreate_topology(block.as_ref(), 0, self.wsv.peers().cloned().collect()); + let new_topology = Topology::recreate_topology( + block.as_ref(), + 0, + state_block.world.peers().cloned().collect(), + ); let events = block.produce_events(); // https://github.com/hyperledger/iroha/issues/3396 - // Kura should store the block only upon successful application to the internal WSV to avoid storing a corrupted block. - // Public-facing WSV update should happen after that and be followed by `BlockCommited` event to prevent client access to uncommitted data. + // Kura should store the block only upon successful application to the internal state to avoid storing a corrupted block. + // Public-facing state update should happen after that and be followed by `BlockCommited` event to prevent client access to uncommitted data. Strategy::kura_store_block(&self.kura, block); - // Update WSV copy that is public facing - self.public_wsv_sender - .send_modify(|public_wsv| *public_wsv = self.wsv.clone()); - self.public_finalized_wsv_sender - .send_if_modified(|public_finalized_wsv| { - if public_finalized_wsv.height() < self.finalized_wsv.height() { - *public_finalized_wsv = self.finalized_wsv.clone(); - true - } else { - false - } - }); - - // NOTE: This sends "Block committed" event, - // so it should be done AFTER public facing WSV update - self.send_events(events); + // Parameters are updated before updating public copy of sumeragi + self.update_params(&state_block); + self.cache_transaction(&state_block); self.current_topology = new_topology; self.connect_peers(&self.current_topology); - self.cache_transaction(); + // Commit new block making it's effect visible for the rest of application + state_block.commit(); + // NOTE: This sends "Block committed" event, + // so it should be done AFTER public facing state update + self.send_events(events); } - fn update_params(&mut self) { + fn update_params(&mut self, state_block: &StateBlock<'_>) { use iroha_data_model::parameter::default::*; - if let Some(block_time) = self.wsv.query_param(BLOCK_TIME) { + if let Some(block_time) = state_block.world.query_param(BLOCK_TIME) { self.block_time = Duration::from_millis(block_time); } - if let Some(commit_time) = self.wsv.query_param(COMMIT_TIME_LIMIT) { + if let Some(commit_time) = state_block.world.query_param(COMMIT_TIME_LIMIT) { self.commit_time = Duration::from_millis(commit_time); } - if let Some(max_txs_in_block) = self.wsv.query_param::(MAX_TRANSACTIONS_IN_BLOCK) { + if let Some(max_txs_in_block) = state_block + .world + .query_param::(MAX_TRANSACTIONS_IN_BLOCK) + { self.max_txs_in_block = max_txs_in_block as usize; } } - fn cache_transaction(&mut self) { + fn cache_transaction(&mut self, state_block: &StateBlock<'_>) { self.transaction_cache.retain(|tx| { - !self.wsv.has_transaction(tx.as_ref().hash()) && !self.queue.is_expired(tx) + !state_block.has_transaction(tx.as_ref().hash()) && !self.queue.is_expired(tx) }); } - fn vote_for_block( + fn vote_for_block<'state>( &self, + state: &'state State, topology: &Topology, BlockCreated { block }: BlockCreated, - ) -> Option { + ) -> Option> { let block_hash = block.hash_of_payload(); let addr = &self.peer_id.address; let role = self.current_topology.role(&self.peer_id); trace!(%addr, %role, block_hash=%block_hash, "Block received, voting..."); - let mut new_wsv = self.wsv.clone(); - let block = match ValidBlock::validate(block, topology, &self.chain_id, &mut new_wsv) { + let mut state_block = state.block(false); + let block = match ValidBlock::validate(block, topology, &self.chain_id, &mut state_block) { Ok(block) => block, Err((_, error)) => { warn!(%addr, %role, ?error, "Block validation failed"); @@ -422,26 +401,28 @@ impl Sumeragi { let signed_block = block.sign(&self.key_pair); - Some(VotingBlock::new(signed_block, new_wsv)) + Some(VotingBlock::new(signed_block, state_block)) } fn prune_view_change_proofs_and_calculate_current_index( &self, + state_view: &StateView<'_>, view_change_proof_chain: &mut ProofChain, ) -> u64 { - view_change_proof_chain.prune(self.wsv.latest_block_hash()); + view_change_proof_chain.prune(state_view.latest_block_hash()); view_change_proof_chain.verify_with_state( &self.current_topology.ordered_peers, self.current_topology.max_faults(), - self.wsv.latest_block_hash(), + state_view.latest_block_hash(), ) as u64 } #[allow(clippy::too_many_lines)] - fn handle_message( + fn handle_message<'state>( &mut self, message: BlockMessage, - voting_block: &mut Option, + state: &'state State, + voting_block: &mut Option>, current_view_change_index: u64, voting_signatures: &mut Vec>, ) { @@ -455,20 +436,22 @@ impl Sumeragi { let block_hash = block.hash(); info!(%addr, %role, hash=%block_hash, "Block sync update received"); - match handle_block_sync(&self.chain_id, block, &self.wsv, &self.finalized_wsv) { - Ok(BlockSyncOk::CommitBlock(block, new_wsv)) => { - self.commit_block(block, new_wsv) + // Release writer before handling block sync + let _ = voting_block.take(); + match handle_block_sync(&self.chain_id, block, state) { + Ok(BlockSyncOk::CommitBlock(block, state_block)) => { + self.commit_block(block, state_block) } - Ok(BlockSyncOk::ReplaceTopBlock(block, new_wsv)) => { + Ok(BlockSyncOk::ReplaceTopBlock(block, state_block)) => { warn!( %addr, %role, - peer_latest_block_hash=?self.wsv.latest_block_hash(), - peer_latest_block_view_change_index=?self.wsv.latest_block_view_change_index(), + peer_latest_block_hash=?state_block.latest_block_hash(), + peer_latest_block_view_change_index=?state_block.latest_block_view_change_index(), consensus_latest_block_hash=%block.as_ref().hash(), consensus_latest_block_view_change_index=%block.as_ref().header().view_change_index, "Soft fork occurred: peer in inconsistent state. Rolling back and replacing top block." ); - self.replace_top_block(block, new_wsv) + self.replace_top_block(block, state_block) } Err((_, BlockSyncError::BlockNotValid(error))) => { error!(%addr, %role, %block_hash, ?error, "Block not valid.") @@ -485,7 +468,7 @@ impl Sumeragi { )) => { debug!( %addr, %role, - peer_latest_block_hash=?self.wsv.latest_block_hash(), + peer_latest_block_hash=?state.view().latest_block_hash(), peer_latest_block_view_change_index=?peer_view_change_index, consensus_latest_block_hash=%block_hash, consensus_latest_block_view_change_index=%block_view_change_index, @@ -521,7 +504,7 @@ impl Sumeragi { .commit_with_signatures(current_topology, signatures) { Ok(committed_block) => { - self.commit_block(committed_block, voted_block.new_wsv) + self.commit_block(committed_block, voted_block.state_block) } Err((_, error)) => { error!(%addr, %role, %hash, ?error, "Block failed to be committed") @@ -544,7 +527,10 @@ impl Sumeragi { .is_consensus_required() .expect("Peer has `ValidatingPeer` role, which mean that current topology require consensus"); - if let Some(v_block) = self.vote_for_block(¤t_topology, block_created) { + // Release block writer before creating a new one + let _ = voting_block.take(); + if let Some(v_block) = self.vote_for_block(state, ¤t_topology, block_created) + { let block_hash = v_block.block.as_ref().hash_of_payload(); let msg = BlockSigned::from(v_block.block.clone()).into(); @@ -560,9 +546,12 @@ impl Sumeragi { "Peer has `ObservingPeer` role, which mean that current topology require consensus", ); - if let Some(v_block) = self.vote_for_block(¤t_topology, block_created) { + // Release block writer before creating new one + let _ = voting_block.take(); + if let Some(v_block) = self.vote_for_block(state, ¤t_topology, block_created) + { if current_view_change_index >= 1 { - let block_hash = v_block.block.as_ref().hash_of_payload(); + let block_hash = v_block.block.as_ref().hash(); self.broadcast_packet_to( BlockSigned::from(v_block.block.clone()).into(), @@ -576,7 +565,11 @@ impl Sumeragi { } } (BlockMessage::BlockCreated(block_created), Role::ProxyTail) => { - if let Some(mut new_block) = self.vote_for_block(current_topology, block_created) { + // Release block writer before creating new one + let _ = voting_block.take(); + if let Some(mut new_block) = + self.vote_for_block(state, current_topology, block_created) + { // NOTE: Up until this point it was unknown which block is expected to be received, // therefore all the signatures (of any hash) were collected and will now be pruned add_signatures::(&mut new_block, voting_signatures.drain(..)); @@ -615,9 +608,10 @@ impl Sumeragi { } #[allow(clippy::too_many_lines)] - fn process_message_independent( + fn process_message_independent<'state>( &mut self, - voting_block: &mut Option, + state: &'state State, + voting_block: &mut Option>, current_view_change_index: u64, round_start_time: &Instant, #[cfg_attr(not(debug_assertions), allow(unused_variables))] is_genesis_peer: bool, @@ -639,14 +633,14 @@ impl Sumeragi { let create_block_start_time = Instant::now(); // TODO: properly process triggers! - let mut new_wsv = self.wsv.clone(); + let mut state_block = state.block(false); let event_recommendations = Vec::new(); let new_block = BlockBuilder::new( transactions, self.current_topology.clone(), event_recommendations, ) - .chain(current_view_change_index, &mut new_wsv) + .chain(current_view_change_index, &mut state_block) .sign(&self.key_pair); let created_in = create_block_start_time.elapsed(); @@ -656,7 +650,7 @@ impl Sumeragi { if created_in > self.pipeline_time() / 2 { warn!("Creating block takes too much time. This might prevent consensus from operating. Consider increasing `commit_time` or decreasing `max_transactions_in_block`"); } - *voting_block = Some(VotingBlock::new(new_block.clone(), new_wsv)); + *voting_block = Some(VotingBlock::new(new_block.clone(), state_block)); let msg = BlockCreated::from(new_block).into(); if current_view_change_index >= 1 { @@ -670,7 +664,7 @@ impl Sumeragi { self.broadcast_packet( BlockCommitted::from(committed_block.clone()).into(), ); - self.commit_block(committed_block, new_wsv); + self.commit_block(committed_block, state_block); } Err((_, error)) => error!(%addr, role=%Role::Leader, ?error), } @@ -681,7 +675,7 @@ impl Sumeragi { Role::ProxyTail => { if let Some(voted_block) = voting_block.take() { let voted_at = voted_block.voted_at; - let new_wsv = voted_block.new_wsv; + let state_block = voted_block.state_block; match voted_block.block.commit(current_topology) { Ok(committed_block) => { @@ -716,11 +710,12 @@ impl Sumeragi { ); } } - self.commit_block(committed_block, new_wsv); + self.commit_block(committed_block, state_block); } Err((block, error)) => { // Restore the current voting block and continue the round - *voting_block = Some(VotingBlock::voted_at(block, new_wsv, voted_at)); + *voting_block = + Some(VotingBlock::voted_at(block, state_block, voted_at)); trace!(?error, "Not enough signatures, waiting for more..."); } } @@ -799,20 +794,21 @@ pub(crate) fn run( genesis_network: Option, mut sumeragi: Sumeragi, mut shutdown_receiver: tokio::sync::oneshot::Receiver<()>, + state: Arc, ) { // Connect peers with initial topology sumeragi.connect_peers(&sumeragi.current_topology); let span = span!(tracing::Level::TRACE, "genesis").entered(); - let is_genesis_peer = if sumeragi.wsv.height() == 0 - || sumeragi.wsv.latest_block_hash().is_none() + let is_genesis_peer = if state.view().height() == 0 + || state.view().latest_block_hash().is_none() { if let Some(genesis_network) = genesis_network { - sumeragi.sumeragi_init_commit_genesis(genesis_network); + sumeragi.sumeragi_init_commit_genesis(genesis_network, &state); true } else { sumeragi - .init_listen_for_genesis(&mut shutdown_receiver) + .init_listen_for_genesis(&state, &mut shutdown_receiver) .unwrap_or_else(|err| assert_ne!(EarlyReturn::Disconnected, err, "Disconnected")); false } @@ -833,10 +829,10 @@ pub(crate) fn run( let mut should_sleep = false; let mut view_change_proof_chain = ProofChain::default(); let mut old_view_change_index = 0; - let mut old_latest_block_hash = sumeragi - .wsv + let mut old_latest_block_hash = state + .view() .latest_block_ref() - .expect("WSV must have blocks") + .expect("state must have blocks") .hash(); // Duration after which a view change is suggested let mut view_change_time = sumeragi.pipeline_time(); @@ -854,6 +850,8 @@ pub(crate) fn run( let span_for_sumeragi_cycle = span!(Level::TRACE, "main_thread_cycle"); let _enter_for_sumeragi_cycle = span_for_sumeragi_cycle.enter(); + let state_view = state.view(); + sumeragi .transaction_cache // Checking if transactions are in the blockchain is costly @@ -867,7 +865,7 @@ pub(crate) fn run( let mut expired_transactions = Vec::new(); sumeragi.queue.get_transactions_for_block( - &sumeragi.wsv, + &state_view, sumeragi.max_txs_in_block, &mut sumeragi.transaction_cache, &mut expired_transactions, @@ -875,7 +873,10 @@ pub(crate) fn run( sumeragi.send_events(expired_transactions.iter().map(expired_event)); let current_view_change_index = sumeragi - .prune_view_change_proofs_and_calculate_current_index(&mut view_change_proof_chain); + .prune_view_change_proofs_and_calculate_current_index( + &state_view, + &mut view_change_proof_chain, + ); reset_state( &sumeragi.peer_id, @@ -883,10 +884,9 @@ pub(crate) fn run( current_view_change_index, &mut old_view_change_index, &mut old_latest_block_hash, - &sumeragi - .wsv + &state_view .latest_block_ref() - .expect("WSV must have blocks"), + .expect("state must have blocks"), &mut sumeragi.current_topology, &mut voting_block, &mut voting_signatures, @@ -896,12 +896,14 @@ pub(crate) fn run( ); if let Some(message) = { - let (msg, sleep) = sumeragi.receive_network_packet(&mut view_change_proof_chain); + let (msg, sleep) = + sumeragi.receive_network_packet(&state_view, &mut view_change_proof_chain); should_sleep = sleep; msg } { sumeragi.handle_message( message, + &state, &mut voting_block, current_view_change_index, &mut voting_signatures, @@ -909,8 +911,12 @@ pub(crate) fn run( } // State could be changed after handling message so it is necessary to reset state before handling message independent step + let state_view = state.view(); let current_view_change_index = sumeragi - .prune_view_change_proofs_and_calculate_current_index(&mut view_change_proof_chain); + .prune_view_change_proofs_and_calculate_current_index( + &state_view, + &mut view_change_proof_chain, + ); // We broadcast our view change suggestion after having processed the latest from others inside `receive_network_packet` let node_expects_block = !sumeragi.transaction_cache.is_empty(); @@ -930,14 +936,14 @@ pub(crate) fn run( } let suspect_proof = - ProofBuilder::new(sumeragi.wsv.latest_block_hash(), current_view_change_index) + ProofBuilder::new(state_view.latest_block_hash(), current_view_change_index) .sign(&sumeragi.key_pair); view_change_proof_chain .insert_proof( &sumeragi.current_topology.ordered_peers, sumeragi.current_topology.max_faults(), - sumeragi.wsv.latest_block_hash(), + state_view.latest_block_hash(), suspect_proof, ) .unwrap_or_else(|err| error!("{err}")); @@ -957,10 +963,9 @@ pub(crate) fn run( current_view_change_index, &mut old_view_change_index, &mut old_latest_block_hash, - &sumeragi - .wsv + &state_view .latest_block_ref() - .expect("WSV must have blocks"), + .expect("state must have blocks"), &mut sumeragi.current_topology, &mut voting_block, &mut voting_signatures, @@ -970,6 +975,7 @@ pub(crate) fn run( ); sumeragi.process_message_independent( + &state, &mut voting_block, current_view_change_index, &round_start_time, @@ -1039,10 +1045,6 @@ fn early_return( trait ApplyBlockStrategy { const LOG_MESSAGE: &'static str; - /// Perform necessary changes in sumeragi before applying block. - /// Like updating `wsv` or `finalized_wsv`. - fn before_update_hook(sumeragi: &mut Sumeragi); - /// Operation to invoke in kura to store block. fn kura_store_block(kura: &Kura, block: CommittedBlock); } @@ -1053,13 +1055,6 @@ struct NewBlockStrategy; impl ApplyBlockStrategy for NewBlockStrategy { const LOG_MESSAGE: &'static str = "Committing block"; - #[inline] - fn before_update_hook(sumeragi: &mut Sumeragi) { - // Save current wsv state in case of rollback in the future - // Use swap to avoid cloning since `wsv` will be overwritten anyway by `new_wsv` - core::mem::swap(&mut sumeragi.finalized_wsv, &mut sumeragi.wsv); - } - #[inline] fn kura_store_block(kura: &Kura, block: CommittedBlock) { kura.store_block(block) @@ -1072,20 +1067,15 @@ struct ReplaceTopBlockStrategy; impl ApplyBlockStrategy for ReplaceTopBlockStrategy { const LOG_MESSAGE: &'static str = "Replacing top block"; - #[inline] - fn before_update_hook(_sumeragi: &mut Sumeragi) { - // Do nothing since valid new_wsv already provided - } - #[inline] fn kura_store_block(kura: &Kura, block: CommittedBlock) { kura.replace_top_block(block) } } -enum BlockSyncOk { - CommitBlock(CommittedBlock, WorldStateView), - ReplaceTopBlock(CommittedBlock, WorldStateView), +enum BlockSyncOk<'state> { + CommitBlock(CommittedBlock, StateBlock<'state>), + ReplaceTopBlock(CommittedBlock, StateBlock<'state>), } #[derive(Debug)] @@ -1102,73 +1092,71 @@ enum BlockSyncError { }, } -fn handle_block_sync( +fn handle_block_sync<'state>( chain_id: &ChainId, block: SignedBlock, - wsv: &WorldStateView, - finalized_wsv: &WorldStateView, -) -> Result { + state: &'state State, +) -> Result, (SignedBlock, BlockSyncError)> { let block_height = block.header().height; - let wsv_height = wsv.height(); - if wsv_height + 1 == block_height { + let state_height = state.view().height(); + if state_height + 1 == block_height { // Normal branch for adding new block on top of current - let mut new_wsv = wsv.clone(); + let mut state_block = state.block(false); let topology = { - let last_committed_block = new_wsv + let last_committed_block = state_block .latest_block_ref() .expect("Not in genesis round so must have at least genesis block"); - let new_peers = new_wsv.peers().cloned().collect(); + let new_peers = state_block.world.peers().cloned().collect(); let view_change_index = block.header().view_change_index; Topology::recreate_topology(&last_committed_block, view_change_index, new_peers) }; - ValidBlock::validate(block, &topology, chain_id, &mut new_wsv) + ValidBlock::validate(block, &topology, chain_id, &mut state_block) .and_then(|block| { block .commit(&topology) .map_err(|(block, err)| (block.into(), err)) }) - .map(|block| BlockSyncOk::CommitBlock(block, new_wsv)) + .map(|block| BlockSyncOk::CommitBlock(block, state_block)) .map_err(|(block, error)| (block, BlockSyncError::BlockNotValid(error))) - } else if wsv_height == block_height && block_height > 1 { + } else if state_height == block_height && block_height > 1 { // Soft-fork on genesis block isn't possible // Soft fork branch for replacing current block with valid one - let mut new_wsv = finalized_wsv.clone(); + + let peer_view_change_index = state.view().latest_block_view_change_index(); + let block_view_change_index = block.header().view_change_index; + if peer_view_change_index >= block_view_change_index { + return Err(( + block, + BlockSyncError::SoftForkBlockSmallViewChangeIndex { + peer_view_change_index, + block_view_change_index, + }, + )); + } + + let mut state_block = state.block(true); let topology = { - let last_committed_block = new_wsv + let last_committed_block = state_block .latest_block_ref() .expect("Not in genesis round so must have at least genesis block"); - let new_peers = new_wsv.peers().cloned().collect(); + let new_peers = state_block.world.peers().cloned().collect(); let view_change_index = block.header().view_change_index; Topology::recreate_topology(&last_committed_block, view_change_index, new_peers) }; - ValidBlock::validate(block, &topology, chain_id, &mut new_wsv) + ValidBlock::validate(block, &topology, chain_id, &mut state_block) .and_then(|block| { block .commit(&topology) .map_err(|(block, err)| (block.into(), err)) }) .map_err(|(block, error)| (block, BlockSyncError::SoftForkBlockNotValid(error))) - .and_then(|block| { - let peer_view_change_index = wsv.latest_block_view_change_index(); - let block_view_change_index = block.as_ref().header().view_change_index; - if peer_view_change_index < block_view_change_index { - Ok(BlockSyncOk::ReplaceTopBlock(block, new_wsv)) - } else { - Err(( - block.into(), - BlockSyncError::SoftForkBlockSmallViewChangeIndex { - peer_view_change_index, - block_view_change_index, - }, - )) - } - }) + .map(|block| BlockSyncOk::ReplaceTopBlock(block, state_block)) } else { // Error branch other peer send irrelevant block Err(( block, BlockSyncError::BlockNotProperHeight { - peer_height: wsv_height, + peer_height: state_height, block_height, }, )) @@ -1193,7 +1181,7 @@ mod tests { chain_id: &ChainId, topology: &Topology, leader_key_pair: &KeyPair, - ) -> (WorldStateView, Arc, SignedBlock) { + ) -> (State, Arc, SignedBlock) { // Predefined world state let alice_id: AccountId = "alice@wonderland".parse().expect("Valid"); let alice_keys = KeyPair::random(); @@ -1205,12 +1193,13 @@ mod tests { let world = World::with([domain], topology.ordered_peers.clone()); let kura = Kura::blank_kura_for_testing(); let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(world, Arc::clone(&kura), query_handle); + let state = State::new(world, Arc::clone(&kura), query_handle); // Create "genesis" block // Creating an instruction let fail_box = Fail::new("Dummy isi".to_owned()); + let mut state_block = state.block(false); // Making two transactions that have the same instruction let tx = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) .with_instructions([fail_box]) @@ -1218,54 +1207,58 @@ mod tests { let tx = AcceptedTransaction::accept( tx, chain_id, - &wsv.transaction_executor().transaction_limits, + &state_block.transaction_executor().transaction_limits, ) .expect("Valid"); // Creating a block of two identical transactions and validating it let block = BlockBuilder::new(vec![tx.clone(), tx], topology.clone(), Vec::new()) - .chain(0, &mut wsv) + .chain(0, &mut state_block) .sign(leader_key_pair); let genesis = block.commit(topology).expect("Block is valid"); - wsv.apply(&genesis).expect("Failed to apply block"); + state_block.apply(&genesis).expect("Failed to apply block"); + state_block.commit(); kura.store_block(genesis); - // Making two transactions that have the same instruction - let create_asset_definition1 = Register::asset_definition(AssetDefinition::numeric( - "xor1#wonderland".parse().expect("Valid"), - )); - let create_asset_definition2 = Register::asset_definition(AssetDefinition::numeric( - "xor2#wonderland".parse().expect("Valid"), - )); - - let tx1 = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) - .with_instructions([create_asset_definition1]) - .sign(&alice_keys); - let tx1 = AcceptedTransaction::accept( - tx1, - chain_id, - &wsv.transaction_executor().transaction_limits, - ) - .map(Into::into) - .expect("Valid"); - let tx2 = TransactionBuilder::new(chain_id.clone(), alice_id) - .with_instructions([create_asset_definition2]) - .sign(&alice_keys); - let tx2 = AcceptedTransaction::accept( - tx2, - chain_id, - &wsv.transaction_executor().transaction_limits, - ) - .map(Into::into) - .expect("Valid"); - - // Creating a block of two identical transactions and validating it - let block = BlockBuilder::new(vec![tx1, tx2], topology.clone(), Vec::new()) - .chain(0, &mut wsv.clone()) - .sign(leader_key_pair); + let block = { + let mut state_block = state.block(false); + // Making two transactions that have the same instruction + let create_asset_definition1 = Register::asset_definition(AssetDefinition::numeric( + "xor1#wonderland".parse().expect("Valid"), + )); + let create_asset_definition2 = Register::asset_definition(AssetDefinition::numeric( + "xor2#wonderland".parse().expect("Valid"), + )); + + let tx1 = TransactionBuilder::new(chain_id.clone(), alice_id.clone()) + .with_instructions([create_asset_definition1]) + .sign(&alice_keys); + let tx1 = AcceptedTransaction::accept( + tx1, + chain_id, + &state_block.transaction_executor().transaction_limits, + ) + .map(Into::into) + .expect("Valid"); + let tx2 = TransactionBuilder::new(chain_id.clone(), alice_id) + .with_instructions([create_asset_definition2]) + .sign(&alice_keys); + let tx2 = AcceptedTransaction::accept( + tx2, + chain_id, + &state_block.transaction_executor().transaction_limits, + ) + .map(Into::into) + .expect("Valid"); + + // Creating a block of two identical transactions and validating it + BlockBuilder::new(vec![tx1, tx2], topology.clone(), Vec::new()) + .chain(0, &mut state_block) + .sign(leader_key_pair) + }; - (wsv, kura, block.into()) + (state, kura, block.into()) } #[test] @@ -1278,14 +1271,12 @@ mod tests { "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (finalized_wsv, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair); - let wsv = finalized_wsv.clone(); + let (state, _, mut block) = create_data_for_test(&chain_id, &topology, &leader_key_pair); // Malform block to make it invalid payload_mut(&mut block).commit_topology.clear(); - let result = handle_block_sync(&chain_id, block, &wsv, &finalized_wsv); + let result = handle_block_sync(&chain_id, block, &state); assert!(matches!(result, Err((_, BlockSyncError::BlockNotValid(_))))) } @@ -1298,21 +1289,23 @@ mod tests { "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (finalized_wsv, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair); - let mut wsv = finalized_wsv.clone(); + let (state, kura, mut block) = create_data_for_test(&chain_id, &topology, &leader_key_pair); + let mut state_block = state.block(false); let validated_block = - ValidBlock::validate(block.clone(), &topology, &chain_id, &mut wsv).unwrap(); + ValidBlock::validate(block.clone(), &topology, &chain_id, &mut state_block).unwrap(); let committed_block = validated_block.commit(&topology).expect("Block is valid"); - wsv.apply_without_execution(&committed_block) + state_block + .apply_without_execution(&committed_block) .expect("Failed to apply block"); + state_block.commit(); kura.store_block(committed_block); // Malform block to make it invalid payload_mut(&mut block).commit_topology.clear(); + payload_mut(&mut block).header.view_change_index = 1; - let result = handle_block_sync(&chain_id, block, &wsv, &finalized_wsv); + let result = handle_block_sync(&chain_id, block, &state); assert!(matches!( result, Err((_, BlockSyncError::SoftForkBlockNotValid(_))) @@ -1326,14 +1319,12 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let leader_key_pair = KeyPair::random(); - let (finalized_wsv, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair); - let wsv = finalized_wsv.clone(); + let (state, _, mut block) = create_data_for_test(&chain_id, &topology, &leader_key_pair); // Change block height payload_mut(&mut block).header.height = 42; - let result = handle_block_sync(&chain_id, block, &wsv, &finalized_wsv); + let result = handle_block_sync(&chain_id, block, &state); assert!(matches!( result, Err(( @@ -1356,10 +1347,8 @@ mod tests { "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (finalized_wsv, _, block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair); - let wsv = finalized_wsv.clone(); - let result = handle_block_sync(&chain_id, block, &wsv, &finalized_wsv); + let (state, _, block) = create_data_for_test(&chain_id, &topology, &leader_key_pair); + let result = handle_block_sync(&chain_id, block, &state); assert!(matches!(result, Ok(BlockSyncOk::CommitBlock(_, _)))) } @@ -1372,22 +1361,24 @@ mod tests { "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (finalized_wsv, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair); - let mut wsv = finalized_wsv.clone(); + let (state, kura, mut block) = create_data_for_test(&chain_id, &topology, &leader_key_pair); + let mut state_block = state.block(false); let validated_block = - ValidBlock::validate(block.clone(), &topology, &chain_id, &mut wsv).unwrap(); + ValidBlock::validate(block.clone(), &topology, &chain_id, &mut state_block).unwrap(); let committed_block = validated_block.commit(&topology).expect("Block is valid"); - wsv.apply_without_execution(&committed_block) + state_block + .apply_without_execution(&committed_block) .expect("Failed to apply block"); + state_block.commit(); + kura.store_block(committed_block); - assert_eq!(wsv.latest_block_view_change_index(), 0); + assert_eq!(state.view().latest_block_view_change_index(), 0); // Increase block view change index payload_mut(&mut block).header.view_change_index = 42; - let result = handle_block_sync(&chain_id, block, &wsv, &finalized_wsv); + let result = handle_block_sync(&chain_id, block, &state); assert!(matches!(result, Ok(BlockSyncOk::ReplaceTopBlock(_, _)))) } @@ -1400,25 +1391,26 @@ mod tests { "127.0.0.1:8080".parse().unwrap(), leader_key_pair.public_key().clone(), )]); - let (finalized_wsv, kura, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair); - let mut wsv = finalized_wsv.clone(); + let (state, kura, mut block) = create_data_for_test(&chain_id, &topology, &leader_key_pair); // Increase block view change index payload_mut(&mut block).header.view_change_index = 42; + let mut state_block = state.block(false); let validated_block = - ValidBlock::validate(block.clone(), &topology, &chain_id, &mut wsv).unwrap(); + ValidBlock::validate(block.clone(), &topology, &chain_id, &mut state_block).unwrap(); let committed_block = validated_block.commit(&topology).expect("Block is valid"); - wsv.apply_without_execution(&committed_block) + state_block + .apply_without_execution(&committed_block) .expect("Failed to apply block"); + state_block.commit(); kura.store_block(committed_block); - assert_eq!(wsv.latest_block_view_change_index(), 42); + assert_eq!(state.view().latest_block_view_change_index(), 42); // Decrease block view change index back payload_mut(&mut block).header.view_change_index = 0; - let result = handle_block_sync(&chain_id, block, &wsv, &finalized_wsv); + let result = handle_block_sync(&chain_id, block, &state); assert!(matches!( result, Err(( @@ -1438,16 +1430,14 @@ mod tests { let topology = Topology::new(UniqueVec::new()); let leader_key_pair = KeyPair::random(); - let (finalized_wsv, _, mut block) = - create_data_for_test(&chain_id, &topology, &leader_key_pair); - let wsv = finalized_wsv.clone(); + let (state, _, mut block) = create_data_for_test(&chain_id, &topology, &leader_key_pair); // Change block height and view change index // Soft-fork on genesis block is not possible payload_mut(&mut block).header.view_change_index = 42; payload_mut(&mut block).header.height = 1; - let result = handle_block_sync(&chain_id, block, &wsv, &finalized_wsv); + let result = handle_block_sync(&chain_id, block, &state); assert!(matches!( result, Err(( diff --git a/core/src/sumeragi/mod.rs b/core/src/sumeragi/mod.rs index b08525a4ea1..2084fabbc97 100644 --- a/core/src/sumeragi/mod.rs +++ b/core/src/sumeragi/mod.rs @@ -15,9 +15,13 @@ use iroha_genesis::GenesisNetwork; use iroha_logger::prelude::*; use iroha_telemetry::metrics::Metrics; use network_topology::{Role, Topology}; -use tokio::sync::watch; -use crate::{block::ValidBlock, handler::ThreadHandler, kura::BlockCount}; +use crate::{ + block::ValidBlock, + handler::ThreadHandler, + kura::BlockCount, + state::{State, StateBlock}, +}; pub mod main_loop; pub mod message; @@ -42,8 +46,7 @@ struct LastUpdateMetricsData { /// Handle to `Sumeragi` actor #[derive(Clone)] pub struct SumeragiHandle { - public_wsv_receiver: watch::Receiver, - public_finalized_wsv_receiver: watch::Receiver, + state: Arc, metrics: Metrics, last_update_metrics_mutex: Arc>, network: IrohaNetwork, @@ -56,46 +59,6 @@ pub struct SumeragiHandle { } impl SumeragiHandle { - /// Pass closure inside and apply fn to [`WorldStateView`]. - /// This function must be used with very cheap closures. - /// So that it costs no more than cloning wsv. - pub fn apply_wsv(&self, f: impl FnOnce(&WorldStateView) -> T) -> T { - f(&self.public_wsv_receiver.borrow()) - } - - /// Get public clone of [`WorldStateView`]. - pub fn wsv_clone(&self) -> WorldStateView { - self.public_wsv_receiver.borrow().clone() - } - - /// Notify when [`WorldStateView`] is updated. - pub async fn wsv_updated(&mut self) { - self.public_wsv_receiver - .changed() - .await - .expect("Shouldn't return error as long as there is at least one SumeragiHandle"); - } - - /// Pass closure inside and apply fn to finalized [`WorldStateView`]. - /// This function must be used with very cheap closures. - /// So that it costs no more than cloning wsv. - pub fn apply_finalized_wsv(&self, f: impl FnOnce(&WorldStateView) -> T) -> T { - f(&self.public_finalized_wsv_receiver.borrow()) - } - - /// Get public clone of finalized [`WorldStateView`]. - pub fn finalized_wsv_clone(&self) -> WorldStateView { - self.public_finalized_wsv_receiver.borrow().clone() - } - - /// Notify when finalized [`WorldStateView`] is updated. - pub async fn finalized_wsv_updated(&mut self) { - self.public_finalized_wsv_receiver - .changed() - .await - .expect("Shouldn't return error as long as there is at least one SumeragiHandle"); - } - /// Update the metrics on the world state view. /// /// # Errors @@ -114,14 +77,14 @@ impl SumeragiHandle { .try_into() .expect("casting usize to u64"); - let wsv = self.wsv_clone(); + let state_view = self.state.view(); let mut last_guard = self.last_update_metrics_mutex.lock(); let start_index = last_guard.block_height; { let mut block_index = start_index; - while block_index < wsv.height() { + while block_index < state_view.height() { let Some(block) = self.kura.get_block_by_height(block_index + 1) else { break; }; @@ -155,7 +118,7 @@ impl SumeragiHandle { let new_tx_amounts = { let mut new_buf = Vec::new(); - core::mem::swap(&mut new_buf, &mut wsv.new_tx_amounts.lock()); + core::mem::swap(&mut new_buf, &mut state_view.new_tx_amounts.lock()); new_buf }; @@ -164,7 +127,7 @@ impl SumeragiHandle { } #[allow(clippy::cast_possible_truncation)] - if let Some(timestamp) = wsv.genesis_timestamp() { + if let Some(timestamp) = state_view.genesis_timestamp() { // this will overflow in 584942417years. self.metrics.uptime_since_genesis_ms.set( (current_time() - timestamp) @@ -176,9 +139,10 @@ impl SumeragiHandle { self.metrics.connected_peers.set(online_peers_count); - let domains = wsv.domains(); - self.metrics.domains.set(domains.len() as u64); - for domain in domains.values() { + self.metrics + .domains + .set(state_view.world.domains.len() as u64); + for domain in state_view.world.domains() { self.metrics .accounts .get_metric_with_label_values(&[domain.id().name.as_ref()]) @@ -188,7 +152,7 @@ impl SumeragiHandle { self.metrics .view_changes - .set(wsv.latest_block_view_change_index()); + .set(state_view.latest_block_view_change_index()); self.metrics.queue_size.set(self.queue.tx_len() as u64); @@ -227,27 +191,31 @@ impl SumeragiHandle { fn replay_block( chain_id: &ChainId, block: &SignedBlock, - wsv: &mut WorldStateView, + state_block: &mut StateBlock<'_>, mut current_topology: Topology, ) -> Topology { // NOTE: topology need to be updated up to block's view_change_index current_topology.rotate_all_n(block.header().view_change_index); - let block = ValidBlock::validate(block.clone(), ¤t_topology, chain_id, wsv) + let block = ValidBlock::validate(block.clone(), ¤t_topology, chain_id, state_block) .expect("Kura blocks should be valid") .commit(¤t_topology) .expect("Kura blocks should be valid"); if block.as_ref().header().is_genesis() { - wsv.world_mut().trusted_peers_ids = block.as_ref().commit_topology().clone(); + *state_block.world.trusted_peers_ids = block.as_ref().commit_topology().clone(); } - wsv.apply_without_execution(&block).expect( + state_block.apply_without_execution(&block).expect( "Block application in init should not fail. \ Blocks loaded from kura assumed to be valid", ); - Topology::recreate_topology(block.as_ref(), 0, wsv.peers().cloned().collect()) + Topology::recreate_topology( + block.as_ref(), + 0, + state_block.world.peers().cloned().collect(), + ) } /// Start [`Sumeragi`] actor and return handle to it. @@ -260,7 +228,7 @@ impl SumeragiHandle { sumeragi_config, common_config, events_sender, - mut wsv, + state, queue, kura, network, @@ -271,48 +239,50 @@ impl SumeragiHandle { let (control_message_sender, control_message_receiver) = mpsc::sync_channel(100); let (message_sender, message_receiver) = mpsc::sync_channel(100); - let skip_block_count = wsv.block_hashes.len(); - let mut blocks_iter = (skip_block_count + 1..=block_count).map(|block_height| { - kura.get_block_by_height(block_height as u64).expect( - "Sumeragi should be able to load the block that was reported as presented. \ - If not, the block storage was probably disconnected.", - ) - }); + let blocks_iter; + let mut current_topology; - let mut current_topology = match wsv.height() { - 0 => { - assert!(!sumeragi_config.trusted_peers.is_empty()); - Topology::new(sumeragi_config.trusted_peers.clone()) - } - height => { - let block_ref = kura.get_block_by_height(height).expect( - "Sumeragi could not load block that was reported as present. \ - Please check that the block storage was not disconnected.", - ); - Topology::recreate_topology(&block_ref, 0, wsv.peers().cloned().collect()) - } - }; - - let block_iter_except_last = - (&mut blocks_iter).take(block_count.saturating_sub(skip_block_count + 1)); - for block in block_iter_except_last { - current_topology = - Self::replay_block(&common_config.chain_id, &block, &mut wsv, current_topology); + { + let state_view = state.view(); + let skip_block_count = state_view.block_hashes.len(); + blocks_iter = (skip_block_count + 1..=block_count).map(|block_height| { + kura.get_block_by_height(block_height as u64).expect( + "Sumeragi should be able to load the block that was reported as presented. \ + If not, the block storage was probably disconnected.", + ) + }); + + current_topology = match state_view.height() { + 0 => { + assert!(!sumeragi_config.trusted_peers.is_empty()); + Topology::new(sumeragi_config.trusted_peers.clone()) + } + height => { + let block_ref = kura.get_block_by_height(height).expect( + "Sumeragi could not load block that was reported as present. \ + Please check that the block storage was not disconnected.", + ); + Topology::recreate_topology( + &block_ref, + 0, + state_view.world.peers_ids().iter().cloned().collect(), + ) + } + }; } - // finalized_wsv is one block behind - let finalized_wsv = wsv.clone(); - - if let Some(block) = blocks_iter.next() { - current_topology = - Self::replay_block(&common_config.chain_id, &block, &mut wsv, current_topology); + for block in blocks_iter { + let mut state_block = state.block(false); + current_topology = Self::replay_block( + &common_config.chain_id, + &block, + &mut state_block, + current_topology, + ); + state_block.commit(); } - info!("Sumeragi has finished loading blocks and setting up the WSV"); - - let (public_wsv_sender, public_wsv_receiver) = watch::channel(wsv.clone()); - let (public_finalized_wsv_sender, public_finalized_wsv_receiver) = - watch::channel(finalized_wsv.clone()); + info!("Sumeragi has finished loading blocks and setting up the state"); #[cfg(debug_assertions)] let debug_force_soft_fork = sumeragi_config.debug_force_soft_fork; @@ -327,31 +297,30 @@ impl SumeragiHandle { peer_id, queue: Arc::clone(&queue), events_sender, - public_wsv_sender, - public_finalized_wsv_sender, - commit_time: wsv.config.commit_time, - block_time: wsv.config.block_time, - max_txs_in_block: wsv.config.max_transactions_in_block.get() as usize, + commit_time: state.view().config.commit_time, + block_time: state.view().config.block_time, + max_txs_in_block: state.view().config.max_transactions_in_block.get() as usize, kura: Arc::clone(&kura), network: network.clone(), control_message_receiver, message_receiver, debug_force_soft_fork, current_topology, - wsv, - finalized_wsv, transaction_cache: Vec::new(), }; // Oneshot channel to allow forcefully stopping the thread. let (shutdown_sender, shutdown_receiver) = tokio::sync::oneshot::channel(); - let thread_handle = std::thread::Builder::new() - .name("sumeragi thread".to_owned()) - .spawn(move || { - main_loop::run(genesis_network, sumeragi, shutdown_receiver); - }) - .expect("Sumeragi thread spawn should not fail."); + let thread_handle = { + let state = Arc::clone(&state); + std::thread::Builder::new() + .name("sumeragi thread".to_owned()) + .spawn(move || { + main_loop::run(genesis_network, sumeragi, shutdown_receiver, state); + }) + .expect("Sumeragi thread spawn should not fail.") + }; let shutdown = move || { if let Err(error) = shutdown_sender.send(()) { @@ -361,13 +330,12 @@ impl SumeragiHandle { let thread_handle = ThreadHandler::new(Box::new(shutdown), thread_handle); SumeragiHandle { + state, network, queue, kura, control_message_sender, message_sender, - public_wsv_receiver, - public_finalized_wsv_receiver, metrics: Metrics::default(), last_update_metrics_mutex: Arc::new(Mutex::new(LastUpdateMetricsData { block_height: 0, @@ -388,34 +356,34 @@ pub const TELEMETRY_INTERVAL: Duration = Duration::from_secs(5); /// Structure represents a block that is currently in discussion. #[non_exhaustive] -pub struct VotingBlock { +pub struct VotingBlock<'state> { /// At what time has this peer voted for this block pub voted_at: Instant, /// Valid Block pub block: ValidBlock, - /// WSV after applying transactions to it - pub new_wsv: WorldStateView, + /// [`WorldState`] after applying transactions to it but before it was committed + pub state_block: StateBlock<'state>, } -impl VotingBlock { +impl VotingBlock<'_> { /// Construct new `VotingBlock` with current time. - pub fn new(block: ValidBlock, new_wsv: WorldStateView) -> VotingBlock { + pub fn new(block: ValidBlock, state_block: StateBlock<'_>) -> VotingBlock { VotingBlock { block, voted_at: Instant::now(), - new_wsv, + state_block, } } /// Construct new `VotingBlock` with the given time. pub(crate) fn voted_at( block: ValidBlock, - new_wsv: WorldStateView, + state_block: StateBlock<'_>, voted_at: Instant, ) -> VotingBlock { VotingBlock { voted_at, block, - new_wsv, + state_block, } } } @@ -426,7 +394,7 @@ pub struct SumeragiStartArgs { pub sumeragi_config: SumeragiConfig, pub common_config: CommonConfig, pub events_sender: EventsSender, - pub wsv: WorldStateView, + pub state: Arc, pub queue: Arc, pub kura: Arc, pub network: IrohaNetwork, diff --git a/core/src/tx.rs b/core/src/tx.rs index 76397356d1c..1dbe083d776 100644 --- a/core/src/tx.rs +++ b/core/src/tx.rs @@ -20,7 +20,10 @@ use iroha_genesis::GenesisTransaction; use iroha_logger::{debug, error}; use iroha_macro::FromVariant; -use crate::{prelude::*, smartcontracts::wasm}; +use crate::{ + smartcontracts::wasm, + state::{StateBlock, StateTransaction}, +}; /// `AcceptedTransaction` — a transaction accepted by iroha peer. #[derive(Debug, Clone, PartialEq, Eq)] @@ -160,7 +163,7 @@ impl TransactionExecutor { } /// Move transaction lifecycle forward by checking if the - /// instructions can be applied to the `WorldStateView`. + /// instructions can be applied to the [`StateBlock`]. /// /// Validation is skipped for genesis. /// @@ -169,11 +172,13 @@ impl TransactionExecutor { pub fn validate( &self, tx: AcceptedTransaction, - wsv: &mut WorldStateView, + state_block: &mut StateBlock<'_>, ) -> Result { - if let Err(rejection_reason) = self.validate_internal(tx.clone(), wsv) { + let mut state_transaction = state_block.transaction(); + if let Err(rejection_reason) = self.validate_internal(tx.clone(), &mut state_transaction) { return Err((tx.0, rejection_reason)); } + state_transaction.apply(); Ok(tx.0) } @@ -181,11 +186,12 @@ impl TransactionExecutor { fn validate_internal( &self, tx: AcceptedTransaction, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), TransactionRejectionReason> { let authority = tx.as_ref().authority(); - if !wsv + if !state_transaction + .world .domain(&authority.domain_id) .map_err(|_e| { TransactionRejectionReason::AccountDoesNotExist(FindError::Domain( @@ -200,19 +206,13 @@ impl TransactionExecutor { )); } - // Create clone wsv to try execute transaction against it to prevent failed transaction from changing wsv - let mut wsv_for_validation = wsv.clone(); - debug!("Validating transaction: {:?}", tx); - Self::validate_with_runtime_executor(tx.clone(), &mut wsv_for_validation)?; + Self::validate_with_runtime_executor(tx.clone(), state_transaction)?; if let (authority, Executable::Wasm(bytes)) = tx.into() { - self.validate_wasm(authority, &mut wsv_for_validation, bytes)? + self.validate_wasm(authority, state_transaction, bytes)? } - // Replace wsv in case of successful execution - *wsv = wsv_for_validation; - debug!("Validation successful"); Ok(()) } @@ -220,7 +220,7 @@ impl TransactionExecutor { fn validate_wasm( &self, authority: AccountId, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, wasm: WasmSmartContract, ) -> Result<(), TransactionRejectionReason> { debug!("Validating wasm"); @@ -229,7 +229,7 @@ impl TransactionExecutor { .build() .and_then(|mut wasm_runtime| { wasm_runtime.validate( - wsv, + state_transaction, authority, wasm, self.transaction_limits.max_instruction_number, @@ -243,17 +243,18 @@ impl TransactionExecutor { /// Validate transaction with runtime executors. /// - /// Note: transaction instructions will be executed on the given `wsv`. + /// Note: transaction instructions will be executed on the given `state_transaction`. fn validate_with_runtime_executor( tx: AcceptedTransaction, - wsv: &mut WorldStateView, + state_transaction: &mut StateTransaction<'_, '_>, ) -> Result<(), TransactionRejectionReason> { let tx: SignedTransaction = tx.into(); let authority = tx.authority().clone(); - wsv.executor() + state_transaction.world + .executor .clone() // Cloning executor is a cheap operation - .validate_transaction(wsv, &authority, tx) + .validate_transaction(state_transaction, &authority, tx) .map_err(|error| { if let ValidationFail::InternalError(msg) = &error { error!( diff --git a/core/src/wsv.rs b/core/src/wsv.rs deleted file mode 100644 index fbe6730f77f..00000000000 --- a/core/src/wsv.rs +++ /dev/null @@ -1,1432 +0,0 @@ -//! This module provides the [`WorldStateView`] — an in-memory representation of the current blockchain -//! state. -use std::{ - borrow::Borrow, collections::BTreeSet, fmt::Debug, marker::PhantomData, sync::Arc, - time::Duration, -}; - -use eyre::Result; -use indexmap::IndexMap; -use iroha_config::parameters::actual::ChainWide as Config; -use iroha_crypto::HashOf; -use iroha_data_model::{ - account::AccountId, - block::SignedBlock, - events::trigger_completed::{TriggerCompletedEvent, TriggerCompletedOutcome}, - isi::error::{InstructionExecutionError as Error, MathError}, - parameter::{Parameter, ParameterValueBox}, - permission::PermissionTokenSchema, - prelude::*, - query::error::{FindError, QueryExecutionFail}, -}; -use iroha_logger::prelude::*; -use iroha_primitives::{numeric::Numeric, small::SmallVec}; -use parking_lot::Mutex; -use range_bounds::RoleIdByAccountBounds; -use serde::{ - de::{DeserializeSeed, MapAccess, Visitor}, - Deserializer, Serialize, -}; - -use crate::{ - block::CommittedBlock, - executor::Executor, - kura::Kura, - query::store::LiveQueryStoreHandle, - smartcontracts::{ - triggers::{ - self, - set::{LoadedActionTrait, LoadedWasm, Set as TriggerSet}, - }, - wasm, Execute, - }, - tx::TransactionExecutor, - DomainsMap, Parameters, PeersIds, -}; - -/// The global entity consisting of `domains`, `triggers` and etc. -/// For example registration of domain, will have this as an ISI target. -#[derive(Debug, Default, Clone, Serialize)] -pub struct World { - /// Iroha config parameters. - pub(crate) parameters: Parameters, - /// Identifications of discovered trusted peers. - pub(crate) trusted_peers_ids: PeersIds, - /// Registered domains. - pub(crate) domains: DomainsMap, - /// Roles. [`Role`] pairs. - pub(crate) roles: crate::RolesMap, - /// Permission tokens of an account. - pub(crate) account_permission_tokens: crate::PermissionTokensMap, - /// Roles of an account. - pub(crate) account_roles: crate::AccountRolesSet, - /// Registered permission token ids. - pub(crate) permission_token_schema: PermissionTokenSchema, - /// Triggers - pub(crate) triggers: TriggerSet, - /// Runtime Executor - pub(crate) executor: Executor, -} - -// Loader for [`Set`] -#[derive(Clone, Copy)] -pub(crate) struct WasmSeed<'e, T> { - pub engine: &'e wasmtime::Engine, - _marker: PhantomData, -} - -impl<'e, T> WasmSeed<'e, T> { - pub fn cast(&self) -> WasmSeed<'e, U> { - WasmSeed { - engine: self.engine, - _marker: PhantomData, - } - } -} - -impl<'e, 'de, T> DeserializeSeed<'de> for WasmSeed<'e, Option> -where - WasmSeed<'e, T>: DeserializeSeed<'de, Value = T>, -{ - type Value = Option; - - fn deserialize(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct OptionVisitor<'l, T> { - loader: WasmSeed<'l, T>, - _marker: PhantomData, - } - - impl<'e, 'de, T> Visitor<'de> for OptionVisitor<'e, T> - where - WasmSeed<'e, T>: DeserializeSeed<'de, Value = T>, - { - type Value = Option; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct World") - } - - fn visit_none(self) -> Result - where - E: serde::de::Error, - { - Ok(None) - } - - fn visit_some(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - Some(self.loader.deserialize(deserializer)).transpose() - } - } - - let visitor = OptionVisitor { - loader: self.cast::(), - _marker: PhantomData, - }; - deserializer.deserialize_option(visitor) - } -} - -impl<'de> DeserializeSeed<'de> for WasmSeed<'_, World> { - type Value = World; - - fn deserialize(self, deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct WorldVisitor<'l> { - loader: &'l WasmSeed<'l, World>, - } - - impl<'de> Visitor<'de> for WorldVisitor<'_> { - type Value = World; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct World") - } - - fn visit_map(self, mut map: M) -> Result - where - M: MapAccess<'de>, - { - let mut parameters = None; - let mut trusted_peers_ids = None; - let mut domains = None; - let mut roles = None; - let mut account_permission_tokens = None; - let mut account_roles = None; - let mut permission_token_schema = None; - let mut triggers = None; - let mut executor = None; - - while let Some(key) = map.next_key::()? { - match key.as_str() { - "parameters" => { - parameters = Some(map.next_value()?); - } - "trusted_peers_ids" => { - trusted_peers_ids = Some(map.next_value()?); - } - "domains" => { - domains = Some(map.next_value()?); - } - "roles" => { - roles = Some(map.next_value()?); - } - "account_permission_tokens" => { - account_permission_tokens = Some(map.next_value()?); - } - "account_roles" => { - account_roles = Some(map.next_value()?); - } - "permission_token_schema" => { - permission_token_schema = Some(map.next_value()?); - } - "triggers" => { - triggers = Some(map.next_value_seed(self.loader.cast::())?); - } - "executor" => { - executor = Some(map.next_value_seed(self.loader.cast::())?); - } - _ => { /* Skip unknown fields */ } - } - } - - Ok(World { - parameters: parameters - .ok_or_else(|| serde::de::Error::missing_field("parameters"))?, - trusted_peers_ids: trusted_peers_ids - .ok_or_else(|| serde::de::Error::missing_field("trusted_peers_ids"))?, - domains: domains.ok_or_else(|| serde::de::Error::missing_field("domains"))?, - roles: roles.ok_or_else(|| serde::de::Error::missing_field("roles"))?, - account_permission_tokens: account_permission_tokens.ok_or_else(|| { - serde::de::Error::missing_field("account_permission_tokens") - })?, - account_roles: account_roles - .ok_or_else(|| serde::de::Error::missing_field("account_roles"))?, - permission_token_schema: permission_token_schema.ok_or_else(|| { - serde::de::Error::missing_field("permission_token_schema") - })?, - triggers: triggers - .ok_or_else(|| serde::de::Error::missing_field("triggers"))?, - executor: executor - .ok_or_else(|| serde::de::Error::missing_field("executor"))?, - }) - } - } - - deserializer.deserialize_struct( - "World", - &[ - "parameters", - "trusted_peers_ids", - "domains", - "roles", - "account_permission_tokens", - "account_roles", - "permission_token_schema", - "triggers", - "executor", - ], - WorldVisitor { loader: &self }, - ) - } -} - -impl World { - /// Creates an empty `World`. - pub fn new() -> Self { - Self::default() - } - - /// Creates a [`World`] with these [`Domain`]s and trusted [`PeerId`]s. - pub fn with(domains: D, trusted_peers_ids: PeersIds) -> Self - where - D: IntoIterator, - { - let domains = domains - .into_iter() - .map(|domain| (domain.id().clone(), domain)) - .collect(); - World { - trusted_peers_ids, - domains, - ..World::new() - } - } -} - -/// Current state of the blockchain aligned with `Iroha` module. -#[derive(Serialize)] -pub struct WorldStateView { - /// The world. Contains `domains`, `triggers`, `roles` and other data representing the current state of the blockchain. - pub world: World, - /// Configuration of World State View. - pub config: Config, - /// Blockchain. - pub block_hashes: Vec>, - /// Hashes of transactions mapped onto block height where they stored - pub transactions: IndexMap, u64>, - /// Buffer containing events generated during `WorldStateView::apply`. Renewed on every block commit. - #[serde(skip)] - pub events_buffer: Vec, - /// Engine for WASM [`Runtime`](wasm::Runtime) to execute triggers. - #[serde(skip)] - pub engine: wasmtime::Engine, - - /// Reference to Kura subsystem. - #[serde(skip)] - kura: Arc, - /// Handle to the [`LiveQueryStore`]. - #[serde(skip)] - query_handle: LiveQueryStoreHandle, - /// Temporary metrics buffer of amounts of any asset that has been transacted. - #[serde(skip)] - pub new_tx_amounts: Arc>>, -} - -/// Context necessary for deserializing [`WorldStateView`] -pub struct KuraSeed { - /// Kura subsystem reference - pub kura: Arc, - /// Handle to the [`LiveQueryStore`](crate::query::store::LiveQueryStore). - pub query_handle: LiveQueryStoreHandle, -} - -impl<'de> DeserializeSeed<'de> for KuraSeed { - type Value = WorldStateView; - - fn deserialize(self, deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct WorldStateViewVisitor { - loader: KuraSeed, - } - - impl<'de> Visitor<'de> for WorldStateViewVisitor { - type Value = WorldStateView; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct WorldStateView") - } - - fn visit_map(self, mut map: M) -> Result - where - M: MapAccess<'de>, - { - let mut world = None; - let mut config = None; - let mut block_hashes = None; - let mut transactions = None; - - let engine = wasm::create_engine(); - - let wasm_seed: WasmSeed<()> = WasmSeed { - engine: &engine, - _marker: PhantomData, - }; - - while let Some(key) = map.next_key::()? { - match key.as_str() { - "world" => { - world = Some(map.next_value_seed(wasm_seed.cast::())?); - } - "config" => { - config = Some(map.next_value()?); - } - "block_hashes" => { - block_hashes = Some(map.next_value()?); - } - "transactions" => { - transactions = Some(map.next_value()?); - } - _ => { /* Skip unknown fields */ } - } - } - - Ok(WorldStateView { - world: world.ok_or_else(|| serde::de::Error::missing_field("world"))?, - config: config.ok_or_else(|| serde::de::Error::missing_field("config"))?, - block_hashes: block_hashes - .ok_or_else(|| serde::de::Error::missing_field("block_hashes"))?, - transactions: transactions - .ok_or_else(|| serde::de::Error::missing_field("transactions"))?, - kura: self.loader.kura, - query_handle: self.loader.query_handle, - engine, - events_buffer: Vec::new(), - new_tx_amounts: Arc::new(Mutex::new(Vec::new())), - }) - } - } - - deserializer.deserialize_struct( - "WorldStateView", - &["world", "config", "block_hashes", "transactions"], - WorldStateViewVisitor { loader: self }, - ) - } -} - -impl Clone for WorldStateView { - fn clone(&self) -> Self { - Self { - world: Clone::clone(&self.world), - config: self.config, - block_hashes: self.block_hashes.clone(), - transactions: self.transactions.clone(), - events_buffer: Vec::new(), - new_tx_amounts: Arc::clone(&self.new_tx_amounts), - engine: self.engine.clone(), - kura: Arc::clone(&self.kura), - query_handle: self.query_handle.clone(), - } - } -} - -/// WARNING!!! INTERNAL USE ONLY!!! -impl WorldStateView { - /// Construct [`WorldStateView`] with given [`World`]. - #[must_use] - #[inline] - pub fn new(world: World, kura: Arc, query_handle: LiveQueryStoreHandle) -> Self { - // Added to remain backward compatible with other code primary in tests - Self::from_config(Config::default(), world, kura, query_handle) - } - - /// Get `Account`'s `Asset`s - /// - /// # Errors - /// Fails if there is no domain or account - pub fn account_assets( - &self, - id: &AccountId, - ) -> Result, QueryExecutionFail> { - self.map_account(id, |account| account.assets.values()) - } - - /// Get [`Account`]'s [`RoleId`]s - pub fn account_roles(&self, id: &AccountId) -> impl Iterator { - self.world - .account_roles - .range(RoleIdByAccountBounds::new(id)) - .map(|role| &role.role_id) - } - - /// Return a set of all permission tokens granted to this account. - /// - /// # Errors - /// - /// - if `account_id` is not found in `self` - pub fn account_permission_tokens( - &self, - account_id: &AccountId, - ) -> Result, FindError> { - self.account(account_id)?; - - let mut tokens = self - .account_inherent_permission_tokens(account_id) - .collect::>(); - - for role_id in self.account_roles(account_id) { - if let Some(role) = self.world.roles.get(role_id) { - tokens.extend(role.permissions.iter()); - } - } - - Ok(tokens.into_iter()) - } - - /// Return a set of permission tokens granted to this account not as part of any role. - /// - /// # Errors - /// - /// - `account_id` is not found in `self.world`. - pub fn account_inherent_permission_tokens( - &self, - account_id: &AccountId, - ) -> impl ExactSizeIterator { - self.world - .account_permission_tokens - .get(account_id) - .map_or_else(Default::default, std::collections::BTreeSet::iter) - } - - /// Return `true` if [`Account`] contains a permission token not associated with any role. - #[inline] - pub fn account_contains_inherent_permission( - &self, - account: &AccountId, - token: &PermissionToken, - ) -> bool { - self.world - .account_permission_tokens - .get(account) - .map_or(false, |permissions| permissions.contains(token)) - } - - /// Add [`permission`](PermissionToken) to the [`Account`] if the account does not have this permission yet. - /// - /// Return a Boolean value indicating whether or not the [`Account`] already had this permission. - pub fn add_account_permission(&mut self, account: &AccountId, token: PermissionToken) -> bool { - // `match` here instead of `map_or_else` to avoid cloning token into each closure - match self.world.account_permission_tokens.get_mut(account) { - None => { - self.world - .account_permission_tokens - .insert(account.clone(), BTreeSet::from([token])); - true - } - Some(permissions) => { - if permissions.contains(&token) { - return true; - } - permissions.insert(token); - false - } - } - } - - /// Remove a [`permission`](PermissionToken) from the [`Account`] if the account has this permission. - /// Return a Boolean value indicating whether the [`Account`] had this permission. - pub fn remove_account_permission( - &mut self, - account: &AccountId, - token: &PermissionToken, - ) -> bool { - self.world - .account_permission_tokens - .get_mut(account) - .map_or(false, |permissions| permissions.remove(token)) - } - - fn process_trigger( - &mut self, - id: &TriggerId, - action: &dyn LoadedActionTrait, - event: Event, - ) -> Result<()> { - use triggers::set::LoadedExecutable::*; - let authority = action.authority(); - - match action.executable() { - Instructions(instructions) => { - self.process_instructions(instructions.iter().cloned(), authority) - } - Wasm(LoadedWasm { module, .. }) => { - let mut wasm_runtime = wasm::RuntimeBuilder::::new() - .with_config(self.config.wasm_runtime) - .with_engine(self.engine.clone()) // Cloning engine is cheap - .build()?; - wasm_runtime - .execute_trigger_module(self, id, authority.clone(), module, event) - .map_err(Into::into) - } - } - } - - /// Process every trigger in `matched_ids` - fn process_triggers(&mut self) -> Result<(), Vec> { - // Cloning and clearing `self.matched_ids` so that `handle_` call won't deadlock - let matched_ids = self.world.triggers.extract_matched_ids(); - let mut succeed = Vec::::with_capacity(matched_ids.len()); - let mut errors = Vec::new(); - for (event, id) in matched_ids { - // Eliding the closure triggers a lifetime mismatch - #[allow(clippy::redundant_closure_for_method_calls)] - let action = self - .world - .triggers - .inspect_by_id(&id, |action| action.clone_and_box()); - if let Some(action) = action { - if let Repeats::Exactly(repeats) = action.repeats() { - if *repeats == 0 { - continue; - } - } - let wsv = self.clone(); - let event = match self.process_trigger(&id, &action, event) { - Ok(()) => { - succeed.push(id.clone()); - TriggerCompletedEvent::new(id, TriggerCompletedOutcome::Success) - } - Err(error) => { - // Revert to previous state on failure inside trigger - *self = wsv; - let event = TriggerCompletedEvent::new( - id, - TriggerCompletedOutcome::Failure(error.to_string()), - ); - errors.push(error); - event - } - }; - self.events_buffer.push(event.into()); - } - } - - self.world.triggers.decrease_repeats(&succeed); - - errors.is_empty().then_some(()).ok_or(errors) - } - - fn process_executable(&mut self, executable: &Executable, authority: AccountId) -> Result<()> { - match executable { - Executable::Instructions(instructions) => { - self.process_instructions(instructions.iter().cloned(), &authority) - } - Executable::Wasm(bytes) => { - let mut wasm_runtime = wasm::RuntimeBuilder::::new() - .with_config(self.config.wasm_runtime) - .with_engine(self.engine.clone()) // Cloning engine is cheap - .build()?; - wasm_runtime - .execute(self, authority, bytes) - .map_err(Into::into) - } - } - } - - fn process_instructions( - &mut self, - instructions: impl IntoIterator, - authority: &AccountId, - ) -> Result<()> { - instructions.into_iter().try_for_each(|instruction| { - instruction.execute(authority, self)?; - Ok::<_, eyre::Report>(()) - }) - } - - /// Apply `CommittedBlock` with changes in form of **Iroha Special - /// Instructions** to `self`. - /// - /// Order of execution: - /// 1) Transactions - /// 2) Triggers - /// - /// # Errors - /// - /// - (RARE) if applying transaction after validation fails. This - /// scenario is rare, because the `tx` validation implies applying - /// instructions directly to a clone of the wsv. If this happens, - /// you likely have data corruption. - /// - If trigger execution fails - /// - If timestamp conversion to `u64` fails - #[cfg_attr( - not(debug_assertions), - deprecated(note = "This function is to be used in testing only. ") - )] - #[iroha_logger::log(skip_all, fields(block_height))] - pub fn apply(&mut self, block: &CommittedBlock) -> Result<()> { - self.execute_transactions(block)?; - debug!("All block transactions successfully executed"); - - self.apply_without_execution(block)?; - - Ok(()) - } - - /// Apply transactions without actually executing them. - /// It's assumed that block's transaction was already executed (as part of validation for example). - #[iroha_logger::log(skip_all, fields(block_height = block.as_ref().header().height()))] - pub fn apply_without_execution(&mut self, block: &CommittedBlock) -> Result<()> { - let block_hash = block.as_ref().hash(); - trace!(%block_hash, "Applying block"); - - let time_event = self.create_time_event(block); - self.events_buffer.push(Event::Time(time_event)); - - let block_height = block.as_ref().header().height(); - block - .as_ref() - .transactions() - .map(|tx| &tx.value) - .map(SignedTransaction::hash) - .for_each(|tx_hash| { - self.transactions.insert(tx_hash, *block_height); - }); - - self.world.triggers.handle_time_event(time_event); - - let res = self.process_triggers(); - - if let Err(errors) = res { - warn!( - ?errors, - "The following errors have occurred during trigger execution" - ); - } - - self.block_hashes.push(block_hash); - - self.apply_parameters(); - - Ok(()) - } - - fn apply_parameters(&mut self) { - use iroha_data_model::parameter::default::*; - - macro_rules! update_params { - ($($param:expr => $config:expr),+ $(,)?) => { - $(if let Some(param) = self.query_param($param) { - $config = param; - })+ - }; - } - - update_params! { - WSV_ASSET_METADATA_LIMITS => self.config.asset_metadata_limits, - WSV_ASSET_DEFINITION_METADATA_LIMITS => self.config.asset_definition_metadata_limits, - WSV_ACCOUNT_METADATA_LIMITS => self.config.account_metadata_limits, - WSV_DOMAIN_METADATA_LIMITS => self.config.domain_metadata_limits, - WSV_IDENT_LENGTH_LIMITS => self.config.ident_length_limits, - EXECUTOR_FUEL_LIMIT => self.config.executor_runtime.fuel_limit, - EXECUTOR_MAX_MEMORY => self.config.executor_runtime.max_memory_bytes, - WASM_FUEL_LIMIT => self.config.wasm_runtime.fuel_limit, - WASM_MAX_MEMORY => self.config.wasm_runtime.max_memory_bytes, - TRANSACTION_LIMITS => self.config.transaction_limits, - } - } - - /// Get transaction executor - pub fn transaction_executor(&self) -> TransactionExecutor { - TransactionExecutor::new(self.config.transaction_limits) - } - - /// Get a reference to the latest block. Returns none if genesis is not committed. - #[inline] - pub fn latest_block_ref(&self) -> Option> { - self.kura - .get_block_by_height(self.block_hashes.len() as u64) - } - - /// Create time event using previous and current blocks - fn create_time_event(&self, block: &CommittedBlock) -> TimeEvent { - let prev_interval = self.latest_block_ref().map(|latest_block| { - let header = latest_block.header(); - - TimeInterval { - since: header.timestamp(), - length: header.consensus_estimation(), - } - }); - - let interval = TimeInterval { - since: block.as_ref().header().timestamp(), - length: block.as_ref().header().consensus_estimation(), - }; - - TimeEvent { - prev_interval, - interval, - } - } - - /// Execute `block` transactions and store their hashes as well as - /// `rejected_transactions` hashes - /// - /// # Errors - /// Fails if transaction instruction execution fails - fn execute_transactions(&mut self, block: &CommittedBlock) -> Result<()> { - // TODO: Should this block panic instead? - for tx in block.as_ref().transactions() { - if tx.error.is_none() { - self.process_executable( - tx.as_ref().instructions(), - tx.as_ref().authority().clone(), - )?; - } - } - - Ok(()) - } - - /// Get `Asset` by its id - /// - /// # Errors - /// - No such [`Asset`] - /// - The [`Account`] with which the [`Asset`] is associated doesn't exist. - /// - The [`Domain`] with which the [`Account`] is associated doesn't exist. - pub fn asset(&self, id: &AssetId) -> Result { - self.map_account( - &id.account_id, - |account| -> Result { - account - .assets - .get(id) - .ok_or_else(|| QueryExecutionFail::from(FindError::Asset(id.clone()))) - .cloned() - }, - )? - } - - /// Get asset or inserts new with `default_asset_value`. - /// - /// # Errors - /// - There is no account with such name. - pub fn asset_or_insert( - &mut self, - asset_id: AssetId, - default_asset_value: impl Into, - ) -> Result<&mut Asset, Error> { - // Check that asset definition exists - { - let asset_definition_id = &asset_id.definition_id; - let asset_definition_domain_id = &asset_id.definition_id.domain_id; - let asset_definition_domain = self - .world - .domains - .get(asset_definition_domain_id) - .ok_or(FindError::Domain(asset_definition_domain_id.clone()))?; - asset_definition_domain - .asset_definitions - .get(asset_definition_id) - .ok_or(FindError::AssetDefinition(asset_definition_id.clone()))?; - } - - let account_id = &asset_id.account_id; - let account_domain = self - .world - .domains - .get_mut(&asset_id.account_id.domain_id) - .ok_or(FindError::Domain(asset_id.account_id.domain_id.clone()))?; - let account = account_domain - .accounts - .get_mut(account_id) - .ok_or(FindError::Account(account_id.clone()))?; - - Ok(account.assets.entry(asset_id.clone()).or_insert_with(|| { - let asset = Asset::new(asset_id, default_asset_value.into()); - Self::emit_events_impl( - &mut self.world.triggers, - &mut self.events_buffer, - Some(AccountEvent::Asset(AssetEvent::Created(asset.clone()))), - ); - asset - })) - } - - /// Load all blocks in the block chain from disc - pub fn all_blocks(&self) -> impl DoubleEndedIterator> + '_ { - let block_count = self.block_hashes.len() as u64; - (1..=block_count).map(|height| { - self.kura - .get_block_by_height(height) - .expect("Failed to load block.") - }) - } - - /// Return a vector of blockchain blocks after the block with the given `hash` - pub fn block_hashes_after_hash( - &self, - hash: Option>, - ) -> Vec> { - hash.map_or_else( - || self.block_hashes.clone(), - |block_hash| { - self.block_hashes - .iter() - .skip_while(|&x| *x != block_hash) - .skip(1) - .copied() - .collect() - }, - ) - } - - /// Return mutable reference to the [`World`] - pub fn world_mut(&mut self) -> &mut World { - &mut self.world - } - - /// Return an iterator over blockchain block hashes starting with the block of the given `height` - pub fn block_hashes_from_height(&self, height: usize) -> Vec> { - self.block_hashes - .iter() - .skip(height.saturating_sub(1)) - .copied() - .collect() - } - - /// Get `Domain` without an ability to modify it. - /// - /// # Errors - /// Fails if there is no domain - pub fn domain<'wsv>(&'wsv self, id: &DomainId) -> Result<&'wsv Domain, FindError> { - let domain = self - .world - .domains - .get(id) - .ok_or_else(|| FindError::Domain(id.clone()))?; - Ok(domain) - } - - /// Get `Domain` with an ability to modify it. - /// - /// # Errors - /// Fails if there is no domain - pub fn domain_mut(&mut self, id: &DomainId) -> Result<&mut Domain, FindError> { - let domain = self - .world - .domains - .get_mut(id) - .ok_or_else(|| FindError::Domain(id.clone()))?; - Ok(domain) - } - - /// Returns reference for domains map - #[inline] - pub fn domains(&self) -> &DomainsMap { - &self.world.domains - } - - /// Get `Domain` and pass it to closure. - /// - /// # Errors - /// Fails if there is no domain - pub fn map_domain<'wsv, T>( - &'wsv self, - id: &DomainId, - f: impl FnOnce(&'wsv Domain) -> T, - ) -> Result { - let domain = self.domain(id)?; - let value = f(domain); - Ok(value) - } - - /// Get all roles - #[inline] - pub fn roles(&self) -> &crate::RolesMap { - &self.world.roles - } - - /// Get all permission token definitions - #[inline] - pub fn permission_token_schema(&self) -> &crate::PermissionTokenSchema { - &self.world.permission_token_schema - } - - /// Construct [`WorldStateView`] with specific [`Configuration`]. - #[inline] - pub fn from_config( - config: Config, - world: World, - kura: Arc, - query_handle: LiveQueryStoreHandle, - ) -> Self { - Self { - world, - config, - transactions: IndexMap::new(), - block_hashes: Vec::new(), - events_buffer: Vec::new(), - new_tx_amounts: Arc::new(Mutex::new(Vec::new())), - engine: wasm::create_engine(), - kura, - query_handle, - } - } - - /// Returns [`Some`] milliseconds since the genesis block was - /// committed, or [`None`] if it wasn't. - #[inline] - pub fn genesis_timestamp(&self) -> Option { - if self.block_hashes.is_empty() { - None - } else { - let opt = self - .kura - .get_block_by_height(1) - .map(|genesis_block| genesis_block.header().timestamp()); - - if opt.is_none() { - error!("Failed to get genesis block from Kura."); - } - opt - } - } - - /// Check if this [`SignedTransaction`] is already committed or rejected. - #[inline] - pub fn has_transaction(&self, hash: HashOf) -> bool { - self.transactions.contains_key(&hash) - } - - /// Height of blockchain - #[inline] - pub fn height(&self) -> u64 { - self.block_hashes.len() as u64 - } - - /// Return the hash of the latest block - pub fn latest_block_hash(&self) -> Option> { - self.block_hashes.iter().nth_back(0).copied() - } - - /// Return the view change index of the latest block - pub fn latest_block_view_change_index(&self) -> u64 { - self.kura - .get_block_by_height(self.height()) - .map_or(0, |block| *block.header().view_change_index()) - } - - /// Return the hash of the block one before the latest block - pub fn previous_block_hash(&self) -> Option> { - self.block_hashes.iter().nth_back(1).copied() - } - - /// Get `Account` and pass it to closure. - /// - /// # Errors - /// Fails if there is no domain or account - pub fn map_account<'wsv, T>( - &'wsv self, - id: &AccountId, - f: impl FnOnce(&'wsv Account) -> T, - ) -> Result { - let domain = self.domain(&id.domain_id)?; - let account = domain - .accounts - .get(id) - .ok_or(FindError::Account(id.clone()))?; - Ok(f(account)) - } - - /// Get `Account` and return reference to it. - /// - /// # Errors - /// Fails if there is no domain or account - pub fn account(&self, id: &AccountId) -> Result<&Account, FindError> { - self.domain(&id.domain_id).and_then(|domain| { - domain - .accounts - .get(id) - .ok_or_else(|| FindError::Account(id.clone())) - }) - } - - /// Get mutable reference to [`Account`] - /// - /// # Errors - /// Fail if domain or account not found - pub fn account_mut(&mut self, id: &AccountId) -> Result<&mut Account, FindError> { - self.domain_mut(&id.domain_id).and_then(move |domain| { - domain - .accounts - .get_mut(id) - .ok_or_else(|| FindError::Account(id.clone())) - }) - } - - /// Get mutable reference to [`Asset`] - /// - /// # Errors - /// If domain, account or asset not found - pub fn asset_mut(&mut self, id: &AssetId) -> Result<&mut Asset, FindError> { - self.account_mut(&id.account_id).and_then(move |account| { - account - .assets - .get_mut(id) - .ok_or_else(|| FindError::Asset(id.clone())) - }) - } - - /// Get mutable reference to [`AssetDefinition`] - /// - /// # Errors - /// If domain or asset definition not found - pub fn asset_definition_mut( - &mut self, - id: &AssetDefinitionId, - ) -> Result<&mut AssetDefinition, FindError> { - self.domain_mut(&id.domain_id).and_then(|domain| { - domain - .asset_definitions - .get_mut(id) - .ok_or_else(|| FindError::AssetDefinition(id.clone())) - }) - } - - /// Get an immutable iterator over the [`PeerId`]s. - pub fn peers(&self) -> impl ExactSizeIterator { - self.world.trusted_peers_ids.iter() - } - - /// Get all `Parameter`s registered in the world. - pub fn parameters(&self) -> impl ExactSizeIterator { - self.world.parameters.iter() - } - - /// Query parameter and convert it to a proper type - pub fn query_param, P: core::hash::Hash + Eq + ?Sized>( - &self, - param: &P, - ) -> Option - where - Parameter: Borrow

, - { - self.world - .parameters - .get(param) - .as_ref() - .map(|param| ¶m.val) - .cloned() - .and_then(|param_val| param_val.try_into().ok()) - } - - /// Get `AssetDefinition` immutable view. - /// - /// # Errors - /// - Asset definition entry not found - pub fn asset_definition( - &self, - asset_id: &AssetDefinitionId, - ) -> Result { - self.domain(&asset_id.domain_id)? - .asset_definitions - .get(asset_id) - .ok_or_else(|| FindError::AssetDefinition(asset_id.clone())) - .cloned() - } - - /// Get total amount of [`Asset`]. - /// - /// # Errors - /// - Asset definition not found - pub fn asset_total_amount( - &self, - definition_id: &AssetDefinitionId, - ) -> Result { - self.domain(&definition_id.domain_id)? - .asset_total_quantities - .get(definition_id) - .ok_or_else(|| FindError::AssetDefinition(definition_id.clone())) - .copied() - } - - /// Increase [`Asset`] total amount by given value - /// - /// # Errors - /// - [`AssetDefinition`], [`Domain`] not found - /// - Overflow - pub fn increase_asset_total_amount( - &mut self, - definition_id: &AssetDefinitionId, - increment: Numeric, - ) -> Result<(), Error> { - let domain = self.domain_mut(&definition_id.domain_id)?; - let asset_total_amount: &mut Numeric = domain - .asset_total_quantities.get_mut(definition_id) - .expect("Asset total amount not being found is a bug: check `Register` to insert initial total amount"); - *asset_total_amount = asset_total_amount - .checked_add(increment) - .ok_or(MathError::Overflow)?; - let asset_total_amount = *asset_total_amount; - - self.emit_events({ - Some(DomainEvent::AssetDefinition( - AssetDefinitionEvent::TotalQuantityChanged(AssetDefinitionTotalQuantityChanged { - asset_definition_id: definition_id.clone(), - total_amount: asset_total_amount, - }), - )) - }); - - Ok(()) - } - - /// Decrease [`Asset`] total amount by given value - /// - /// # Errors - /// - [`AssetDefinition`], [`Domain`] not found - /// - Not enough quantity - pub fn decrease_asset_total_amount( - &mut self, - definition_id: &AssetDefinitionId, - decrement: Numeric, - ) -> Result<(), Error> { - let domain = self.domain_mut(&definition_id.domain_id)?; - let asset_total_amount: &mut Numeric = domain - .asset_total_quantities.get_mut(definition_id) - .expect("Asset total amount not being found is a bug: check `Register` to insert initial total amount"); - *asset_total_amount = asset_total_amount - .checked_sub(decrement) - .ok_or(MathError::NotEnoughQuantity)?; - let asset_total_amount = *asset_total_amount; - - self.emit_events({ - Some(DomainEvent::AssetDefinition( - AssetDefinitionEvent::TotalQuantityChanged(AssetDefinitionTotalQuantityChanged { - asset_definition_id: definition_id.clone(), - total_amount: asset_total_amount, - }), - )) - }); - - Ok(()) - } - - /// Find a [`SignedBlock`] by hash. - pub fn block_with_tx(&self, hash: &HashOf) -> Option> { - let height = *self.transactions.get(hash)?; - self.kura.get_block_by_height(height) - } - - /// Get an immutable view of the `World`. - #[must_use] - #[inline] - pub fn world(&self) -> &World { - &self.world - } - - /// Returns reference for triggers - #[inline] - pub fn triggers(&self) -> &TriggerSet { - &self.world.triggers - } - - /// Return mutable reference for triggers - #[inline] - pub fn triggers_mut(&mut self) -> &mut TriggerSet { - &mut self.world.triggers - } - - /// Execute trigger with `trigger_id` as id and `authority` as owner - /// - /// Produces [`ExecuteTriggerEvent`]. - /// - /// Trigger execution time: - /// - If this method is called by ISI inside *transaction*, - /// then *trigger* will be executed on the **current** block - /// - If this method is called by ISI inside *trigger*, - /// then *trigger* will be executed on the **next** block - pub fn execute_trigger(&mut self, trigger_id: TriggerId, authority: &AccountId) { - let event = ExecuteTriggerEvent { - trigger_id, - authority: authority.clone(), - }; - - self.world - .triggers - .handle_execute_trigger_event(event.clone()); - self.events_buffer.push(event.into()); - } - - /// Get [`Executor`]. - pub fn executor(&self) -> &Executor { - &self.world.executor - } - - /// The function puts events produced by iterator into `events_buffer`. - /// Events should be produced in the order of expanding scope: from specific to general. - /// Example: account events before domain events. - pub fn emit_events, T: Into>(&mut self, world_events: I) { - Self::emit_events_impl( - &mut self.world.triggers, - &mut self.events_buffer, - world_events, - ) - } - - /// Implementation of [`Self::emit_events()`]. - /// - /// Usable when you can't call [`Self::emit_events()`] due to mutable reference to self. - fn emit_events_impl, T: Into>( - triggers: &mut TriggerSet, - events_buffer: &mut Vec, - world_events: I, - ) { - let data_events: SmallVec<[DataEvent; 3]> = world_events - .into_iter() - .map(Into::into) - .map(Into::into) - .collect(); - - for event in data_events.iter() { - triggers.handle_data_event(event.clone()); - } - events_buffer.extend(data_events.into_iter().map(Into::into)); - } - - /// Set new permission token schema. - /// - /// Produces [`PermissionTokenSchemaUpdateEvent`]. - pub fn set_permission_token_schema(&mut self, schema: PermissionTokenSchema) { - let old_schema = std::mem::replace(&mut self.world.permission_token_schema, schema.clone()); - self.emit_events(std::iter::once(DataEvent::PermissionToken( - PermissionTokenSchemaUpdateEvent { - old_schema, - new_schema: schema, - }, - ))) - } - - /// Get reference to the [`LiveQueryStoreHandle`]. - pub fn query_handle(&self) -> &LiveQueryStoreHandle { - &self.query_handle - } -} - -/// Bounds for `range` queries -mod range_bounds { - use core::ops::{Bound, RangeBounds}; - - use iroha_primitives::{cmpext::MinMaxExt, impl_as_dyn_key}; - - use super::*; - use crate::role::RoleIdWithOwner; - - /// Key for range queries over account for roles - #[derive(PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] - pub struct RoleIdByAccount<'role> { - account_id: &'role AccountId, - role_id: MinMaxExt<&'role RoleId>, - } - - /// Bounds for range quired over account for roles - pub struct RoleIdByAccountBounds<'role> { - start: RoleIdByAccount<'role>, - end: RoleIdByAccount<'role>, - } - - impl<'role> RoleIdByAccountBounds<'role> { - /// Create range bounds for range quires of roles over account - pub fn new(account_id: &'role AccountId) -> Self { - Self { - start: RoleIdByAccount { - account_id, - role_id: MinMaxExt::Min, - }, - end: RoleIdByAccount { - account_id, - role_id: MinMaxExt::Max, - }, - } - } - } - - impl<'role> RangeBounds for RoleIdByAccountBounds<'role> { - fn start_bound(&self) -> Bound<&(dyn AsRoleIdByAccount + 'role)> { - Bound::Excluded(&self.start) - } - - fn end_bound(&self) -> Bound<&(dyn AsRoleIdByAccount + 'role)> { - Bound::Excluded(&self.end) - } - } - - impl AsRoleIdByAccount for RoleIdWithOwner { - fn as_key(&self) -> RoleIdByAccount<'_> { - RoleIdByAccount { - account_id: &self.account_id, - role_id: (&self.role_id).into(), - } - } - } - - impl_as_dyn_key! { - target: RoleIdWithOwner, - key: RoleIdByAccount<'_>, - trait: AsRoleIdByAccount - } -} - -#[cfg(test)] -mod tests { - use iroha_data_model::block::BlockPayload; - use iroha_primitives::unique_vec::UniqueVec; - - use super::*; - use crate::{ - block::ValidBlock, query::store::LiveQueryStore, role::RoleIdWithOwner, - sumeragi::network_topology::Topology, - }; - - /// Used to inject faulty payload for testing - fn payload_mut(block: &mut CommittedBlock) -> &mut BlockPayload { - let SignedBlock::V1(signed) = &mut block.0 .0; - &mut signed.payload - } - - #[tokio::test] - async fn get_block_hashes_after_hash() { - const BLOCK_CNT: usize = 10; - - let topology = Topology::new(UniqueVec::new()); - let mut block = ValidBlock::new_dummy().commit(&topology).unwrap(); - let kura = Kura::blank_kura_for_testing(); - let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(World::default(), kura, query_handle); - - let mut block_hashes = vec![]; - for i in 1..=BLOCK_CNT { - payload_mut(&mut block).header.height = i as u64; - payload_mut(&mut block).header.previous_block_hash = block_hashes.last().copied(); - - block_hashes.push(block.as_ref().hash()); - wsv.apply(&block).unwrap(); - } - - assert!(wsv - .block_hashes_after_hash(Some(block_hashes[6])) - .into_iter() - .eq(block_hashes.into_iter().skip(7))); - } - - #[tokio::test] - async fn get_blocks_from_height() { - const BLOCK_CNT: usize = 10; - - let topology = Topology::new(UniqueVec::new()); - let block = ValidBlock::new_dummy().commit(&topology).unwrap(); - let kura = Kura::blank_kura_for_testing(); - let query_handle = LiveQueryStore::test().start(); - let mut wsv = WorldStateView::new(World::default(), kura.clone(), query_handle); - - for i in 1..=BLOCK_CNT { - let mut block = block.clone(); - payload_mut(&mut block).header.height = i as u64; - - wsv.apply(&block).unwrap(); - kura.store_block(block); - } - - assert_eq!( - &wsv.all_blocks() - .skip(7) - .map(|block| *block.header().height()) - .collect::>(), - &[8, 9, 10] - ); - } - - #[test] - fn role_account_range() { - let account_id: AccountId = "alice@wonderland".parse().unwrap(); - let roles = [ - RoleIdWithOwner::new(account_id.clone(), "1".parse().unwrap()), - RoleIdWithOwner::new(account_id.clone(), "2".parse().unwrap()), - RoleIdWithOwner::new("bob@wonderland".parse().unwrap(), "3".parse().unwrap()), - RoleIdWithOwner::new("a@wonderland".parse().unwrap(), "4".parse().unwrap()), - RoleIdWithOwner::new("0@0".parse().unwrap(), "5".parse().unwrap()), - RoleIdWithOwner::new("1@1".parse().unwrap(), "6".parse().unwrap()), - ]; - let map = BTreeSet::from(roles); - - let range = map - .range(RoleIdByAccountBounds::new(&account_id)) - .collect::>(); - assert_eq!(range.len(), 2); - for role in range { - assert_eq!(&role.account_id, &account_id); - } - } -} diff --git a/data_model/src/events/data/filters.rs b/data_model/src/events/data/filters.rs index 77aed5d8558..4edc08c828e 100644 --- a/data_model/src/events/data/filters.rs +++ b/data_model/src/events/data/filters.rs @@ -688,6 +688,7 @@ impl EventFilter for DataEventFilter { fn matches(&self, event: &DataEvent) -> bool { use DataEventFilter::*; + #[allow(clippy::match_same_arms)] match (event, self) { ( DataEvent::Domain(DomainEvent::Account(AccountEvent::Asset(event))), diff --git a/data_model/src/events/time.rs b/data_model/src/events/time.rs index 65ed91682ad..fad7e76a7d2 100644 --- a/data_model/src/events/time.rs +++ b/data_model/src/events/time.rs @@ -12,7 +12,7 @@ use super::*; pub mod model { use super::*; - /// Special event that is emitted when `WSV` is ready for handling time-triggers + /// Special event that is emitted when state is ready for handling time-triggers /// /// Contains time interval which is used to identify time-triggers to be executed #[derive( diff --git a/telemetry/derive/src/lib.rs b/telemetry/derive/src/lib.rs index 48fbef3e848..6ce4c5cdb10 100644 --- a/telemetry/derive/src/lib.rs +++ b/telemetry/derive/src/lib.rs @@ -13,13 +13,12 @@ use syn::{parse::Parse, punctuated::Punctuated, token::Comma, FnArg, LitStr, Pat const TOTAL_STR: &str = "total"; #[cfg(feature = "metric-instrumentation")] const SUCCESS_STR: &str = "success"; -const WSV_STRING: &str = "WorldStateView"; fn type_has_metrics_field(ty: &Type) -> bool { match ty { // This may seem fragile, but it isn't. We use the same convention // everywhere in the code base, and if you follow `CONTRIBUTING.md` - // you'll likely have `use iroha_core::WorldStateView` + // you'll likely have `use iroha_core::{StateTransaction, StateSnapshot}` // somewhere. If you don't, you're violating the `CONTRIBUTING.md` in // more than one way. Type::Path(pth) => { @@ -28,7 +27,7 @@ fn type_has_metrics_field(ty: &Type) -> bool { .last() .expect("Should have at least one segment") .ident; - *type_name == WSV_STRING + *type_name == "StateSnapshot" || *type_name == "StateTransaction" } _ => false, } @@ -38,7 +37,7 @@ fn type_has_metrics_field(ty: &Type) -> bool { /// metrics. /// /// # Errors -/// If no argument is of type `WorldStateView`. +/// If no argument is of type `StateTransaction` of `StateSnapshot`. fn arg_metrics(input: &Punctuated) -> Result> { input .iter() @@ -132,11 +131,11 @@ impl ToTokens for MetricSpec { /// # Examples /// /// ```rust -/// use iroha_core::wsv::{World, WorldStateView}; +/// use iroha_core::state::{World, StateTransaction}; /// use iroha_telemetry_derive::metrics; /// /// #[metrics(+"test_query", "another_test_query_without_timing")] -/// fn execute(wsv: &WorldStateView) -> Result<(), ()> { +/// fn execute(state: &StateTransaction) -> Result<(), ()> { /// Ok(()) /// } /// ``` @@ -162,7 +161,7 @@ pub fn metrics(attr: TokenStream, item: TokenStream) -> TokenStream { emit!( emitter, func.sig, - "Function must have at least one argument of type `WorldStateView`." + "Function must have at least one argument of type `StateTransaction` or `StateSnapshot`." ); return emitter.finish_token_stream(); } @@ -220,7 +219,7 @@ fn impl_metrics(emitter: &mut Emitter, _specs: &MetricSpecs, func: &syn::ItemFn) emit!( emitter, args, - "At least one argument must be a `WorldStateView`." + "At least one argument must be a `StateTransaction` or `StateSnapshot`." ); return quote!(); } diff --git a/telemetry/derive/tests/ui_fail/args_no_wsv.rs b/telemetry/derive/tests/ui_fail/args_no_wsv.rs index d85e46478e6..5539e3621e3 100644 --- a/telemetry/derive/tests/ui_fail/args_no_wsv.rs +++ b/telemetry/derive/tests/ui_fail/args_no_wsv.rs @@ -1,7 +1,7 @@ use iroha_telemetry_derive::metrics; #[metrics(+"test_query", "another_test_query_without_timing")] -fn execute(_wsv: &World) -> Result<(), ()> { +fn execute(_state: &World) -> Result<(), ()> { Ok(()) } diff --git a/telemetry/derive/tests/ui_fail/args_no_wsv.stderr b/telemetry/derive/tests/ui_fail/args_no_wsv.stderr index 4aa2e1da1c3..efe4a8f09a3 100644 --- a/telemetry/derive/tests/ui_fail/args_no_wsv.stderr +++ b/telemetry/derive/tests/ui_fail/args_no_wsv.stderr @@ -1,5 +1,5 @@ -error: At least one argument must be a `WorldStateView`. +error: At least one argument must be a `StateTransaction` or `StateSnapshot`. --> tests/ui_fail/args_no_wsv.rs:4:12 | -4 | fn execute(_wsv: &World) -> Result<(), ()> { - | ^^^^^^^^^^^^ +4 | fn execute(_state: &World) -> Result<(), ()> { + | ^^^^^^^^^^^^^^ diff --git a/telemetry/derive/tests/ui_fail/bare_spec.rs b/telemetry/derive/tests/ui_fail/bare_spec.rs index bb6029fddf1..b5aa7f3c3d8 100644 --- a/telemetry/derive/tests/ui_fail/bare_spec.rs +++ b/telemetry/derive/tests/ui_fail/bare_spec.rs @@ -1,7 +1,7 @@ use iroha_telemetry_derive::metrics; #[metrics(test_query, "another_test_query_without_timing")] -fn execute(wsv: &WorldStateView) -> Result<(), ()> { +fn execute(state: &StateTransaction) -> Result<(), ()> { Ok(()) } diff --git a/telemetry/derive/tests/ui_fail/doubled_plus.rs b/telemetry/derive/tests/ui_fail/doubled_plus.rs index 61db9e0dda1..9d369db3182 100644 --- a/telemetry/derive/tests/ui_fail/doubled_plus.rs +++ b/telemetry/derive/tests/ui_fail/doubled_plus.rs @@ -1,7 +1,7 @@ use iroha_telemetry_derive::metrics; #[metrics(+"test_query", ++"another_test_query_without_timing")] -fn execute(wsv: &WorldStateView) -> Result<(), ()> { +fn execute(state: &StateTransaction) -> Result<(), ()> { Ok(()) } diff --git a/telemetry/derive/tests/ui_fail/no_args.stderr b/telemetry/derive/tests/ui_fail/no_args.stderr index bf2d6e9b557..db72e9a9c2b 100644 --- a/telemetry/derive/tests/ui_fail/no_args.stderr +++ b/telemetry/derive/tests/ui_fail/no_args.stderr @@ -1,4 +1,4 @@ -error: Function must have at least one argument of type `WorldStateView`. +error: Function must have at least one argument of type `StateTransaction` or `StateSnapshot`. --> tests/ui_fail/no_args.rs:4:1 | 4 | fn execute() -> Result<(), ()> { diff --git a/telemetry/derive/tests/ui_fail/non_snake_case_name.rs b/telemetry/derive/tests/ui_fail/non_snake_case_name.rs index 97c83ab152f..bf1a61a8b3d 100644 --- a/telemetry/derive/tests/ui_fail/non_snake_case_name.rs +++ b/telemetry/derive/tests/ui_fail/non_snake_case_name.rs @@ -1,7 +1,7 @@ use iroha_telemetry_derive::metrics; #[metrics(+"test query", "another_test_query_without_timing")] -fn execute(wsv: &WorldStateView) -> Result<(), ()> { +fn execute(state_transaction: &StateTransaction) -> Result<(), ()> { Ok(()) } diff --git a/telemetry/derive/tests/ui_fail/not_execute.rs b/telemetry/derive/tests/ui_fail/not_execute.rs index ddd566862bd..c48342a932a 100644 --- a/telemetry/derive/tests/ui_fail/not_execute.rs +++ b/telemetry/derive/tests/ui_fail/not_execute.rs @@ -1,8 +1,8 @@ use iroha_telemetry_derive::metrics; -use iroha_core::prelude::WorldStateView; +use iroha_core::state::StateTransaction; #[metrics(+"test_query", "another_test_query_without_timing")] -fn exequte(wsv: &WorldStateView) -> Result<(), ()> { +fn exequte(state_transaction: &StateTransaction) -> Result<(), ()> { Ok(()) } diff --git a/telemetry/derive/tests/ui_fail/not_execute.stderr b/telemetry/derive/tests/ui_fail/not_execute.stderr index 4146a4ab62b..87ebf593be8 100644 --- a/telemetry/derive/tests/ui_fail/not_execute.stderr +++ b/telemetry/derive/tests/ui_fail/not_execute.stderr @@ -1,5 +1,5 @@ error: Function should be an `impl execute` --> tests/ui_fail/not_execute.rs:5:4 | -5 | fn exequte(wsv: &WorldStateView) -> Result<(), ()> { +5 | fn exequte(state_transaction: &StateTransaction) -> Result<(), ()> { | ^^^^^^^ diff --git a/telemetry/derive/tests/ui_fail/not_return_result.rs b/telemetry/derive/tests/ui_fail/not_return_result.rs index b5620e99f63..a970bd8aadf 100644 --- a/telemetry/derive/tests/ui_fail/not_return_result.rs +++ b/telemetry/derive/tests/ui_fail/not_return_result.rs @@ -1,10 +1,10 @@ use iroha_telemetry_derive::metrics; -use iroha_core::prelude::WorldStateView; +use iroha_core::state::StateTransaction; type MyNotResult = Option; #[metrics(+"test_query", "another_test_query_without_timing")] -fn execute(_wsv: &WorldStateView) -> MyNotResult { +fn execute(_state_transaction: &StateTransaction) -> MyNotResult { None } diff --git a/telemetry/derive/tests/ui_fail/not_return_result.stderr b/telemetry/derive/tests/ui_fail/not_return_result.stderr index 09daedb0d51..ec4fed2ce5e 100644 --- a/telemetry/derive/tests/ui_fail/not_return_result.stderr +++ b/telemetry/derive/tests/ui_fail/not_return_result.stderr @@ -1,5 +1,5 @@ error: Should return `Result`. Found MyNotResult - --> tests/ui_fail/not_return_result.rs:7:38 + --> tests/ui_fail/not_return_result.rs:7:54 | -7 | fn execute(_wsv: &WorldStateView) -> MyNotResult { - | ^^^^^^^^^^^ +7 | fn execute(_state_transaction: &StateTransaction) -> MyNotResult { + | ^^^^^^^^^^^ diff --git a/telemetry/derive/tests/ui_fail/return_nothing.rs b/telemetry/derive/tests/ui_fail/return_nothing.rs index 6058f160b19..c2b8926bb85 100644 --- a/telemetry/derive/tests/ui_fail/return_nothing.rs +++ b/telemetry/derive/tests/ui_fail/return_nothing.rs @@ -1,8 +1,8 @@ use iroha_telemetry_derive::metrics; -use iroha_core::prelude::WorldStateView; +use iroha_core::state::StateTransaction; #[metrics(+"test_query", "another_test_query_without_timing")] -fn execute(wsv: &WorldStateView) { +fn execute(state_transaction: &StateTransaction) { Ok::<(), ()>(()); } diff --git a/telemetry/derive/tests/ui_fail/trailing_plus.rs b/telemetry/derive/tests/ui_fail/trailing_plus.rs index 3034f0c7f1d..e84882a1676 100644 --- a/telemetry/derive/tests/ui_fail/trailing_plus.rs +++ b/telemetry/derive/tests/ui_fail/trailing_plus.rs @@ -1,7 +1,7 @@ use iroha_telemetry_derive::metrics; #[metrics(+"test_query", "another_test_query_without_timing"+)] -fn execute(wsv: &WorldStateView) -> Result<(), ()> { +fn execute(state_transaction: &StateTransaction) -> Result<(), ()> { Ok(()) } diff --git a/torii/src/lib.rs b/torii/src/lib.rs index 0e770b2667a..46b37d5cfe0 100644 --- a/torii/src/lib.rs +++ b/torii/src/lib.rs @@ -20,6 +20,7 @@ use iroha_core::{ prelude::*, query::store::LiveQueryStoreHandle, queue::{self, Queue}, + state::State, sumeragi::SumeragiHandle, EventsSender, }; @@ -53,6 +54,7 @@ pub struct Torii { kura: Arc, transaction_max_content_length: u64, address: SocketAddr, + state: Arc, } impl Torii { @@ -68,6 +70,7 @@ impl Torii { sumeragi: SumeragiHandle, query_service: LiveQueryStoreHandle, kura: Arc, + state: Arc, ) -> Self { Self { chain_id: Arc::new(chain_id), @@ -78,6 +81,7 @@ impl Torii { sumeragi, query_service, kura, + state, address: config.address, transaction_max_content_length: config.max_content_len_bytes, } @@ -119,9 +123,9 @@ impl Torii { ))) })) .or(warp::path(uri::API_VERSION) - .and(add_state!(self.sumeragi.clone())) - .and_then(|sumeragi| async { - Ok::<_, Infallible>(routing::handle_version(sumeragi).await) + .and(add_state!(self.state.clone())) + .and_then(|state| async { + Ok::<_, Infallible>(routing::handle_version(state).await) })); #[cfg(feature = "schema")] @@ -157,7 +161,7 @@ impl Torii { endpoint4( routing::handle_transaction, warp::path(uri::TRANSACTION) - .and(add_state!(self.chain_id, self.queue, self.sumeragi)) + .and(add_state!(self.chain_id, self.queue, self.state.clone())) .and(warp::body::content_length_limit( self.transaction_max_content_length, )) @@ -166,7 +170,7 @@ impl Torii { .or(endpoint3( routing::handle_queries, warp::path(uri::QUERY) - .and(add_state!(self.query_service, self.sumeragi,)) + .and(add_state!(self.query_service, self.state.clone(),)) .and(routing::client_query_request()), )) .or(endpoint2( diff --git a/torii/src/routing.rs b/torii/src/routing.rs index 02c1a9685c0..87a164c8398 100644 --- a/torii/src/routing.rs +++ b/torii/src/routing.rs @@ -77,15 +77,15 @@ fn fetch_size() -> impl warp::Filter, queue: Arc, - sumeragi: SumeragiHandle, + state: Arc, transaction: SignedTransaction, ) -> Result { - let wsv = sumeragi.wsv_clone(); - let transaction_limits = wsv.config.transaction_limits; + let state_view = state.view(); + let transaction_limits = state_view.config.transaction_limits; let transaction = AcceptedTransaction::accept(transaction, &chain_id, &transaction_limits) .map_err(Error::AcceptTransaction)?; queue - .push(transaction, &wsv) + .push(transaction, &state_view) .map_err(|queue::Failure { tx, err }| { iroha_logger::warn!( tx_hash=%tx.as_ref().hash(), ?err, @@ -101,26 +101,29 @@ pub async fn handle_transaction( #[iroha_futures::telemetry_future] pub async fn handle_queries( live_query_store: LiveQueryStoreHandle, - sumeragi: SumeragiHandle, - + state: Arc, query_request: http::ClientQueryRequest, ) -> Result>> { - let handle = task::spawn_blocking(move || match query_request.0 { - QueryRequest::Query(QueryWithParameters { - query: signed_query, - sorting, - pagination, - fetch_size, - }) => sumeragi.apply_wsv(|wsv| { - let valid_query = ValidQueryRequest::validate(signed_query, wsv)?; - let query_output = valid_query.execute(wsv)?; - live_query_store - .handle_query_output(query_output, &sorting, pagination, fetch_size) - .map_err(ValidationFail::from) - }), - QueryRequest::Cursor(cursor) => live_query_store - .handle_query_cursor(cursor) - .map_err(ValidationFail::from), + let handle = task::spawn_blocking(move || { + let state_view = state.view(); + let state_snapshot = state_view.to_snapshot(); + match query_request.0 { + QueryRequest::Query(QueryWithParameters { + query: signed_query, + sorting, + pagination, + fetch_size, + }) => { + let valid_query = ValidQueryRequest::validate(signed_query, &state_snapshot)?; + let query_output = valid_query.execute(&state_snapshot)?; + live_query_store + .handle_query_output(query_output, &sorting, pagination, fetch_size) + .map_err(ValidationFail::from) + } + QueryRequest::Cursor(cursor) => live_query_store + .handle_query_cursor(cursor) + .map_err(ValidationFail::from), + } }); handle .await @@ -282,11 +285,12 @@ pub mod subscription { #[iroha_futures::telemetry_future] #[cfg(feature = "telemetry")] -pub async fn handle_version(sumeragi: SumeragiHandle) -> Json { +pub async fn handle_version(state: Arc) -> Json { use iroha_version::Version; - let string = sumeragi - .apply_wsv(WorldStateView::latest_block_ref) + let state_view = state.view(); + let string = state_view + .latest_block_ref() .expect("Genesis not applied. Nothing we can do. Solve the issue and rerun.") .version() .to_string();