From 38251a9a8f8a3472e38887dc6d4b618cb764c450 Mon Sep 17 00:00:00 2001 From: Slava Date: Wed, 8 Apr 2026 10:06:26 +0300 Subject: [PATCH] Fixes for transaction execution --- src/block/src/inbound_messages.rs | 11 ++- src/emulator/src/lib.rs | 2 + .../due_payment_in_smc_account_new.boc | Bin 0 -> 3466 bytes .../due_payment_in_smc_account_old.boc | Bin 0 -> 3452 bytes .../due_payment_in_smc_transaction.boc | Bin 0 -> 710 bytes .../fwd_fee_payment_in_smc_account_new.boc | Bin 0 -> 629 bytes .../fwd_fee_payment_in_smc_account_old.boc | Bin 0 -> 630 bytes .../fwd_fee_payment_in_smc_transaction.boc | Bin 0 -> 796 bytes src/executor/src/blockchain_config.rs | 4 + src/executor/src/ordinary_transaction.rs | 1 + src/executor/src/tests/common/mod.rs | 25 ++---- ...est_transaction_executor_with_real_data.rs | 63 ++++++++++++++- src/executor/src/tick_tock_transaction.rs | 1 + src/node/src/validator/collator.rs | 72 +++++++++++++----- src/node/src/validator/validate_query.rs | 10 ++- src/vm/src/smart_contract_info.rs | 2 +- src/vm/src/tests/test_smart_contract_info.rs | 34 ++++++++- 17 files changed, 175 insertions(+), 50 deletions(-) create mode 100644 src/executor/real_boc/due_payment_in_smc_account_new.boc create mode 100644 src/executor/real_boc/due_payment_in_smc_account_old.boc create mode 100644 src/executor/real_boc/due_payment_in_smc_transaction.boc create mode 100644 src/executor/real_boc/fwd_fee_payment_in_smc_account_new.boc create mode 100644 src/executor/real_boc/fwd_fee_payment_in_smc_account_old.boc create mode 100644 src/executor/real_boc/fwd_fee_payment_in_smc_transaction.boc diff --git a/src/block/src/inbound_messages.rs b/src/block/src/inbound_messages.rs index 410f2a2..ef0f26c 100644 --- a/src/block/src/inbound_messages.rs +++ b/src/block/src/inbound_messages.rs @@ -470,10 +470,15 @@ impl Augmentation for InMsg { fees.value_imported.coins = header.fwd_fee; } - InMsg::DeferredFinal(_) => { - fees.fees_collected = header.fwd_fee; + InMsg::DeferredFinal(x) => { + let env = x.read_envelope_message()?; + if env.fwd_fee_remaining() != x.fwd_fee() { + fail!("fwd_fee_remaining not equal to fwd_fee") + } + fees.fees_collected = *env.fwd_fee_remaining(); - fees.value_imported.coins = header.fwd_fee; + fees.value_imported = header.value.clone(); + fees.value_imported.coins.add(env.fwd_fee_remaining())?; } InMsg::DeferredTransit(x) => { let env = x.read_in_envelope_message()?; diff --git a/src/emulator/src/lib.rs b/src/emulator/src/lib.rs index 26e1901..35c0b9d 100644 --- a/src/emulator/src/lib.rs +++ b/src/emulator/src/lib.rs @@ -452,6 +452,7 @@ impl Emulator { let config = BlockchainConfig::with_config(self.config_params.clone()) .inspect_err(|err| log::error!("Failed to create BlockchainConfig: {err}"))?; + let dict_hash_min_cells = config.size_limits_config().acc_state_cells_for_storage_dict; let executor: Box = if in_msg_cell.is_some() { Box::new(OrdinaryTransactionExecutor::new(config)) } else { @@ -479,6 +480,7 @@ impl Emulator { let elapsed_time = now.elapsed().as_micros() as i64; let result = match result { Ok(mut transaction) => { + account.update_storage_stat(dict_hash_min_cells).unwrap(); transaction.set_prev_trans_lt(shard_acc.last_trans_lt()); transaction.set_prev_trans_hash(shard_acc.last_trans_hash().clone()); let old_hash = shard_acc.account_hash(); diff --git a/src/executor/real_boc/due_payment_in_smc_account_new.boc b/src/executor/real_boc/due_payment_in_smc_account_new.boc new file mode 100644 index 0000000000000000000000000000000000000000..5dce1fda2d33a1e23f386e29c1836d2c9721778e GIT binary patch literal 3466 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 literal 0 HcmV?d00001 diff --git a/src/executor/real_boc/due_payment_in_smc_account_old.boc b/src/executor/real_boc/due_payment_in_smc_account_old.boc new file mode 100644 index 0000000000000000000000000000000000000000..a4146c4313c005fd295b2322937e0871c035dfa2 GIT binary patch literal 3452 zcmZ`+3s@6Z7QQnHAqbQqJme(_AP>>P5W>S(h=8J7l)OY#_KQ^zwX{UVs#W6{2WyL8 zc!)|}cb!tVwx3|$v_D-;V?S9*DGUuLi?mze3 zbM853CZ9Yyv<}0kV!&Su@Q=*Ct5TplIBJy&+Yl}W;d|?2rfuJQvHViX2LaFS<$Zds zab0t7z=PgeVdX)$_sqzK{io!6@MrbixxaSR;trY69L$q<^bH-+0DQh>=>09V z=9fsc@;F~aro4=ZpFbi=lMs0+0Fn3v?(ONTF6nwlC1zK>9~iZCFQak(`N^j*nA-t$ zs4}iL|8uh|WUMc zaeCfq>e3lUzx(_A{+j?p%z#@F-sZ5noiatJ#mmDe*Lj#5LJk>QW+2B2oU&4wq?B2_3=hzbglJP~x5$20Pr`hd$G}`8oMVKuVJ-vn zjIhASXyGDQlGxUwnAj$0F$9rXxKRr?;oET|%*Pm5X6H0k#@eV{Jt3`2cWOD^*8$9T z;>7HH^2-_4;1G=6!S9qJb_-=NwfZSOkb@i^a1RIvgvhYxx(Nn^q|cZP7s&ls7k%`^ ztlz(DWm!1)D#u}631=y(c89ebDwHjz$uRCx3oFb~%1!K8pp<#WT&31C<|&u4V<@+G z7#Hb@Pn0WWw;5)V$mLi&9~-j|Q{&B`LEBJmdx|5wT-F$-Cr0|dxc@7=NGKQB2`f6! z8Mmjjc@}&l@aSp8iDUFc^~0t~ZBY}Vg&M8m?hD1(;uJVY{OoT8X5mlO-+am50x zIMD7r$OgK-k<1$QCz9D*Ywg_j-I7SHe4O2j^W_YfJ3cHNNZ|aR*C%zY!nu;3HSVBvtU3t$|Y3|mklMVdP+1JP-Qeo#hICz+~+uy8)Q71*j05*+<+NE zR6duQmEK6Pl;jO2pm8gj_=Z%kBh(vLtG7M0`@Kg_b(mMr*(8s*5hVrXf`+L!ViQpj zfw`1SyP+KBDjDOXPN0ftZ*lZjs1{r_L`X8UP^q*UMQrJvi)hF{(j%F_XYzcpi*A-|p9_@v-sXN-nJ6!sQ&e0)wx@wUK}z ziiz2TWEKft+PfaE8nkmT7pjoLUsuRjcm{-VSAk01-tv54TfuKc&v*&ViQYJCh!&xM z)KwiL(_#?wHp{3w*hyn2$I-uPaDGp+-Bwo35f`7CD8^*Pl;msaZWpR zk?l1(w@`YDKA*Rf&lW@MPXN9m_=!Mej6LPDDOWpbahRvefP18;vi1Dt{S-`PU@-$1 zGJ>&1P}ZTRo#4iAG#C|j(P6FJ4mZZaO2$cs%)DwZyG943|08jVSk6&9TN+Z z^6CY4KRbuTCtOP!8ryz1TFlcUe{N7tnG>L#jI9mBEWT_5QhTD)U#8U)0q^RLb@vEE zP+2>1SlAcJ-lIV4#0A&EwY6F*jBNnizR)-)9@Mj1&;xp}?c|~AZ-uF7r!P(#1=bv9 zh#nY^>G|rS1#F1_*NZRHtO(Vah^7V-F@0EGW!3ufw!^Frpaaq>aXW!9OEKFC`IQsV zjN+0}tKJ-LGTd64WXw*`^OFWCRkO)(B~9_RF+twUsCJnQC$khAj1%Q=GO99@p(#V5 zGRDf)j4Ic(Z~D?Kjn}ZhU>{LB9VM?||4a=pL&LENn&jVW{A^9~A2j~9Af|*-;><+w zC|!|eROgY5D$ZnBk){wBCfJuvdd%-5CS_}Q$o@$U-zLDGOjk%G6xezp z=Lx8m)&t!HDTRoRC6_Tje&i|Fgzm_rh4G`3KC>=bvgw?~tP86~B&wR0>7q;s80H}^ z#Dz;)qGJiG3m-L`brBK-j@U4^dc-+*uQURglDN&w62aq0UC3AHJ47t*b8KvthNO3y zLUfcV#6(>N(jZr`E2vfMb_F4I(E6@ci$f=HNCjuRTHT1#38cZMU>%Vw);PpeI!|or za#*@rX&qsrOoWRv1xnG4RtrROpo^+mXYLYPK6}hPn=an6Py~|U8%`MpD_Qb7>WXmv zEfQ9tFRJ9p;LnS2zZy`)#DhH}<)Ct;vIVbVVoF>5&OC|pJNsnM$nr3*uTLUp{x>lI ze(+uCo--peSoPKe`60C@cDLmgWr3IVPTQ6+kgKr~CgUw>Tj@tc; zAMy)b{{iAz4l(+uIos8QrFYecEsk}e>0PwL?I;b20(|i=qtj|0a~gpxU1ECfFIF2{ znRb{zYqdO1A9HnTl!z-9cc`()UHhdd5PzqU&AvP7U9B>mS%h3pNd-A~a;c(z0hNR9 zRCEjbiGcpl5*NDM46o?wR-lnh5P`5Ee-e)Z!W3Q+ZJeRir0^Da6h3uyhew=ON8cFJ zCxHp2o*>pSA|W&7%(G_D_W#=!>S~54I472YD`7F}T7?U7)En5h=49nj#k+RzhBO;` z$r>VIK@0;cJec_}Fv-fMNeDvPkR$055ZJ!_{+fhc^%xE2w~c5(DqTklYlQR?rOq7^ya2HJ*Qu58$R{w zk(xUfBQoURui@@Q713E2vuyF?J1v{nG<06=dvwTM5LWi`$k$6cPt{SvW2r7b`;nxg zua3u2|9B^?`&h1S+sWtx<71 literal 0 HcmV?d00001 diff --git a/src/executor/real_boc/due_payment_in_smc_transaction.boc b/src/executor/real_boc/due_payment_in_smc_transaction.boc new file mode 100644 index 0000000000000000000000000000000000000000..eb41588cef0e0411a7d0b733b2d7cc767d4d21f1 GIT binary patch literal 710 zcmdn`ZcfodCT>OsrUeWP2N+H<1~8T}`7nhr^Dy6H(PBBn>cG6UIz#Wre-W9CV8&8O zBL>OI6BMoLC+|$%t+$;2&mHIOCnqp4zg;lF_>S?u)Z|Wv_Z<7I`fYi0ED)~0ot6YJ>BN8g69+S$om-`2o= z^S6Id>dtBQ557gs=T&p<=kAq%SlK!IJL5~C<^wAKXYPC^-9VKhBQBimY4tho=$tSYmWWrLcaTI0YC#8*f^A< z7#M1|2(eYSTwdw-#GK<&I0F!vyb9i_Q1W8d5=O@RY>XQ-7;{k|7UlU{WNA^zJJK)TzuTg+a}9$mREl3Kbv+wewAD3s;bbs1BektBGd(n7 z)3X1(Z>Dcp#OuvmV`J2S6Hu zfDs%R5H^`){>gTB3m>7qSHSvN7?^-Gb4sdXR@I(05!ul-vA(zWhxJD-cFMR=<63^) TMABuMbcDkNi3@E%UNOb*=eOw=n*O0D(_g?|MJ{R%pB@@>dNkOGjf%5@#3e~B-aJZ8#Kyso2I7ilWer)kV|o!kD$q!Z^S z>E4OCCUn|f==6V}#*-(`w{igK!t>mDXHGUa{APF%&#Z8rTV8>UorzI_n}_+IDGS)( zfG;eDzZiZ@`Z2lShgG0B$Q2V6uX`S1`1I(A$}bm#PZxcEsOJkbark&UEL1+kFeTV< z!NPkgc@qsME_$SJ=>MmOlavlI^r@UUKS9LL%uVe)x4rnUpmSgc1UdW)iulC@bVSyf zlZC$-CYeP)QpkJE5Aq-fCl`ZVlR$7{PRg!H2K6(+Oy^yP86HhEc+kSnc%0$W7X~)r zcxDD8kY9cU1u*|&Y7)3=RCfD3ch_lqka-LOzk(ov#2~---#s_Cn$Bx>@@9S&%c@!! zZ1>dFXQ?%^?FuFZ}^j&nB7lsy@1~`^_8;5yk2ZfrtS$Gt3@E%UNOb*=eOwoXP+N4Wb{HE?fHK zo>k+DXCD^kGu>(QN^RZuV#=H2(>i6pI(ZdbU^Xj|+`sy9=iUg$5T`57yFMQ}yYtGN z3vY_mc6mwfVAGq&%fy((!pijH!jFlc9NxNIP`K{Zr*PT+m&e5)lN67$etI~S>6gcq zi%cg@+YA2+68aT%=H%Ouhad$YVU_DTO8*jHEP2e7qj+6Mu`be7q)*eB={mRlk4Y!a zPtv^;b4}>9z0m3ZK#eC)oNwg-(uL=_^Uj=XaQMyeAf8#_I=8$68#@!D0yhuyKT{U4 z!2w@b41Y2FnDk?E!4IoIagZw}Dqi=Xp4o)rxyC#9)#GI5}lML!-f|<^{4l_KOXz-wgpYb@ur!Nd_ z!tu-uMj*fZ3JPHU#ndEl)u`@4tI)ZZ)0P?Bvb-Dwb8X zFxc*?tItwvWZM;TZsz|>Zrx3%v-A0YEcZ)p%esH9Y+>liC@Co@w$j(n%*fA4(JLs* z*Uv0SE7lLp4KEBWG7WGn^)?Rk&JGGSb+hm&aP#nXHqXy>woLNPvds2K3N7~twn%ZQ F004Aa9P0o8 literal 0 HcmV?d00001 diff --git a/src/executor/real_boc/fwd_fee_payment_in_smc_transaction.boc b/src/executor/real_boc/fwd_fee_payment_in_smc_transaction.boc new file mode 100644 index 0000000000000000000000000000000000000000..d710adbd0c0b106b34afb9c5587348c303ffbc98 GIT binary patch literal 796 zcmdn`ZcfodCO$?6rVk7Z2N+H<1~BF^sW9m=-C%ma^n!UF^FNj|tU0U?n75XAe5m%< zNdNP5^@=HeKfg_{&{e6kD0(k*d%J#!(_`(s4Ghe$k1-dSm`EEYm}Y1$4Ac0mBqAXG z@zr$O(2$w@k4#n-Asse}1-ZQK0qnTd@zeO#Jci^q%6pJ9`7Ud)AlnB}Fl? z@da&SV5qG)kmuHNd6~?s18hu;ciA}@md2m46=8sa29|`yy(@NETG#e!Y!}UcGOfa> zt8&ZMG)?yD3%^hDk*hCYHYmo znZd!xsK5!t+>Glz7&_(nGoSMOpKn?!)Op|T(tFk={m1!YAI~m(`l=%)wR$51$3b`L zz>=b}-%^2jTK7-td`K1foSAhdh+p6Ny~+%me`gJK8Q8dAu|XZ*0d_nWLo};!??kw} zB{w&m{Tnai`|1GKBC$oX*XN1lo?G&)J$a_-rGUE!3`<@xoFu|2#_5c+f9E#6$Pn8L59LiBpw{(Hrk~Pyhw*l%92ZlLL|4z0o zfVry}#vq%{KiS^O;3LB805*XILSD)Ac5zC#lSrNa!s_{~b%(F&J4G#Jcs8|H?M%+5 Qg_Hj{T(B}ZyVBzg0BvM0A^-pY literal 0 HcmV?d00001 diff --git a/src/executor/src/blockchain_config.rs b/src/executor/src/blockchain_config.rs index e593b0e..c5a4baa 100644 --- a/src/executor/src/blockchain_config.rs +++ b/src/executor/src/blockchain_config.rs @@ -229,6 +229,10 @@ impl BlockchainConfig { }) } + pub fn global_version(&self) -> u32 { + self.global_version + } + /// Get `MsgForwardPrices` for message forward fee calculation pub fn get_fwd_prices(&self, is_masterchain: bool) -> &MsgForwardPrices { if is_masterchain { diff --git a/src/executor/src/ordinary_transaction.rs b/src/executor/src/ordinary_transaction.rs index c80c2bb..388fa07 100644 --- a/src/executor/src/ordinary_transaction.rs +++ b/src/executor/src/ordinary_transaction.rs @@ -262,6 +262,7 @@ impl TransactionExecutor for OrdinaryTransactionExecutor { in_msg: Some(in_msg.clone()), incoming_value: msg_balance.clone(), storage_fees_collected, + due_payment: account.due_payment().map_or(0, Coins::as_u128), config_params, prev_blocks_info: params.prev_blocks_info.clone(), ..Default::default() diff --git a/src/executor/src/tests/common/mod.rs b/src/executor/src/tests/common/mod.rs index 320b637..03cb743 100644 --- a/src/executor/src/tests/common/mod.rs +++ b/src/executor/src/tests/common/mod.rs @@ -141,7 +141,7 @@ pub fn execute_params(last_tr_lt: u64) -> ExecuteParams { Simple, Emulator, } - let debug = DebugType::Emulator; + let debug = DebugType::None; let _ = cross_check::DisableCrossCheck::new(); let (verbosity, pattern, trace_callback) = match debug { DebugType::None => (4, None, None), @@ -458,6 +458,7 @@ pub fn execute_with_params( } else { mc_state_proof }; + let block_version = config.global_version(); let dict_hash_min_cells = config.size_limits_config().acc_state_cells_for_storage_dict; let executor: Box = if in_msg_cell.is_none() { let tt = acc.get_tick_tock().unwrap(); @@ -474,7 +475,11 @@ pub fn execute_with_params( let acc_before = acc.clone(); let trans = executor.execute_with_params(in_msg_cell.clone(), acc, params.clone()); if trans.is_ok() { - acc.update_storage_stat(dict_hash_min_cells).unwrap(); + if block_version < 11 { + acc.del_storage_stat(); + } else { + acc.update_storage_stat(dict_hash_min_cells).unwrap(); + } } #[cfg(feature = "cross_check")] cross_check::cross_check( @@ -922,22 +927,6 @@ pub fn replay_transaction( transaction.read_description().unwrap() ); - let block_version = extra.config.global_version(); - if block_version < 11 { - account.del_storage_stat(); - } else { - account - .update_storage_stat( - mc.read_custom() - .unwrap() - .unwrap() - .config() - .size_limits_config() - .unwrap() - .acc_state_cells_for_storage_dict, - ) - .unwrap(); - } // account.write_to_file(acc_after).unwrap(); let new_hash = account.serialize().unwrap().repr_hash(); // let hash_update = ton_block::HashUpdate::with_hashes(old_hash.clone(), new_hash.clone()); diff --git a/src/executor/src/tests/test_transaction_executor_with_real_data.rs b/src/executor/src/tests/test_transaction_executor_with_real_data.rs index c1ecd1f..f5f510f 100644 --- a/src/executor/src/tests/test_transaction_executor_with_real_data.rs +++ b/src/executor/src/tests/test_transaction_executor_with_real_data.rs @@ -12,9 +12,9 @@ use crate::blockchain_config::BlockchainConfig; use pretty_assertions::assert_eq; use std::io::{BufRead, BufReader}; use ton_block::{ - read_single_root_boc, Account, AccountStorage, Block, Cell, ConfigParams, CurrencyCollection, - Deserializable, Message, MsgAddressInt, Result, Serializable, StateInit, StorageInfo, - Transaction, UnixTime, + base64_decode, read_single_root_boc, Account, AccountStorage, Block, Cell, ConfigParams, + CurrencyCollection, Deserializable, Message, MsgAddressInt, Result, Serializable, ShardAccount, + StateInit, StorageInfo, TrComputePhase, Transaction, UnixTime, }; mod common; @@ -92,7 +92,7 @@ fn many_replay_contract_by_files( let descr = transaction.read_description()?; let compute_ph = descr.compute_phase_ref().expect("no compute phase"); match compute_ph { - ton_block::TrComputePhase::Vm(vm) => { + TrComputePhase::Vm(vm) => { let steps = vm.vm_steps; let gas = vm.gas_used; result.push((idx, contents.len(), steps, gas)); @@ -539,6 +539,26 @@ fn test_size_limits_v12() { ) } +#[test] +fn test_due_payment_in_smc() { + replay_transaction_by_files( + "real_boc/due_payment_in_smc_account_old.boc", + "real_boc/due_payment_in_smc_account_new.boc", + "real_boc/due_payment_in_smc_transaction.boc", + "real_boc/config12.boc", + ) +} + +#[test] +fn test_fwd_fee_payment_in_smc() { + replay_transaction_by_files( + "real_boc/fwd_fee_payment_in_smc_account_old.boc", + "real_boc/fwd_fee_payment_in_smc_account_new.boc", + "real_boc/fwd_fee_payment_in_smc_transaction.boc", + "real_boc/config12.boc", + ) +} + #[ignore = "test for replay transaction by files"] #[test] fn test_replay_transaction_by_files() { @@ -595,3 +615,38 @@ fn test_revert_action_phase() { answer.set_last_tr_time(account.last_tr_time().unwrap_or(0)); assert_eq!(answer, account); } + +#[ignore] +#[test] +fn test_bad_single() { + replay_transaction_by_files( + "real_boc/bad_account_old.boc", + "real_boc/bad_account_new.boc", + "real_boc/bad_transaction.boc", + "real_boc/config12.boc", + ) +} + +#[ignore] +#[test] +fn test_bad_trans() { + let json = "../../emulator/emulator_test.json"; + let prefix = "real_boc/bad_".to_string(); + let json = std::fs::read_to_string(json).unwrap(); + let json: serde_json::Map = serde_json::from_str(&json).unwrap(); + let acc = json["shard_account_boc"].as_str().unwrap(); + let tr = json["tx_boc"].as_str().unwrap(); + let prev = json["prev_blocks_info_boc"].as_str().unwrap(); + let shard_acc = ShardAccount::construct_from_base64(acc).unwrap(); + shard_acc.account_cell().write_to_file(prefix.clone() + "account_old.boc"); + shard_acc.account_cell().write_to_file(prefix.clone() + "account_new.boc"); + std::fs::write(prefix.clone() + "transaction.boc", base64_decode(tr).unwrap()).unwrap(); + + replay_transaction_with_prevs( + acc, + &(prefix + "account_new.boc"), + tr, + "real_boc/config12.boc", + prev, + ); +} diff --git a/src/executor/src/tick_tock_transaction.rs b/src/executor/src/tick_tock_transaction.rs index bb6371e..bb0cc1a 100644 --- a/src/executor/src/tick_tock_transaction.rs +++ b/src/executor/src/tick_tock_transaction.rs @@ -100,6 +100,7 @@ impl TransactionExecutor for TickTockTransactionExecutor { balance: acc_balance.clone(), config_params, storage_fees_collected, + due_payment: account.due_payment().map_or(0, Coins::as_u128), prev_blocks_info: params.prev_blocks_info.clone(), ..Default::default() }; diff --git a/src/node/src/validator/collator.rs b/src/node/src/validator/collator.rs index 2a1fc56..200ad97 100644 --- a/src/node/src/validator/collator.rs +++ b/src/node/src/validator/collator.rs @@ -786,6 +786,7 @@ struct ExecutionManager { config: BlockchainConfig, prev_blocks_info: PrevBlocksInfo, engine: Arc, + cancel_ext: Arc, } impl ExecutionManager { @@ -824,19 +825,10 @@ impl ExecutionManager { mc_data.state.shard_state_extra()?.prev_blocks.clone(), ), engine, + cancel_ext: Arc::new(AtomicBool::new(false)), }) } - // waits and finalizes all parallel tasks - pub async fn wait_transactions(&mut self, collator_data: &mut CollatorData) -> Result<()> { - log::trace!("{}: wait_transactions", self.collated_block_descr); - while self.wait_tr.count() > 0 { - self.wait_transaction(collator_data).await?; - } - self.min_lt.fetch_max(self.max_lt.load(Ordering::Relaxed), Ordering::Relaxed); - Ok(()) - } - // checks if a number of parallel transactilns is not too big, waits and finalizes some if needed. pub async fn check_parallel_transactions( &mut self, @@ -910,6 +902,7 @@ impl ExecutionManager { let engine = self.engine.clone(); let dict_hash_min_cells = self.config.size_limits_config().acc_state_cells_for_storage_dict; let lt_compatible = self.lt_compatible; + let cancel_ext = self.cancel_ext.clone(); let handle = tokio::spawn(async move { let lt = lt.max(min_lt.load(Ordering::Relaxed)); let full_collated_data = config.has_capability(GlobalCapabilities::CapFullCollatedData); @@ -931,10 +924,24 @@ impl ExecutionManager { collated_block_descr, shard_acc.account_id() ); - let config = config.clone(); // TODO: use Arc + if cancel_ext.load(Ordering::Relaxed) { + log::debug!( + "{}: account {:x} ext message cancelled by cutoff timeout", + collated_block_descr, + shard_acc.account_id() + ); + let transaction_res = Err(error!("cancelled by cutoff timeout")); + wait_tr.respond(Some((new_msg, msg_metadata, transaction_res))); + continue; + } - shard_acc.fetch_max_lt(min_lt.load(Ordering::Relaxed)); + let config = config.clone(); // TODO: use Arc + let mut min_lt = min_lt.load(Ordering::Relaxed); + if let AsyncMessage::Deferred(enq) = &*new_msg { + min_lt = min_lt.max(enq.emitted_lt().saturating_add(1)); + }; + shard_acc.fetch_max_lt(min_lt); let mut account = shard_acc.account().clone(); let params = ExecuteParams { @@ -3295,7 +3302,7 @@ impl Collator { let account_id = collator_data.config.raw_config().config_addr.clone(); self.create_ticktock_transaction(account_id, tock, prev_data, collator_data, exec_manager) .await?; - exec_manager.wait_transactions(collator_data).await?; + self.wait_transactions(exec_manager, collator_data, false).await?; Ok(()) } @@ -3373,7 +3380,7 @@ impl Collator { ) .await?; - exec_manager.wait_transactions(collator_data).await?; + self.wait_transactions(exec_manager, collator_data, false).await?; Ok(()) } @@ -3485,7 +3492,7 @@ impl Collator { if to_us { let account_id = enq.dst_account_id().clone(); log::debug!( - "{}: message {:x} sent to execution to account {account_id:x}", + "{}: internal message {:x} sent to execution to account {account_id:x}", self.collated_block_descr, key.hash, ); @@ -3604,9 +3611,8 @@ impl Collator { } let (_, account_id) = header.dst.extract_std_address(true)?; log::debug!( - "{}: message {:x} sent to execution", + "{}: external message {msg_id:x} sent to execution to account {account_id:x}", self.collated_block_descr, - msg_id ); let msg = AsyncMessage::Ext(msg, msg_cell, msg_id); let initiator_addr = @@ -3626,7 +3632,7 @@ impl Collator { } self.check_stop_flag()?; } - exec_manager.wait_transactions(collator_data).await?; + self.wait_transactions(exec_manager, collator_data, true).await?; let accepted = mem::take(&mut collator_data.accepted_ext_messages); let rejected = mem::take(&mut collator_data.rejected_ext_messages); self.engine.complete_external_messages(rejected, accepted)?; @@ -3700,11 +3706,11 @@ impl Collator { } Ok(None) } else { - collator_data.update_last_proc_int_msg((msg.enq.lt(), msg_hash.clone()))?; log::debug!( - "{}: new message {msg_hash:x} sent to execution", + "{}: new message {msg_hash:x} sent to execution to account {account_id:x}", self.collated_block_descr, ); + collator_data.update_last_proc_int_msg((msg.enq.lt(), msg_hash.clone()))?; let msg = if from_dispatch_queue { AsyncMessage::Deferred(msg.enq) } else { @@ -3749,13 +3755,37 @@ impl Collator { } self.check_stop_flag()?; } - exec_manager.wait_transactions(collator_data).await?; + self.wait_transactions(exec_manager, collator_data, false).await?; self.check_stop_flag()?; } Ok(()) } + // waits and finalizes all parallel tasks + async fn wait_transactions( + &self, + exec_manager: &mut ExecutionManager, + collator_data: &mut CollatorData, + allow_cancel: bool, + ) -> Result<()> { + log::trace!("{}: wait_transactions", self.collated_block_descr); + while exec_manager.wait_tr.count() != 0 { + if allow_cancel && self.check_cutoff_timeout() { + log::warn!( + "{}: TIMEOUT is elapsed, cancelling remaining external messages", + self.collated_block_descr + ); + exec_manager.cancel_ext.store(true, Ordering::Relaxed); + } + exec_manager.wait_transaction(collator_data).await?; + } + exec_manager + .min_lt + .fetch_max(exec_manager.max_lt.load(Ordering::Relaxed), Ordering::Relaxed); + Ok(()) + } + fn update_processed_upto( &self, mc_data: &McData, diff --git a/src/node/src/validator/validate_query.rs b/src/node/src/validator/validate_query.rs index 5fa8afe..286d82d 100644 --- a/src/node/src/validator/validate_query.rs +++ b/src/node/src/validator/validate_query.rs @@ -3307,7 +3307,7 @@ impl ValidateQuery { }; let (workchain_id, addr) = dst.extract_std_address(true).map_err(|err| { error!( - "destination of inbound internal message with hash {key:x} \ + "destination {dst} of inbound internal message with hash {key:x} \ is an invalid blockchain address {err}" ) })?; @@ -3495,9 +3495,15 @@ impl ValidateQuery { } if from_dispatch_queue { + let (_, addr) = src.extract_std_address(true).map_err(|err| { + error!( + "source {src} of deferred inbound message with hash {key:x} \ + is an invalid blockchain address {err}" + ) + })?; // Check that the message was removed from DispatchQueue let Some(dispatched_msg_env_cell) = - base.removed_dispatch_queue_messages(src.address(), created_lt) + base.removed_dispatch_queue_messages(&addr, created_lt) else { reject_query!( "deferred InMsg with src_addr={addr:x} lt={created_lt} was not removed from \ diff --git a/src/vm/src/smart_contract_info.rs b/src/vm/src/smart_contract_info.rs index c65bbe2..d9f2c3d 100644 --- a/src/vm/src/smart_contract_info.rs +++ b/src/vm/src/smart_contract_info.rs @@ -272,7 +272,7 @@ impl SmartContractInfo { tuple.push(StackItem::boolean(int.bounce)); tuple.push(StackItem::boolean(int.bounced)); tuple.push(StackItem::slice(int.src.write_to_bitstring().unwrap_or_default())); - tuple.push(StackItem::int(0)); + tuple.push(StackItem::int(int.fwd_fee().as_u128())); tuple.push(StackItem::int(int.created_lt)); tuple.push(StackItem::int(int.created_at)); tuple.push(StackItem::int(int.value.coins.as_u128())); diff --git a/src/vm/src/tests/test_smart_contract_info.rs b/src/vm/src/tests/test_smart_contract_info.rs index ddaf35f..80cf5e8 100644 --- a/src/vm/src/tests/test_smart_contract_info.rs +++ b/src/vm/src/tests/test_smart_contract_info.rs @@ -9,7 +9,10 @@ * This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND. */ use super::*; -use ton_block::{base64_decode, read_single_root_boc, ton_method_id}; +use ton_block::{ + base64_decode, read_single_root_boc, ton_method_id, Coins, CurrencyCollection, + InternalMessageHeader, Message, MsgAddressInt, +}; #[test] fn test_smart_contract_info_serialization_default() { @@ -32,6 +35,35 @@ fn test_smart_contract_info() { assert_eq!(result, 17); } +#[test] +fn test_smart_contract_info_internal_message_info_uses_fwd_fee() { + let src: MsgAddressInt = + "0:cd9c066feaf8e26f56005510b510eebcf0b36e7527343b1cc9ae9286ee018ba7".parse().unwrap(); + let dst: MsgAddressInt = + "0:448f07b4f2867fcf9aba8944ef9f697782d247e3872ef1cdbb72f5442e32bdd8".parse().unwrap(); + let mut hdr = InternalMessageHeader::with_addresses_and_bounce( + src, + dst, + CurrencyCollection::with_coins(10_000_000), + true, + ); + hdr.fwd_fee = Coins::from(1_408_000u64); + hdr.created_lt = 68978789000002; + hdr.created_at = 1775439477; + + let sci = SmartContractInfo { + in_msg: Some(Message::with_int_header(hdr)), + incoming_value: CurrencyCollection::with_coins(10_000_000), + ..Default::default() + }; + + let msg_info = sci.get_message_info(); + let info = msg_info.as_tuple().unwrap(); + assert_eq!(info[3].as_integer().unwrap().to_str_radix(10), "1408000"); + assert_eq!(info[6].as_integer().unwrap().to_str_radix(10), "10000000"); + assert_eq!(info[7].as_integer().unwrap().to_str_radix(10), "10000000"); +} + #[test] fn test_run_get_method_seqno_with_config() { let mc_state_name = "../block/src/tests/data/free-ton-mc-state-61884";