├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── benches ├── benchmarks.rs └── block-0.json ├── deps_map.json ├── real_ever_boc ├── account_with_deploy_contract_with_lib_function.boc ├── bounce_message_to_new_account_account_new.boc ├── bounce_message_to_new_account_account_old.boc ├── bounce_message_to_new_account_transaction.boc ├── bounce_msg_account_new.boc ├── bounce_msg_account_old.boc ├── bounce_msg_transaction.boc ├── bounce_to_empty_account_new.boc ├── bounce_to_empty_account_old.boc ├── bounce_to_empty_transaction.boc ├── bounce_to_low_balance_account_new.boc ├── bounce_to_low_balance_account_old.boc ├── bounce_to_low_balance_transaction.boc ├── config.boc ├── count_steps_acc_new.boc ├── count_steps_acc_old.boc ├── count_steps_tx.boc ├── default_config.boc ├── deploy_account_new.boc ├── deploy_account_old.boc ├── deploy_transaction.boc ├── depool_balance_check_account_new.boc ├── depool_balance_check_account_old.boc ├── depool_balance_check_transaction.boc ├── freeze_account_new.boc ├── freeze_account_old.boc ├── freeze_transaction.boc ├── ihr_fee_account_new.boc ├── ihr_fee_account_old.boc ├── ihr_fee_transaction.boc ├── ihr_message_account_new.boc ├── ihr_message_account_old.boc ├── ihr_message_transaction.boc ├── init_account_new.boc ├── init_account_old.boc ├── init_transaction.boc ├── int_message_to_elector2_account_new.boc ├── int_message_to_elector2_account_old.boc ├── int_message_to_elector2_transaction.boc ├── int_message_to_elector_account_new.boc ├── int_message_to_elector_account_old.boc ├── int_message_to_elector_transaction.boc ├── msg_body_ref_account_new.boc ├── msg_body_ref_account_old.boc ├── msg_body_ref_transaction.boc ├── msg_with_deploy_contract_with_lib_function.boc ├── no_balance_to_send_account_new.boc ├── no_balance_to_send_account_old.boc ├── no_balance_to_send_transaction.boc ├── nofunds_without_error_account_new.boc ├── nofunds_without_error_account_old.boc ├── nofunds_without_error_transaction.boc ├── not_abort_accept_account_account_new.boc ├── not_abort_accept_account_account_old.boc ├── not_abort_accept_account_transaction.boc ├── out_of_gas_account_new.boc ├── out_of_gas_account_old.boc ├── out_of_gas_transaction.boc ├── reserve_value_from_account.boc ├── reserve_value_message.boc ├── revert_action_account.boc ├── revert_action_transaction.boc ├── send_remainig_msg_balance_account_new.boc ├── send_remainig_msg_balance_account_old.boc ├── send_remainig_msg_balance_transaction.boc ├── send_to_frozen_account_new.boc ├── send_to_frozen_account_old.boc ├── send_to_frozen_transaction.boc ├── simple_account_new.boc ├── simple_account_old.boc ├── simple_transaction.boc ├── tick_tock_acc_new.boc ├── tick_tock_acc_old.boc ├── tick_tock_tx.boc ├── unfreeze_account_new.boc ├── unfreeze_account_old.boc ├── unfreeze_transaction.boc ├── uninit_account_new.boc ├── uninit_account_old.boc ├── uninit_account_transaction.boc ├── wrong_compute_phase_account_new.boc ├── wrong_compute_phase_account_old.boc ├── wrong_compute_phase_transaction.boc ├── wrong_skip_reason_account_new.boc ├── wrong_skip_reason_account_old.boc └── wrong_skip_reason_transaction.boc └── src ├── blockchain_config.rs ├── error.rs ├── lib.rs ├── ordinary_transaction.rs ├── tests ├── common │ ├── cross_check.rs │ └── mod.rs ├── log_cfg.yml ├── test_bounced_action_phase.rs ├── test_copyleft_instruction.rs ├── test_currency_collections.rs ├── test_ordinary_freeze.rs ├── test_ordinary_libs_and_code.rs ├── test_ordinary_rawreserve.rs ├── test_ordinary_transaction.rs ├── test_random_gen.rs ├── test_tick_tock_transaction.rs ├── test_tr_phases.rs └── test_transaction_executor_with_real_data.rs ├── tick_tock_transaction.rs ├── transaction_executor.rs └── vmsetup.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | target/ 4 | 5 | # all log files 6 | *.log 7 | 8 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 9 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 10 | Cargo.lock 11 | 12 | # These are backup files generated by rustfmt 13 | **/*.rs.bk 14 | 15 | # vim temporary files 16 | .*.swp 17 | 18 | # IDE files 19 | .idea 20 | .vscode 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "common"] 2 | path = common 3 | url = https://github.com/everx-labs/common 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Release Notes 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## Version 1.18.31 6 | 7 | - Fix the build 8 | 9 | ## Version 1.18.17 10 | 11 | - Added gosh feature 12 | 13 | ## Version 1.18.0 14 | 15 | - Use modern crates anyhow and thiserror instead of failure 16 | 17 | ## Version 1.17.7 18 | 19 | - Mesh and common message structures support 20 | 21 | ## Version 1.17.6 22 | 23 | - No due payments on credit phase and add payed dues to storage fee in TVM 24 | 25 | ## Version 1.17.0 26 | 27 | - the crate was renamed from `ton_executor` to `ever_executor` 28 | - supported renaming of other crates 29 | 30 | ## Version 1.16.122 31 | 32 | - Do not delete frozen accounts (if related capability set) 33 | 34 | ## Version 1.16.108 35 | 36 | - Deny non-zero cell level in code/data/lib 37 | 38 | ## Version 1.16.106 39 | 40 | - Remove compiler warning 41 | - Remove unwraps that lead to panic 42 | 43 | ## Version 1.16.85 44 | 45 | - Deny ChangeLibrary action when CapSetLibCode is unset 46 | 47 | ## Version 1.16.40 48 | 49 | - Disable debug symbols by default 50 | 51 | ## Version 1.16.0 52 | 53 | - Skiped compute phase for suspended addresses 54 | 55 | ## Version 1.15.196 56 | 57 | - Removed extra crates bas64 58 | - Minor refactoring 59 | 60 | ## Version 1.15.191 61 | 62 | - Supported ever-types version 2.0 63 | 64 | ## Version 1.15.190 65 | 66 | - Add test for CapFeeInGasUnits 67 | 68 | ## Version: 1.15.188 69 | 70 | ### News 71 | 72 | - check capability for calculating forward and storage fees 73 | 74 | ## Version: 1.15.183 75 | 76 | ### Fixes 77 | 78 | - check gas limit and credit for overflow 79 | 80 | ## Version: 1.15.177 81 | 82 | ### New 83 | 84 | - capability CapBounceAfterFailedAction: if transaction fails on Action phase, 85 | bounced message will be produced 86 | 87 | ## Version: 1.15.128 88 | 89 | ### New 90 | 91 | - add common submodule 92 | 93 | ### Fixes 94 | 95 | - minor refactor for clippy 96 | 97 | ## Version: 1.15.121 98 | 99 | ### Fixes 100 | 101 | - support other libs changes 102 | ## Version: 1.15.75 103 | 104 | ### New 105 | 106 | - backward compatibility to prev nodes in bounced fee calculating 107 | 108 | ## Version: 1.5.73 109 | 110 | ### New 111 | 112 | - support behavior modifier mechanism for TVM -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | build = 'common/build/build.rs' 3 | edition = '2021' 4 | name = 'ever_executor' 5 | version = '1.18.31' 6 | 7 | [dependencies] 8 | anyhow = '1.0' 9 | lazy_static = '1.4' 10 | log = '0.4' 11 | thiserror = '1.0' 12 | ever_block = { git = 'https://github.com/everx-labs/ever-block.git', tag = '1.11.22' } 13 | ever_vm = { git = 'https://github.com/everx-labs/ever-vm.git', tag = '2.2.26' } 14 | 15 | [dev-dependencies] 16 | criterion = { features = [ 'html_reports' ], version = '0.3' } 17 | libloading = '0.6' 18 | log4rs = '1.2' 19 | pretty_assertions = '1.3' 20 | rand = '0.8' 21 | serde = { features = [ 'derive' ], version = '1.0' } 22 | serde_json = '1.0' 23 | ever_assembler = { git = 'https://github.com/everx-labs/ever-assembler.git' } 24 | 25 | [features] 26 | ci_run = [ ] 27 | cross_check = [ ] 28 | gosh = [ 'ever_vm/gosh', 'ever_block/gosh' ] 29 | ihr_disabled = [ ] 30 | signature_with_id = [ 'ever_block/signature_with_id', 'ever_vm/signature_with_id' ] 31 | timings = [ ] 32 | 33 | [[bench]] 34 | harness = false 35 | name = 'benchmarks' 36 | 37 | [lib] 38 | bench = false 39 | 40 | [profile] 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ever-executor 2 | 3 | Transaction executor for Everscale/Venom nodes 4 | 5 | ## Table of Contents 6 | 7 | - [About](#about) 8 | - [Getting Started](#getting-started) 9 | - [Usage](#usage) 10 | - [Contributing](#contributing) 11 | - [License](#license) 12 | 13 | ## About 14 | 15 | Implementation of transaction executor for Everscale/Venom nodes in safe Rust. 16 | 17 | ## Getting Started 18 | 19 | ### Prerequisites 20 | 21 | Rust complier v1.65+. 22 | 23 | ### Installing 24 | 25 | ``` 26 | git clone --recurse-submodules https://github.com/tonlabs/ever-executor.git 27 | cd ever-executor 28 | cargo build --release 29 | ``` 30 | 31 | ## Usage 32 | 33 | This project output is the library which is used as a part of Everscale/Venom node. Also it can be used in standalone tools. 34 | 35 | ## Contributing 36 | 37 | Contribution to the project is expected to be done via pull requests submission. 38 | 39 | ## License 40 | 41 | See the [LICENSE](LICENSE) file for details. 42 | 43 | ## Tags 44 | 45 | `blockchain` `everscale` `rust` `venom-blockchain` `venom-developer-program` `venom-tvm` 46 | -------------------------------------------------------------------------------- /benches/benchmarks.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2022 - 2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use criterion::{criterion_group, criterion_main, Criterion}; 15 | use serde::{Serialize, Deserialize}; 16 | use ever_block::{Deserializable, Account, Transaction, ConfigParams, Serializable, TransactionDescr}; 17 | use ever_executor::{BlockchainConfig, ExecuteParams, OrdinaryTransactionExecutor, TickTockTransactionExecutor, TransactionExecutor}; 18 | use ever_block::{error, fail, Result, UInt256, Cell, Status}; 19 | use std::sync::{Arc, atomic::AtomicU64}; 20 | 21 | #[path = "../src/tests/common/mod.rs"] 22 | mod common; 23 | use common::replay_transaction_by_files; 24 | 25 | fn bench_real_transaction(c: &mut Criterion) { 26 | replay_transaction_by_files(Some((c, "real_transaction")), 27 | "real_ever_boc/simple_account_old.boc", 28 | "real_ever_boc/simple_account_new.boc", 29 | "real_ever_boc/simple_transaction.boc", 30 | "real_ever_boc/config.boc" 31 | ) 32 | } 33 | 34 | fn bench_real_deploy_transaction(c: &mut Criterion) { 35 | replay_transaction_by_files(Some((c, "real_deploy_transaction")), 36 | "real_ever_boc/deploy_account_old.boc", 37 | "real_ever_boc/deploy_account_new.boc", 38 | "real_ever_boc/deploy_transaction.boc", 39 | "real_ever_boc/config.boc" 40 | ) 41 | } 42 | 43 | fn bench_init_account_transaction(c: &mut Criterion) { 44 | replay_transaction_by_files(Some((c, "init_account_transaction")), 45 | "real_ever_boc/init_account_old.boc", 46 | "real_ever_boc/init_account_new.boc", 47 | "real_ever_boc/init_transaction.boc", 48 | "real_ever_boc/config.boc" 49 | ) 50 | } 51 | 52 | fn bench_check_execute_bounced_message(c: &mut Criterion) { 53 | replay_transaction_by_files(Some((c, "check_execute_bounced_message")), 54 | "real_ever_boc/bounce_msg_account_old.boc", 55 | "real_ever_boc/bounce_msg_account_new.boc", 56 | "real_ever_boc/bounce_msg_transaction.boc", 57 | "real_ever_boc/config.boc" 58 | ) 59 | } 60 | 61 | fn bench_check_execute_out_message_with_body_in_ref(c: &mut Criterion) { 62 | replay_transaction_by_files(Some((c, "check_execute_out_message_with_body_in_ref")), 63 | "real_ever_boc/msg_body_ref_account_old.boc", 64 | "real_ever_boc/msg_body_ref_account_new.boc", 65 | "real_ever_boc/msg_body_ref_transaction.boc", 66 | "real_ever_boc/config.boc" 67 | ) 68 | } 69 | 70 | fn bench_check_execute_uninit_account(c: &mut Criterion) { 71 | replay_transaction_by_files(Some((c, "check_execute_uninit_account")), 72 | "real_ever_boc/uninit_account_old.boc", 73 | "real_ever_boc/uninit_account_new.boc", 74 | "real_ever_boc/uninit_account_transaction.boc", 75 | "real_ever_boc/config.boc" 76 | ) 77 | } 78 | 79 | fn bench_check_send_remainig_msg_balance(c: &mut Criterion) { 80 | replay_transaction_by_files(Some((c, "check_send_remainig_msg_balance")), 81 | "real_ever_boc/send_remainig_msg_balance_account_old.boc", 82 | "real_ever_boc/send_remainig_msg_balance_account_new.boc", 83 | "real_ever_boc/send_remainig_msg_balance_transaction.boc", 84 | "real_ever_boc/config.boc" 85 | ) 86 | } 87 | 88 | fn bench_check_out_of_gas_transaction(c: &mut Criterion) { 89 | replay_transaction_by_files(Some((c, "check_out_of_gas_transaction")), 90 | "real_ever_boc/out_of_gas_account_old.boc", 91 | "real_ever_boc/out_of_gas_account_new.boc", 92 | "real_ever_boc/out_of_gas_transaction.boc", 93 | "real_ever_boc/config.boc" 94 | ) 95 | } 96 | 97 | fn bench_check_wrong_skip_reason(c: &mut Criterion) { 98 | replay_transaction_by_files(Some((c, "check_wrong_skip_reason")), 99 | "real_ever_boc/wrong_skip_reason_account_old.boc", 100 | "real_ever_boc/wrong_skip_reason_account_new.boc", 101 | "real_ever_boc/wrong_skip_reason_transaction.boc", 102 | "real_ever_boc/config.boc" 103 | ) 104 | } 105 | 106 | fn bench_check_wrong_compute_phase(c: &mut Criterion) { 107 | replay_transaction_by_files(Some((c, "check_wrong_compute_phase")), 108 | "real_ever_boc/wrong_compute_phase_account_old.boc", 109 | "real_ever_boc/wrong_compute_phase_account_new.boc", 110 | "real_ever_boc/wrong_compute_phase_transaction.boc", 111 | "real_ever_boc/config.boc" 112 | ) 113 | } 114 | 115 | fn bench_check_nofunds_to_send_message_without_error(c: &mut Criterion) { 116 | replay_transaction_by_files(Some((c, "check_nofunds_to_send_message_without_error")), 117 | "real_ever_boc/nofunds_without_error_account_old.boc", 118 | "real_ever_boc/nofunds_without_error_account_new.boc", 119 | "real_ever_boc/nofunds_without_error_transaction.boc", 120 | "real_ever_boc/config.boc" 121 | ) 122 | } 123 | 124 | fn bench_bounce_message_to_new_account(c: &mut Criterion) { 125 | replay_transaction_by_files(Some((c, "bounce_message_to_new_account")), 126 | "real_ever_boc/bounce_message_to_new_account_account_old.boc", 127 | "real_ever_boc/bounce_message_to_new_account_account_new.boc", 128 | "real_ever_boc/bounce_message_to_new_account_transaction.boc", 129 | "real_ever_boc/config.boc" 130 | ) 131 | } 132 | 133 | fn bench_out_of_gas_in_cmd(c: &mut Criterion) { 134 | replay_transaction_by_files(Some((c, "out_of_gas_in_cmd")), 135 | "real_ever_boc/bounce_message_to_new_account_account_old.boc", 136 | "real_ever_boc/bounce_message_to_new_account_account_new.boc", 137 | "real_ever_boc/bounce_message_to_new_account_transaction.boc", 138 | "real_ever_boc/config.boc" 139 | ) 140 | } 141 | 142 | fn bench_freeze_account(c: &mut Criterion) { 143 | replay_transaction_by_files(Some((c, "freeze_account")), 144 | "real_ever_boc/freeze_account_old.boc", 145 | "real_ever_boc/freeze_account_new.boc", 146 | "real_ever_boc/freeze_transaction.boc", 147 | "real_ever_boc/config.boc" 148 | ) 149 | } 150 | 151 | fn bench_send_to_frozen_account(c: &mut Criterion) { 152 | replay_transaction_by_files(Some((c, "send_to_frozen_account")), 153 | "real_ever_boc/send_to_frozen_account_old.boc", 154 | "real_ever_boc/send_to_frozen_account_new.boc", 155 | "real_ever_boc/send_to_frozen_transaction.boc", 156 | "real_ever_boc/config.boc" 157 | ) 158 | } 159 | 160 | fn bench_unfreeze_account(c: &mut Criterion) { 161 | replay_transaction_by_files(Some((c, "unfreeze_account")), 162 | "real_ever_boc/unfreeze_account_old.boc", 163 | "real_ever_boc/unfreeze_account_new.boc", 164 | "real_ever_boc/unfreeze_transaction.boc", 165 | "real_ever_boc/config.boc" 166 | ) 167 | } 168 | 169 | fn bench_bounce_to_empty_account(c: &mut Criterion) { 170 | replay_transaction_by_files(Some((c, "bounce_to_empty_account")), 171 | "real_ever_boc/bounce_to_empty_account_old.boc", 172 | "real_ever_boc/bounce_to_empty_account_new.boc", 173 | "real_ever_boc/bounce_to_empty_transaction.boc", 174 | "real_ever_boc/config.boc" 175 | ) 176 | } 177 | 178 | fn bench_bounce_to_low_balance_account(c: &mut Criterion) { 179 | replay_transaction_by_files(Some((c, "bounce_to_low_balance_account")), 180 | "real_ever_boc/bounce_to_low_balance_account_old.boc", 181 | "real_ever_boc/bounce_to_low_balance_account_new.boc", 182 | "real_ever_boc/bounce_to_low_balance_transaction.boc", 183 | "real_ever_boc/config.boc" 184 | ) 185 | } 186 | 187 | fn bench_depool_balance_check(c: &mut Criterion) { 188 | replay_transaction_by_files(Some((c, "depool_balance_check")), 189 | "real_ever_boc/depool_balance_check_account_old.boc", 190 | "real_ever_boc/depool_balance_check_account_new.boc", 191 | "real_ever_boc/depool_balance_check_transaction.boc", 192 | "real_ever_boc/config.boc" 193 | ) 194 | } 195 | 196 | fn bench_no_balance_to_send_transaction(c: &mut Criterion) { 197 | replay_transaction_by_files(Some((c, "no_balance_to_send_transaction")), 198 | "real_ever_boc/no_balance_to_send_account_old.boc", 199 | "real_ever_boc/no_balance_to_send_account_new.boc", 200 | "real_ever_boc/no_balance_to_send_transaction.boc", 201 | "real_ever_boc/config.boc" 202 | ) 203 | } 204 | 205 | fn bench_int_message_to_elector_transaction(c: &mut Criterion) { 206 | replay_transaction_by_files(Some((c, "int_message_to_elector_transaction")), 207 | "real_ever_boc/int_message_to_elector_account_old.boc", 208 | "real_ever_boc/int_message_to_elector_account_new.boc", 209 | "real_ever_boc/int_message_to_elector_transaction.boc", 210 | "real_ever_boc/config.boc" 211 | ) 212 | } 213 | 214 | fn bench_int_message_to_elector2_transaction(c: &mut Criterion) { 215 | replay_transaction_by_files(Some((c, "int_message_to_elector2_transaction")), 216 | "real_ever_boc/int_message_to_elector2_account_old.boc", 217 | "real_ever_boc/int_message_to_elector2_account_new.boc", 218 | "real_ever_boc/int_message_to_elector2_transaction.boc", 219 | "real_ever_boc/config.boc" 220 | ) 221 | } 222 | 223 | fn bench_ihr_message(c: &mut Criterion) { 224 | replay_transaction_by_files(Some((c, "ihr_message")), 225 | "real_ever_boc/ihr_message_account_old.boc", 226 | "real_ever_boc/ihr_message_account_new.boc", 227 | "real_ever_boc/ihr_message_transaction.boc", 228 | "real_ever_boc/config.boc" 229 | ) 230 | } 231 | 232 | fn bench_tick_tock_message(c: &mut Criterion) { 233 | replay_transaction_by_files(Some((c, "tick_tock_message")), 234 | "real_ever_boc/tick_tock_acc_old.boc", 235 | "real_ever_boc/tick_tock_acc_new.boc", 236 | "real_ever_boc/tick_tock_tx.boc", 237 | "real_ever_boc/config.boc" 238 | ) 239 | } 240 | 241 | fn bench_count_steps_vm(c: &mut Criterion) { 242 | replay_transaction_by_files(Some((c, "count_steps_vm")), 243 | "real_ever_boc/count_steps_acc_old.boc", 244 | "real_ever_boc/count_steps_acc_new.boc", 245 | "real_ever_boc/count_steps_tx.boc", 246 | "real_ever_boc/config.boc" 247 | ) 248 | } 249 | 250 | fn bench_not_aborted_accepted_transaction(c: &mut Criterion) { 251 | replay_transaction_by_files(Some((c, "not_aborted_accepted_transaction")), 252 | "real_ever_boc/not_abort_accept_account_account_old.boc", 253 | "real_ever_boc/not_abort_accept_account_account_new.boc", 254 | "real_ever_boc/not_abort_accept_account_transaction.boc", 255 | "real_ever_boc/config.boc" 256 | ) 257 | } 258 | 259 | fn bench_ihr_fee_output_msg(c: &mut Criterion) { 260 | replay_transaction_by_files(Some((c, "ihr_fee_output_msg")), 261 | "real_ever_boc/ihr_fee_account_old.boc", 262 | "real_ever_boc/ihr_fee_account_new.boc", 263 | "real_ever_boc/ihr_fee_transaction.boc", 264 | "real_ever_boc/config.boc" 265 | ) 266 | } 267 | 268 | #[derive(Serialize, Deserialize)] 269 | struct BlockDescr { 270 | id: String, 271 | config_boc: String, 272 | accounts: Vec, 273 | } 274 | 275 | #[derive(Serialize, Deserialize)] 276 | struct BlockAccountDescr { 277 | account_boc: String, 278 | transactions: Vec, 279 | } 280 | 281 | #[derive(Clone)] 282 | struct BlockData { 283 | config: BlockchainConfig, 284 | accounts: Vec, 285 | } 286 | 287 | #[derive(Clone)] 288 | struct BlockAccountData { 289 | account_cell: Cell, 290 | transactions: Vec, 291 | } 292 | 293 | fn load_blockchain_config(config_account: &Account) -> Result { 294 | let config_cell = config_account 295 | .get_data().ok_or_else(|| error!("config account data loading error"))? 296 | .reference(0).ok(); 297 | let config_params = ConfigParams::with_address_and_params( 298 | UInt256::with_array([0x55; 32]), config_cell); 299 | BlockchainConfig::with_config(config_params) 300 | } 301 | 302 | fn load_block(block_filename: &str) -> Result { 303 | let block_file = std::fs::File::open(block_filename)?; 304 | let block: BlockDescr = serde_json::from_reader(std::io::BufReader::new(block_file))?; 305 | let config_account = Account::construct_from_base64(&block.config_boc)?; 306 | let config = load_blockchain_config(&config_account)?; 307 | let mut accounts = Vec::new(); 308 | for acc in block.accounts { 309 | let account = Account::construct_from_base64(&acc.account_boc)?; 310 | let account_cell = account.serialize()?; 311 | let mut transactions = Vec::new(); 312 | for txn in acc.transactions { 313 | let tr = Transaction::construct_from_base64(&txn)?; 314 | transactions.push(tr); 315 | } 316 | accounts.push(BlockAccountData { account_cell, transactions }); 317 | } 318 | Ok(BlockData { config, accounts }) 319 | } 320 | 321 | fn replay_block(data: BlockData) -> Status { 322 | for acc in data.accounts { 323 | let mut account = acc.account_cell; 324 | for tr in acc.transactions { 325 | let executor: Box = 326 | match tr.read_description()? { 327 | TransactionDescr::TickTock(desc) => { 328 | Box::new(TickTockTransactionExecutor::new(data.config.clone(), desc.tt)) 329 | } 330 | TransactionDescr::Ordinary(_) => { 331 | Box::new(OrdinaryTransactionExecutor::new(data.config.clone())) 332 | } 333 | _ => fail!("unknown transaction type") 334 | }; 335 | executor.execute_with_libs_and_params( 336 | tr.read_in_msg()?.as_ref(), 337 | &mut account, 338 | ExecuteParams { 339 | block_unixtime: tr.now(), 340 | block_lt: tr.logical_time(), 341 | last_tr_lt: Arc::new(AtomicU64::new(tr.logical_time())), 342 | ..Default::default() 343 | } 344 | )?; 345 | if account.repr_hash() != tr.read_state_update()?.new_hash { 346 | fail!("new hash mismatch"); 347 | } 348 | } 349 | } 350 | Ok(()) 351 | } 352 | 353 | // block 9278c99e55994a1636d4343b651c09beceb684cdb3a3a173f2c844feeef541ba 354 | // downloaded from os.ever.dev on 2022-03-24 355 | fn bench_block_0(c: &mut Criterion) { 356 | let block = load_block("benches/block-0.json").unwrap(); 357 | c.bench_function("block-0", |b| b.iter(|| { 358 | replay_block(block.clone()).expect("replay failed") 359 | })); 360 | } 361 | 362 | criterion_group!(benches, 363 | bench_real_transaction, 364 | bench_real_deploy_transaction, 365 | bench_init_account_transaction, 366 | bench_check_execute_bounced_message, 367 | bench_check_execute_out_message_with_body_in_ref, 368 | bench_check_execute_uninit_account, 369 | bench_check_send_remainig_msg_balance, 370 | bench_check_out_of_gas_transaction, 371 | bench_check_wrong_skip_reason, 372 | bench_check_wrong_compute_phase, 373 | bench_check_nofunds_to_send_message_without_error, 374 | bench_bounce_message_to_new_account, 375 | bench_out_of_gas_in_cmd, 376 | bench_freeze_account, 377 | bench_send_to_frozen_account, 378 | bench_unfreeze_account, 379 | bench_bounce_to_empty_account, 380 | bench_bounce_to_low_balance_account, 381 | bench_depool_balance_check, 382 | bench_no_balance_to_send_transaction, 383 | bench_int_message_to_elector_transaction, 384 | bench_int_message_to_elector2_transaction, 385 | bench_ihr_message, 386 | bench_tick_tock_message, 387 | bench_count_steps_vm, 388 | bench_not_aborted_accepted_transaction, 389 | bench_ihr_fee_output_msg, 390 | bench_block_0, 391 | ); 392 | criterion_main!(benches); 393 | -------------------------------------------------------------------------------- /deps_map.json: -------------------------------------------------------------------------------- 1 | {"ton-block":"821c95532e4e569dbb1e068f2959d32f47421cbc","ton-types":"ce59ecfb5da3611c93c396eb730b8dcee565df7a","ton-vm":"52f13cab4f6773f87f664aa5827f90b91fe5a15f"} -------------------------------------------------------------------------------- /real_ever_boc/account_with_deploy_contract_with_lib_function.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/account_with_deploy_contract_with_lib_function.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_message_to_new_account_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_message_to_new_account_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_message_to_new_account_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_message_to_new_account_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_message_to_new_account_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_message_to_new_account_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_msg_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_msg_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_msg_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_msg_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_msg_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_msg_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_to_empty_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_to_empty_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_to_empty_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_to_empty_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_to_empty_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_to_empty_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_to_low_balance_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_to_low_balance_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_to_low_balance_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_to_low_balance_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/bounce_to_low_balance_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/bounce_to_low_balance_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/config.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/config.boc -------------------------------------------------------------------------------- /real_ever_boc/count_steps_acc_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/count_steps_acc_new.boc -------------------------------------------------------------------------------- /real_ever_boc/count_steps_acc_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/count_steps_acc_old.boc -------------------------------------------------------------------------------- /real_ever_boc/count_steps_tx.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/count_steps_tx.boc -------------------------------------------------------------------------------- /real_ever_boc/default_config.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/default_config.boc -------------------------------------------------------------------------------- /real_ever_boc/deploy_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/deploy_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/deploy_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/deploy_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/deploy_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/deploy_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/depool_balance_check_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/depool_balance_check_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/depool_balance_check_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/depool_balance_check_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/depool_balance_check_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/depool_balance_check_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/freeze_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/freeze_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/freeze_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/freeze_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/freeze_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/freeze_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/ihr_fee_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/ihr_fee_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/ihr_fee_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/ihr_fee_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/ihr_fee_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/ihr_fee_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/ihr_message_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/ihr_message_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/ihr_message_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/ihr_message_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/ihr_message_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/ihr_message_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/init_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/init_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/init_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/init_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/init_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/init_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/int_message_to_elector2_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/int_message_to_elector2_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/int_message_to_elector2_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/int_message_to_elector2_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/int_message_to_elector2_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/int_message_to_elector2_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/int_message_to_elector_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/int_message_to_elector_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/int_message_to_elector_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/int_message_to_elector_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/int_message_to_elector_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/int_message_to_elector_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/msg_body_ref_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/msg_body_ref_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/msg_body_ref_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/msg_body_ref_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/msg_body_ref_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/msg_body_ref_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/msg_with_deploy_contract_with_lib_function.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/msg_with_deploy_contract_with_lib_function.boc -------------------------------------------------------------------------------- /real_ever_boc/no_balance_to_send_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/no_balance_to_send_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/no_balance_to_send_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/no_balance_to_send_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/no_balance_to_send_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/no_balance_to_send_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/nofunds_without_error_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/nofunds_without_error_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/nofunds_without_error_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/nofunds_without_error_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/nofunds_without_error_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/nofunds_without_error_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/not_abort_accept_account_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/not_abort_accept_account_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/not_abort_accept_account_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/not_abort_accept_account_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/not_abort_accept_account_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/not_abort_accept_account_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/out_of_gas_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/out_of_gas_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/out_of_gas_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/out_of_gas_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/out_of_gas_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/out_of_gas_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/reserve_value_from_account.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/reserve_value_from_account.boc -------------------------------------------------------------------------------- /real_ever_boc/reserve_value_message.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/reserve_value_message.boc -------------------------------------------------------------------------------- /real_ever_boc/revert_action_account.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/revert_action_account.boc -------------------------------------------------------------------------------- /real_ever_boc/revert_action_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/revert_action_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/send_remainig_msg_balance_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/send_remainig_msg_balance_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/send_remainig_msg_balance_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/send_remainig_msg_balance_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/send_remainig_msg_balance_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/send_remainig_msg_balance_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/send_to_frozen_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/send_to_frozen_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/send_to_frozen_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/send_to_frozen_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/send_to_frozen_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/send_to_frozen_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/simple_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/simple_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/simple_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/simple_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/simple_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/simple_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/tick_tock_acc_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/tick_tock_acc_new.boc -------------------------------------------------------------------------------- /real_ever_boc/tick_tock_acc_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/tick_tock_acc_old.boc -------------------------------------------------------------------------------- /real_ever_boc/tick_tock_tx.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/tick_tock_tx.boc -------------------------------------------------------------------------------- /real_ever_boc/unfreeze_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/unfreeze_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/unfreeze_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/unfreeze_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/unfreeze_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/unfreeze_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/uninit_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/uninit_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/uninit_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/uninit_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/uninit_account_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/uninit_account_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/wrong_compute_phase_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/wrong_compute_phase_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/wrong_compute_phase_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/wrong_compute_phase_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/wrong_compute_phase_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/wrong_compute_phase_transaction.boc -------------------------------------------------------------------------------- /real_ever_boc/wrong_skip_reason_account_new.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/wrong_skip_reason_account_new.boc -------------------------------------------------------------------------------- /real_ever_boc/wrong_skip_reason_account_old.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/wrong_skip_reason_account_old.boc -------------------------------------------------------------------------------- /real_ever_boc/wrong_skip_reason_transaction.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/venom-blockchain/executor/3d104e4ca829397285302ad56bdffe3b03e1897c/real_ever_boc/wrong_skip_reason_transaction.boc -------------------------------------------------------------------------------- /src/blockchain_config.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use ever_block::{ 15 | ConfigParam18, ConfigParams, FundamentalSmcAddresses, GasLimitsPrices, GlobalCapabilities, Grams, 16 | MsgAddressInt, MsgForwardPrices, StorageInfo, StoragePrices, StorageUsedShort, 17 | }; 18 | use ever_block::{Cell, Result, UInt256}; 19 | 20 | pub const VERSION_BLOCK_REVERT_MESSAGES_WITH_ANYCAST_ADDRESSES: u32 = 8; 21 | pub const VERSION_BLOCK_NEW_CALCULATION_BOUNCED_STORAGE: u32 = 30; 22 | 23 | pub(crate) trait EVERXDefaultConfig { 24 | /// Get default value for masterchain 25 | fn default_mc() -> Self; 26 | /// Get default value for workchains 27 | fn default_wc() -> Self; 28 | } 29 | 30 | impl EVERXDefaultConfig for MsgForwardPrices { 31 | fn default_mc() -> Self { 32 | MsgForwardPrices { 33 | lump_price: 10000000, 34 | bit_price: 655360000, 35 | cell_price: 65536000000, 36 | ihr_price_factor: 98304, 37 | first_frac: 21845, 38 | next_frac: 21845 39 | } 40 | } 41 | 42 | fn default_wc() -> Self { 43 | MsgForwardPrices { 44 | lump_price: 1000000, 45 | bit_price: 65536000, 46 | cell_price: 6553600000, 47 | ihr_price_factor: 98304, 48 | first_frac: 21845, 49 | next_frac: 21845 50 | } 51 | } 52 | } 53 | 54 | pub trait CalcMsgFwdFees { 55 | fn fwd_fee(&self, msg_cell: &Cell) -> u128; 56 | fn ihr_fee_checked(&self, fwd_fee: &Grams) -> Result; 57 | fn mine_fee_checked(&self, fwd_fee: &Grams) -> Result; 58 | fn next_fee_checked(&self, fwd_fee: &Grams) -> Result; 59 | } 60 | 61 | impl CalcMsgFwdFees for MsgForwardPrices { 62 | /// Calculate message forward fee 63 | /// Forward fee is calculated according to the following formula: 64 | /// `fwd_fee = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16))`. 65 | /// `msg.bits` and `msg.cells` are calculated from message represented as tree of cells. Root cell is not counted. 66 | fn fwd_fee(&self, msg_cell: &Cell) -> u128 { 67 | let mut storage = StorageUsedShort::default(); 68 | storage.append(msg_cell); 69 | let mut bits = storage.bits() as u128; 70 | let mut cells = storage.cells() as u128; 71 | bits -= msg_cell.bit_length() as u128; 72 | cells -= 1; 73 | 74 | // All prices except `lump_price` are presented in `0xffff * price` form. 75 | // It is needed because `ihr_factor`, `first_frac` and `next_frac` are not integer values 76 | // but calculations are performed in integers, so prices are multiplied to some big 77 | // number (0xffff) and fee calculation uses such values. At the end result is divided by 78 | // 0xffff with ceil rounding to obtain nanograms (add 0xffff and then `>> 16`) 79 | self.lump_price as u128 + ((cells * self.cell_price as u128 + bits * self.bit_price as u128 + 0xffff) >> 16) 80 | } 81 | 82 | /// Calculate message IHR fee 83 | /// IHR fee is calculated as `(msg_forward_fee * ihr_factor) >> 16` 84 | fn ihr_fee_checked(&self, fwd_fee: &Grams) -> Result { 85 | Grams::new((fwd_fee.as_u128() * self.ihr_price_factor as u128) >> 16) 86 | } 87 | 88 | /// Calculate mine part of forward fee 89 | /// Forward fee for internal message is splited to `int_msg_mine_fee` and `int_msg_remain_fee`: 90 | /// `msg_forward_fee = int_msg_mine_fee + int_msg_remain_fee` 91 | /// `int_msg_mine_fee` is a part of transaction `total_fees` and will go validators of account's shard 92 | /// `int_msg_remain_fee` is placed in header of internal message and will go to validators 93 | /// of shard to which message destination address is belong. 94 | fn mine_fee_checked(&self, fwd_fee: &Grams) -> Result { 95 | Grams::new((fwd_fee.as_u128() * self.first_frac as u128) >> 16) 96 | } 97 | fn next_fee_checked(&self, fwd_fee: &Grams) -> Result { 98 | Grams::new((fwd_fee.as_u128() * self.next_frac as u128) >> 16) 99 | } 100 | } 101 | 102 | #[derive(Clone)] 103 | pub struct AccStoragePrices { 104 | prices: Vec 105 | } 106 | 107 | impl Default for AccStoragePrices { 108 | fn default() -> Self { 109 | AccStoragePrices { 110 | prices: vec![ 111 | StoragePrices { 112 | utime_since: 0, 113 | bit_price_ps: 1, 114 | cell_price_ps: 500, 115 | mc_bit_price_ps: 1000, 116 | mc_cell_price_ps: 500000, 117 | } 118 | ] 119 | } 120 | } 121 | } 122 | 123 | impl AccStoragePrices { 124 | /// Calculate storage fee for provided data 125 | pub fn calc_storage_fee(&self, cells: u128, bits: u128, mut last_paid: u32, now: u32, is_masterchain: bool) -> u128 { 126 | if now <= last_paid || last_paid == 0 || self.prices.is_empty() || now <= self.prices[0].utime_since { 127 | return 0 128 | } 129 | let mut fee = 0u128; 130 | // storage prices config contains prices array for some time intervals 131 | // to calculate account storage fee we need to sum fees for all intervals since last 132 | // storage fee pay calculated by formula `(cells * cell_price + bits * bits_price) * interval` 133 | for i in 0 .. self.prices.len() { 134 | let prices = &self.prices[i]; 135 | let end = if i < self.prices.len() - 1 { 136 | self.prices[i + 1].utime_since 137 | } else { 138 | now 139 | }; 140 | 141 | if end >= last_paid { 142 | let delta = end - std::cmp::max(prices.utime_since, last_paid); 143 | fee += if is_masterchain { 144 | (cells * prices.mc_cell_price_ps as u128 + bits * prices.mc_bit_price_ps as u128) * delta as u128 145 | } else { 146 | (cells * prices.cell_price_ps as u128 + bits * prices.bit_price_ps as u128) * delta as u128 147 | }; 148 | last_paid = end; 149 | } 150 | } 151 | 152 | // stirage fee is calculated in pseudo values (like forward fee and gas fee) - multiplied 153 | // to 0xffff, so divide by this value with ceil rounding 154 | (fee + 0xffff) >> 16 155 | } 156 | 157 | fn with_config(config: &ConfigParam18) -> Result { 158 | let mut prices = vec![]; 159 | for i in 0..config.len()? { 160 | prices.push(config.get(i as u32)?); 161 | } 162 | 163 | Ok(AccStoragePrices { prices }) 164 | } 165 | } 166 | 167 | impl EVERXDefaultConfig for GasLimitsPrices { 168 | fn default_mc() -> Self { 169 | GasLimitsPrices { 170 | gas_price: 655360000, 171 | flat_gas_limit: 100, 172 | flat_gas_price: 1000000, 173 | gas_limit: 1000000, 174 | special_gas_limit: 10000000, 175 | gas_credit: 10000, 176 | block_gas_limit: 10000000, 177 | freeze_due_limit: 100000000, 178 | delete_due_limit:1000000000, 179 | max_gas_threshold:10000000000, 180 | } 181 | } 182 | 183 | fn default_wc() -> Self { 184 | GasLimitsPrices { 185 | gas_price: 65536000, 186 | flat_gas_limit: 100, 187 | flat_gas_price: 100000, 188 | gas_limit: 1000000, 189 | special_gas_limit: 1000000, 190 | gas_credit: 10000, 191 | block_gas_limit: 10000000, 192 | freeze_due_limit: 100000000, 193 | delete_due_limit:1000000000, 194 | max_gas_threshold:1000000000, 195 | } 196 | } 197 | } 198 | 199 | /// Blockchain configuration parameters 200 | #[derive(Clone)] 201 | pub struct BlockchainConfig { 202 | gas_prices_mc: GasLimitsPrices, 203 | gas_prices_wc: GasLimitsPrices, 204 | fwd_prices_mc: MsgForwardPrices, 205 | fwd_prices_wc: MsgForwardPrices, 206 | storage_prices: AccStoragePrices, 207 | special_contracts: FundamentalSmcAddresses, 208 | capabilities: u64, 209 | global_version: u32, 210 | raw_config: ConfigParams, 211 | } 212 | 213 | impl Default for BlockchainConfig { 214 | fn default() -> Self { 215 | BlockchainConfig { 216 | gas_prices_mc: GasLimitsPrices::default_mc(), 217 | gas_prices_wc: GasLimitsPrices::default_wc(), 218 | fwd_prices_mc: MsgForwardPrices::default_mc(), 219 | fwd_prices_wc: MsgForwardPrices::default_wc(), 220 | storage_prices: AccStoragePrices::default(), 221 | special_contracts: Self::get_default_special_contracts(), 222 | raw_config: Self::get_defult_raw_config(), 223 | global_version: 0, 224 | capabilities: 0x2e, 225 | } 226 | } 227 | } 228 | 229 | impl BlockchainConfig { 230 | 231 | fn get_default_special_contracts() -> FundamentalSmcAddresses { 232 | let mut map = FundamentalSmcAddresses::default(); 233 | map.add_key(&UInt256::with_array([0x33u8; 32])).unwrap(); 234 | map.add_key(&UInt256::with_array([0x66u8; 32])).unwrap(); 235 | map.add_key(& 236 | "34517C7BDF5187C55AF4F8B61FDC321588C7AB768DEE24B006DF29106458D7CF".parse::().unwrap() 237 | ).unwrap(); 238 | map 239 | } 240 | 241 | fn get_defult_raw_config() -> ConfigParams { 242 | ConfigParams { 243 | config_addr: [0x55; 32].into(), 244 | ..ConfigParams::default() 245 | } 246 | } 247 | 248 | /// Create `BlockchainConfig` struct with `ConfigParams` taken from blockchain 249 | pub fn with_config(config: ConfigParams) -> Result { 250 | Ok(BlockchainConfig { 251 | gas_prices_mc: config.gas_prices(true)?, 252 | gas_prices_wc: config.gas_prices(false)?, 253 | fwd_prices_mc: config.fwd_prices(true)?, 254 | fwd_prices_wc: config.fwd_prices(false)?, 255 | storage_prices: AccStoragePrices::with_config(&config.storage_prices()?)?, 256 | special_contracts: config.fundamental_smc_addr()?, 257 | capabilities: config.capabilities(), 258 | global_version: config.global_version(), 259 | raw_config: config, 260 | }) 261 | } 262 | 263 | /// Get `MsgForwardPrices` for message forward fee calculation 264 | pub fn get_fwd_prices(&self, is_masterchain: bool) -> &MsgForwardPrices { 265 | if is_masterchain { 266 | &self.fwd_prices_mc 267 | } else { 268 | &self.fwd_prices_wc 269 | } 270 | } 271 | 272 | /// Calculate gas fee for account 273 | pub fn calc_gas_fee(&self, gas_used: u64, address: &MsgAddressInt) -> u128 { 274 | self.get_gas_config(address.is_masterchain()).calc_gas_fee(gas_used) 275 | } 276 | 277 | /// Get `GasLimitsPrices` for account gas fee calculation 278 | pub fn get_gas_config(&self, is_masterchain: bool) -> &GasLimitsPrices { 279 | if is_masterchain { 280 | &self.gas_prices_mc 281 | } else { 282 | &self.gas_prices_wc 283 | } 284 | } 285 | 286 | /// Calculate forward fee 287 | pub fn calc_fwd_fee(&self, is_masterchain: bool, msg_cell: &Cell) -> Result { 288 | let mut in_fwd_fee = self.get_fwd_prices(is_masterchain).fwd_fee(msg_cell); 289 | if self.raw_config.has_capability(GlobalCapabilities::CapFeeInGasUnits) { 290 | in_fwd_fee = self.get_gas_config(is_masterchain).calc_gas_fee(in_fwd_fee.try_into()?) 291 | } 292 | Grams::new(in_fwd_fee) 293 | } 294 | 295 | /// Calculate account storage fee 296 | pub fn calc_storage_fee(&self, storage: &StorageInfo, is_masterchain: bool, now: u32) -> Result { 297 | let mut storage_fee = self.storage_prices.calc_storage_fee( 298 | storage.used().cells().into(), 299 | storage.used().bits().into(), 300 | storage.last_paid(), 301 | now, 302 | is_masterchain 303 | ); 304 | if self.raw_config.has_capability(GlobalCapabilities::CapFeeInGasUnits) { 305 | storage_fee = self.get_gas_config(is_masterchain).calc_gas_fee(storage_fee.try_into()?) 306 | } 307 | Grams::new(storage_fee) 308 | } 309 | 310 | /// Check if account is special EVERX account 311 | pub fn is_special_account(&self, address: &MsgAddressInt) -> Result { 312 | if address.is_masterchain() { 313 | let account_id = address.get_address(); 314 | // special account adresses are stored in hashmap 315 | // config account is special too 316 | Ok( 317 | self.raw_config.config_addr == account_id || 318 | self.special_contracts.get_raw(account_id)?.is_some() 319 | ) 320 | } else { 321 | Ok(false) 322 | } 323 | } 324 | 325 | pub fn global_version(&self) -> u32 { 326 | self.global_version 327 | } 328 | 329 | pub fn raw_config(&self) -> &ConfigParams { 330 | &self.raw_config 331 | } 332 | 333 | pub fn has_capability(&self, capability: GlobalCapabilities) -> bool { 334 | (self.capabilities & (capability as u64)) != 0 335 | } 336 | 337 | pub fn capabilites(&self) -> u64 { 338 | self.capabilities 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use ever_block::ComputeSkipReason; 15 | use ever_vm::stack::StackItem; 16 | 17 | #[derive(Debug, thiserror::Error, PartialEq)] 18 | pub enum ExecutorError { 19 | #[error("Invalid external message")] 20 | InvalidExtMessage, 21 | #[error("Transaction executor internal error: {0}")] 22 | TrExecutorError(String), 23 | #[error("Contract did not accept message, exit code: {0}")] 24 | NoAcceptError(i32, Option), 25 | #[error("Cannot pay for importing this external message")] 26 | NoFundsToImportMsg, 27 | #[error("Compute phase skipped while processing exteranl inbound messagewith reason {:?}", .0)] 28 | ExtMsgComputeSkipped(ComputeSkipReason) 29 | } 30 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #![cfg_attr(feature = "ci_run", deny(warnings))] 15 | 16 | pub mod transaction_executor; 17 | pub use transaction_executor::*; 18 | 19 | pub mod ordinary_transaction; 20 | pub use ordinary_transaction::OrdinaryTransactionExecutor; 21 | 22 | pub mod tick_tock_transaction; 23 | pub use tick_tock_transaction::TickTockTransactionExecutor; 24 | 25 | #[macro_use] 26 | pub mod error; 27 | pub use error::*; 28 | 29 | pub mod vmsetup; 30 | pub use vmsetup::*; 31 | 32 | pub mod blockchain_config; 33 | pub use blockchain_config::*; 34 | 35 | include!("../common/src/info.rs"); 36 | -------------------------------------------------------------------------------- /src/ordinary_transaction.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use crate::{ 15 | ActionPhaseResult, blockchain_config::BlockchainConfig, error::ExecutorError, 16 | ExecuteParams, TransactionExecutor, VERSION_BLOCK_REVERT_MESSAGES_WITH_ANYCAST_ADDRESSES 17 | }; 18 | #[cfg(feature = "timings")] 19 | use std::sync::atomic::AtomicU64; 20 | use std::sync::atomic::Ordering; 21 | #[cfg(feature = "timings")] 22 | use std::time::Instant; 23 | use ever_block::{ 24 | AccStatusChange, Account, AccountStatus, AddSub, CommonMessage, CommonMsgInfo, Grams, Message, 25 | Serializable, TrBouncePhase, TrComputePhase, Transaction, TransactionDescr, 26 | TransactionDescrOrdinary, MASTERCHAIN_ID, GlobalCapabilities, 27 | SERDE_OPTS_COMMON_MESSAGE, SERDE_OPTS_EMPTY 28 | }; 29 | use ever_block::{error, fail, Result, HashmapType, SliceData}; 30 | use ever_vm::{ 31 | boolean, int, 32 | stack::{integer::IntegerData, Stack, StackItem}, SmartContractInfo, 33 | }; 34 | 35 | #[cfg(test)] 36 | #[path = "tests/test_ordinary_transaction.rs"] 37 | mod tests1; 38 | 39 | #[cfg(test)] 40 | #[path = "tests/test_ordinary_libs_and_code.rs"] 41 | mod tests2; 42 | 43 | #[cfg(test)] 44 | #[path = "tests/test_ordinary_freeze.rs"] 45 | mod tests3; 46 | 47 | #[cfg(test)] 48 | #[path = "tests/test_ordinary_rawreserve.rs"] 49 | mod tests4; 50 | 51 | #[cfg(test)] 52 | #[path = "tests/test_random_gen.rs"] 53 | mod tests5; 54 | 55 | #[cfg(test)] 56 | #[path = "tests/test_currency_collections.rs"] 57 | mod tests6; 58 | 59 | #[cfg(test)] 60 | #[path = "tests/test_bounced_action_phase.rs"] 61 | mod tests7; 62 | 63 | pub struct OrdinaryTransactionExecutor { 64 | config: BlockchainConfig, 65 | 66 | #[cfg(feature="timings")] 67 | timings: [AtomicU64; 3], // 0 - preparation, 1 - compute, 2 - after compute 68 | } 69 | 70 | impl OrdinaryTransactionExecutor { 71 | pub fn new(config: BlockchainConfig) -> Self { 72 | Self { 73 | config, 74 | 75 | #[cfg(feature="timings")] 76 | timings: [AtomicU64::new(0), AtomicU64::new(0), AtomicU64::new(0)], 77 | } 78 | } 79 | 80 | #[cfg(feature="timings")] 81 | pub fn timing(&self, kind: usize) -> u64 { 82 | self.timings[kind].load(Ordering::Relaxed) 83 | } 84 | } 85 | 86 | impl TransactionExecutor for OrdinaryTransactionExecutor { 87 | /// 88 | /// Create end execute transaction from message for account 89 | fn execute_with_params( 90 | &self, 91 | in_msg: Option<&CommonMessage>, 92 | account: &mut Account, 93 | params: ExecuteParams, 94 | ) -> Result { 95 | #[cfg(feature="timings")] 96 | let mut now = Instant::now(); 97 | 98 | let revert_anycast = 99 | self.config.global_version() >= VERSION_BLOCK_REVERT_MESSAGES_WITH_ANYCAST_ADDRESSES; 100 | 101 | let in_common_msg = in_msg.ok_or_else(|| error!("Ordinary transaction must have input message"))?; 102 | let opts = if self.config().has_capability(GlobalCapabilities::CapCommonMessage) { 103 | SERDE_OPTS_COMMON_MESSAGE 104 | } else { 105 | SERDE_OPTS_EMPTY 106 | }; 107 | let in_msg_cell = in_common_msg.serialize_with_opts(opts)?; // TODO: get from outside 108 | let in_std_msg = in_common_msg.get_std()?; 109 | // Serialize cmn msg to cell in legacy format and put on TvmStack in legacy 110 | // format to support old smart contracts. 111 | let in_std_msg_cell = in_std_msg.serialize()?; 112 | let is_masterchain = in_std_msg.dst_workchain_id() == Some(MASTERCHAIN_ID); 113 | log::debug!( 114 | target: "executor", 115 | "Ordinary transaction executing, in message id: {:x}", 116 | in_msg_cell.repr_hash() 117 | ); 118 | let (bounce, is_ext_msg) = match in_std_msg.header() { 119 | CommonMsgInfo::ExtOutMsgInfo(_) => fail!(ExecutorError::InvalidExtMessage), 120 | CommonMsgInfo::IntMsgInfo(ref hdr) => (hdr.bounce, false), 121 | CommonMsgInfo::ExtInMsgInfo(_) => (false, true) 122 | }; 123 | 124 | let account_address = in_std_msg.dst_ref().ok_or_else(|| ExecutorError::TrExecutorError( 125 | format!("Input message {:x} has no dst address", in_msg_cell.repr_hash()) 126 | ))?; 127 | let account_id = match account.get_id() { 128 | Some(account_id) => { 129 | log::debug!(target: "executor", "Account = {:x}", account_id); 130 | account_id 131 | } 132 | None => { 133 | log::debug!(target: "executor", "Account = None, address = {:x}", account_address.address()); 134 | account_address.address() 135 | } 136 | }; 137 | 138 | let mut acc_balance = account.balance().cloned().unwrap_or_default(); 139 | let mut msg_balance = in_std_msg.get_value().cloned().unwrap_or_default(); 140 | let ihr_delivered = false; // ihr is disabled because it does not work 141 | if !ihr_delivered { 142 | if let Some(h) = in_std_msg.int_header() { 143 | msg_balance.grams += h.ihr_fee; 144 | } 145 | } 146 | log::debug!(target: "executor", "acc_balance: {}, msg_balance: {}, credit_first: {}", 147 | acc_balance.grams, msg_balance.grams, !bounce); 148 | 149 | let is_special = self.config.is_special_account(account_address)?; 150 | let lt = std::cmp::max( 151 | account.last_tr_time().unwrap_or(0), 152 | std::cmp::max(params.last_tr_lt.load(Ordering::Relaxed), in_std_msg.lt().unwrap_or(0) + 1) 153 | ); 154 | let mut tr = self.create_transaction(account_id); 155 | tr.orig_status = account.status(); 156 | tr.set_logical_time(lt); 157 | tr.set_now(params.block_unixtime); 158 | tr.write_in_msg(Some(in_common_msg))?; 159 | 160 | let mut description = TransactionDescrOrdinary { 161 | credit_first: !bounce, 162 | ..TransactionDescrOrdinary::default() 163 | }; 164 | 165 | if revert_anycast && account_address.rewrite_pfx().is_some() { 166 | description.aborted = true; 167 | tr.set_end_status(account.status()); 168 | params.last_tr_lt.store(lt, Ordering::Relaxed); 169 | account.set_last_tr_time(lt); 170 | tr.write_description(&TransactionDescr::Ordinary(description))?; 171 | return Ok(tr); 172 | } 173 | 174 | // first check if contract can pay for importing external message 175 | if is_ext_msg && !is_special { 176 | // extranal message comes serialized 177 | let in_fwd_fee = self.config.calc_fwd_fee(is_masterchain, &in_msg_cell)?; 178 | log::debug!(target: "executor", "import message fee: {}, acc_balance: {}", in_fwd_fee, acc_balance.grams); 179 | if !acc_balance.grams.sub(&in_fwd_fee)? { 180 | fail!(ExecutorError::NoFundsToImportMsg) 181 | } 182 | tr.add_fee_grams(&in_fwd_fee)?; 183 | } 184 | 185 | if description.credit_first && !is_ext_msg { 186 | description.credit_ph = match self.credit_phase(account, &mut tr, &mut msg_balance, &mut acc_balance) { 187 | Ok(credit_ph) => Some(credit_ph), 188 | Err(e) => fail!( 189 | ExecutorError::TrExecutorError( 190 | format!("cannot create credit phase of a new transaction for smart contract for reason {}", e) 191 | ) 192 | ) 193 | }; 194 | } 195 | let due_before_storage = account.due_payment().map(|due| due.as_u128()); 196 | let mut storage_fee; 197 | description.storage_ph = match self.storage_phase( 198 | account, 199 | &mut acc_balance, 200 | &mut tr, 201 | is_masterchain, 202 | is_special 203 | ) { 204 | Ok(storage_ph) => { 205 | storage_fee = storage_ph.storage_fees_collected.as_u128(); 206 | if !self.config().has_capability(GlobalCapabilities::CapDuePaymentFix) { 207 | if let Some(due) = &storage_ph.storage_fees_due { 208 | storage_fee += due.as_u128() 209 | } 210 | if let Some(due) = due_before_storage { 211 | storage_fee -= due; 212 | } 213 | } 214 | Some(storage_ph) 215 | }, 216 | Err(e) => fail!( 217 | ExecutorError::TrExecutorError( 218 | format!("cannot create storage phase of a new transaction for smart contract for reason {}", e) 219 | ) 220 | ) 221 | }; 222 | 223 | if description.credit_first && msg_balance.grams > acc_balance.grams { 224 | msg_balance.grams = acc_balance.grams; 225 | } 226 | 227 | log::debug!(target: "executor", 228 | "storage_phase: {}", if description.storage_ph.is_some() {"present"} else {"none"}); 229 | let mut original_acc_balance = account.balance().cloned().unwrap_or_default(); 230 | original_acc_balance.sub(tr.total_fees())?; 231 | 232 | if !description.credit_first && !is_ext_msg { 233 | description.credit_ph = match self.credit_phase(account, &mut tr, &mut msg_balance, &mut acc_balance) { 234 | Ok(credit_ph) => Some(credit_ph), 235 | Err(e) => fail!( 236 | ExecutorError::TrExecutorError( 237 | format!("cannot create credit phase of a new transaction for smart contract for reason {}", e) 238 | ) 239 | ) 240 | }; 241 | } 242 | log::debug!(target: "executor", 243 | "credit_phase: {}", if description.credit_ph.is_some() {"present"} else {"none"}); 244 | 245 | let last_paid = if !is_special {params.block_unixtime} else {0}; 246 | account.set_last_paid(last_paid); 247 | #[cfg(feature="timings")] { 248 | self.timings[0].fetch_add(now.elapsed().as_micros() as u64, Ordering::SeqCst); 249 | now = Instant::now(); 250 | } 251 | 252 | let config_params = self.config().raw_config().config_params.data().cloned(); 253 | let mut smc_info = SmartContractInfo { 254 | capabilities: self.config().raw_config().capabilities(), 255 | myself: SliceData::load_builder(account_address.write_to_new_cell().unwrap_or_default())?, 256 | block_lt: params.block_lt, 257 | trans_lt: lt, 258 | unix_time: params.block_unixtime, 259 | seq_no: params.seq_no, 260 | balance: acc_balance.clone(), 261 | config_params, 262 | ..Default::default() 263 | }; 264 | smc_info.calc_rand_seed(params.seed_block.clone(), &account_address.address().get_bytestring(0)); 265 | let mut stack = Stack::new(); 266 | stack 267 | .push(int!(acc_balance.grams.as_u128())) 268 | .push(int!(msg_balance.grams.as_u128())) 269 | .push(StackItem::Cell(in_std_msg_cell)) 270 | .push(StackItem::Slice(in_std_msg.body().unwrap_or_default())) 271 | .push(boolean!(is_ext_msg)); 272 | log::debug!(target: "executor", "compute_phase"); 273 | let (compute_ph, actions, new_data) = match self.compute_phase( 274 | Some(in_std_msg), 275 | account, 276 | &mut acc_balance, 277 | &msg_balance, 278 | smc_info, 279 | stack, 280 | storage_fee, 281 | is_masterchain, 282 | is_special, 283 | ¶ms, 284 | ) { 285 | Ok((compute_ph, actions, new_data)) => (compute_ph, actions, new_data), 286 | Err(e) => { 287 | log::debug!(target: "executor", "compute_phase error: {}", e); 288 | match e.downcast_ref::() { 289 | Some(ExecutorError::NoAcceptError(_, _)) => return Err(e), 290 | _ => fail!(ExecutorError::TrExecutorError(e.to_string())) 291 | } 292 | } 293 | }; 294 | let mut out_msgs = vec![]; 295 | let mut action_phase_processed = false; 296 | let mut compute_phase_gas_fees = Grams::zero(); 297 | let mut copyleft = None; 298 | description.compute_ph = compute_ph; 299 | let mut new_acc_balance = acc_balance.clone(); 300 | description.action = match &description.compute_ph { 301 | TrComputePhase::Vm(phase) => { 302 | compute_phase_gas_fees = phase.gas_fees; 303 | tr.add_fee_grams(&phase.gas_fees)?; 304 | if phase.success { 305 | log::debug!(target: "executor", "compute_phase: success"); 306 | log::debug!(target: "executor", "action_phase: lt={}", lt); 307 | action_phase_processed = true; 308 | match self.action_phase_with_copyleft( 309 | &mut tr, 310 | account, 311 | &original_acc_balance, 312 | &mut new_acc_balance, 313 | &mut msg_balance, 314 | &compute_phase_gas_fees, 315 | actions.unwrap_or_default(), 316 | new_data, 317 | account_address, 318 | is_special 319 | ) { 320 | Ok(ActionPhaseResult{phase, messages, copyleft_reward}) => { 321 | out_msgs = messages; 322 | if let Some(copyleft_reward) = ©left_reward { 323 | tr.total_fees_mut().grams.sub(©left_reward.reward)?; 324 | } 325 | copyleft = copyleft_reward; 326 | Some(phase) 327 | } 328 | Err(e) => fail!( 329 | ExecutorError::TrExecutorError( 330 | format!("cannot create action phase of a new transaction for smart contract for reason {}", e) 331 | ) 332 | ) 333 | } 334 | } else { 335 | log::debug!(target: "executor", "compute_phase: failed"); 336 | None 337 | } 338 | } 339 | TrComputePhase::Skipped(skipped) => { 340 | log::debug!(target: "executor", "compute_phase: skipped reason {:?}", skipped.reason); 341 | if is_ext_msg { 342 | fail!(ExecutorError::ExtMsgComputeSkipped(skipped.reason.clone())) 343 | } 344 | None 345 | } 346 | }; 347 | 348 | #[cfg(feature="timings")] { 349 | self.timings[1].fetch_add(now.elapsed().as_micros() as u64, Ordering::SeqCst); 350 | now = Instant::now(); 351 | } 352 | 353 | description.aborted = match description.action.as_ref() { 354 | Some(phase) => { 355 | log::debug!( 356 | target: "executor", 357 | "action_phase: present: success={}, err_code={}", phase.success, phase.result_code 358 | ); 359 | if AccStatusChange::Deleted == phase.status_change { 360 | *account = Account::default(); 361 | description.destroyed = true; 362 | } 363 | if phase.success { 364 | acc_balance = new_acc_balance; 365 | } 366 | !phase.success 367 | } 368 | None => { 369 | log::debug!(target: "executor", "action_phase: none"); 370 | true 371 | } 372 | }; 373 | 374 | log::debug!(target: "executor", "Desciption.aborted {}", description.aborted); 375 | if description.aborted && !is_ext_msg && bounce { 376 | if !action_phase_processed || self.config().has_capability(GlobalCapabilities::CapBounceAfterFailedAction) { 377 | log::debug!(target: "executor", "bounce_phase"); 378 | description.bounce = match self.bounce_phase( 379 | msg_balance.clone(), 380 | &mut acc_balance, 381 | &compute_phase_gas_fees, 382 | in_std_msg, 383 | &mut tr, 384 | account_address, 385 | params.block_version, 386 | ) { 387 | Ok((bounce_ph, Some(bounce_msg))) => { 388 | out_msgs.push(bounce_msg); 389 | Some(bounce_ph) 390 | } 391 | Ok((bounce_ph, None)) => Some(bounce_ph), 392 | Err(e) => fail!( 393 | ExecutorError::TrExecutorError( 394 | format!("cannot create bounce phase of a new transaction for smart contract for reason {}", e) 395 | ) 396 | ) 397 | }; 398 | } 399 | // if money can be returned to sender 400 | // restore account balance - storage fee 401 | if let Some(TrBouncePhase::Ok(_)) = description.bounce { 402 | log::debug!(target: "executor", "restore balance {} => {}", acc_balance.grams, original_acc_balance.grams); 403 | acc_balance = original_acc_balance; 404 | } else if account.is_none() && !acc_balance.is_zero()? { 405 | *account = Account::uninit( 406 | account_address.clone(), 407 | 0, 408 | last_paid, 409 | acc_balance.clone() 410 | ); 411 | } 412 | } 413 | if (account.status() == AccountStatus::AccStateUninit) && acc_balance.is_zero()? { 414 | *account = Account::default(); 415 | } 416 | tr.set_end_status(account.status()); 417 | log::debug!(target: "executor", "set balance {}", acc_balance.grams); 418 | account.set_balance(acc_balance); 419 | log::debug!(target: "executor", "add messages"); 420 | params.last_tr_lt.store(lt, Ordering::Relaxed); 421 | let lt = self.add_messages(&mut tr, out_msgs, params.last_tr_lt)?; 422 | account.set_last_tr_time(lt); 423 | tr.write_description(&TransactionDescr::Ordinary(description))?; 424 | #[cfg(feature="timings")] 425 | self.timings[2].fetch_add(now.elapsed().as_micros() as u64, Ordering::SeqCst); 426 | tr.set_copyleft_reward(copyleft); 427 | Ok(tr) 428 | } 429 | fn ordinary_transaction(&self) -> bool { true } 430 | fn config(&self) -> &BlockchainConfig { &self.config } 431 | fn build_stack(&self, in_msg: Option<&Message>, account: &Account) -> Result { 432 | let mut stack = Stack::new(); 433 | let in_msg = match in_msg { 434 | Some(in_msg) => in_msg, 435 | None => return Ok(stack) 436 | }; 437 | let acc_balance = int!(account.balance().map_or(0, |value| value.grams.as_u128())); 438 | let msg_balance = int!(in_msg.get_value().map_or(0, |value| value.grams.as_u128())); 439 | let function_selector = boolean!(in_msg.is_inbound_external()); 440 | let body_slice = in_msg.body().unwrap_or_default(); 441 | let in_msg_cell = in_msg.serialize().unwrap_or_default(); 442 | stack 443 | .push(acc_balance) 444 | .push(msg_balance) 445 | .push(StackItem::Cell(in_msg_cell)) 446 | .push(StackItem::Slice(body_slice)) 447 | .push(function_selector); 448 | Ok(stack) 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /src/tests/common/cross_check.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2020 EVERX DEV SOLUTIONS LTD. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #![cfg(test)] 15 | #![allow(dead_code)] 16 | 17 | use super::*; 18 | use std::sync::Mutex; 19 | use lazy_static::lazy_static; 20 | use pretty_assertions::assert_eq; 21 | use std::collections::HashSet; 22 | use std::thread::ThreadId; 23 | use ever_block::{ 24 | accounts::Account, messages::Message, transactions::Transaction, Deserializable, Serializable, 25 | }; 26 | use ever_block::read_single_root_boc; 27 | use std::thread; 28 | use core::sync::atomic::Ordering; 29 | use crate::BlockchainConfig; 30 | use crate::ExecuteParams; 31 | 32 | lazy_static! { 33 | static ref DISABLED_TESTS: Mutex> = Mutex::new(HashSet::::default()); 34 | static ref DISABLED_TESTS_SCOPE: Mutex> = Mutex::new(HashSet::::default()); 35 | } 36 | 37 | pub(crate) fn disable_cross_check() { 38 | let thread_id = thread::current().id(); 39 | DISABLED_TESTS.lock().unwrap().insert(thread_id); 40 | } 41 | 42 | pub struct DisableCrossCheck { 43 | thread_id: ThreadId 44 | } 45 | 46 | impl DisableCrossCheck { 47 | pub fn new() -> DisableCrossCheck { 48 | let thread_id = thread::current().id(); 49 | DISABLED_TESTS_SCOPE.lock().unwrap().insert(thread_id); 50 | DisableCrossCheck { thread_id } 51 | } 52 | } 53 | 54 | impl Drop for DisableCrossCheck { 55 | fn drop(&mut self) { 56 | DISABLED_TESTS.lock().unwrap().remove(&self.thread_id); 57 | } 58 | } 59 | 60 | fn load_cells(data: &[u8]) -> Cell { 61 | read_single_root_boc(data).unwrap() 62 | } 63 | 64 | pub(crate) fn cross_check(config: &BlockchainConfig, acc_before: &Account, acc_after: &Account, msg: Option<&Message>, transaction: Option<&Transaction>, params: &ExecuteParams, _it: u32) { 65 | let thread_id = thread::current().id(); 66 | 67 | if DISABLED_TESTS.lock().unwrap().remove(&thread_id) || DISABLED_TESTS_SCOPE.lock().unwrap().contains(&thread_id) { 68 | return; 69 | } 70 | 71 | let lib_name = "libtransaction-replayer-lib-dyn.so"; 72 | let lib = libloading::Library::new(lib_name).unwrap(); 73 | 74 | let acc_data = acc_before.write_to_bytes().unwrap(); 75 | let cfg_data = config.raw_config().write_to_bytes().unwrap(); 76 | 77 | let mut res_acc_size: i32 = 500000; 78 | let mut res_tx_size: i32 = 500000; 79 | let mut res_acc = vec![0u8; res_acc_size as usize]; 80 | let mut res_tx = vec![0u8; res_tx_size as usize]; 81 | 82 | if let Some(msg) = msg { 83 | let msg_data = msg.write_to_bytes().unwrap(); 84 | 85 | unsafe { 86 | type RunBoc<'a> = libloading::Symbol<'a, 87 | unsafe extern "C" fn(*const u8, i32, *const u8, i32, *const u8, i32, u64, i32, u64, *mut u8, &mut i32, *mut u8, &mut i32) -> bool 88 | >; 89 | let run_boc: RunBoc = lib.get(b"replay_ordinary_transaction_ext").unwrap(); 90 | 91 | let res: bool = run_boc( 92 | acc_data.as_ptr(), acc_data.len() as i32, 93 | msg_data.as_ptr(), msg_data.len() as i32, 94 | cfg_data.as_ptr(), cfg_data.len() as i32, 95 | params.last_tr_lt.load(Ordering::Relaxed), params.block_unixtime as i32, params.block_lt, 96 | res_acc.as_mut_ptr(), &mut res_acc_size, res_tx.as_mut_ptr(), &mut res_tx_size 97 | ); 98 | assert!(res, "check preallocated size for output data"); 99 | } 100 | } else { 101 | let tick = if let TransactionDescr::TickTock(descr) = transaction.unwrap().read_description().unwrap() { 102 | descr.tt.is_tick() 103 | } else { 104 | unreachable!(); 105 | }; 106 | unsafe { 107 | type RunBoc<'a> = libloading::Symbol<'a, 108 | unsafe extern "C" fn(*const u8, i32, *const u8, i32, u64, i32, u64, bool, *mut u8, &mut i32, *mut u8, &mut i32) -> bool 109 | >; 110 | let run_boc: RunBoc = lib.get(b"replay_ticktock_transaction_ext").unwrap(); 111 | 112 | let res: bool = run_boc( 113 | acc_data.as_ptr(), acc_data.len() as i32, 114 | cfg_data.as_ptr(), cfg_data.len() as i32, 115 | params.last_tr_lt.load(Ordering::Relaxed), params.block_unixtime as i32, params.block_lt, tick, 116 | res_acc.as_mut_ptr(), &mut res_acc_size, res_tx.as_mut_ptr(), &mut res_tx_size 117 | ); 118 | assert!(res, "check preallocated size for output data"); 119 | } 120 | } 121 | 122 | if res_tx_size != 0 { 123 | let acc_file_cells = load_cells(&res_acc[0..res_acc_size as usize]); 124 | let tx_file_cells = load_cells(&res_tx[0..res_tx_size as usize]); 125 | 126 | let mut acc_file = Account::construct_from_cell(acc_file_cells.clone()).unwrap(); 127 | let tx_file = Transaction::construct_from_cell(tx_file_cells.clone()).unwrap(); 128 | 129 | let mut acc_after_copy = acc_after.clone(); 130 | acc_after_copy.update_storage_stat().unwrap(); 131 | acc_file.update_storage_stat().unwrap(); 132 | assert!(transaction.is_some()); 133 | let mut transaction = transaction.unwrap().clone(); 134 | transaction.write_state_update(&tx_file.read_state_update().unwrap()).unwrap(); 135 | 136 | /*if acc_after_copy != acc_file { 137 | println!("Iteration {}", _it); 138 | }*/ 139 | assert_eq!(transaction.read_description().unwrap(), tx_file.read_description().unwrap()); 140 | assert_eq!(acc_after_copy, acc_file); 141 | transaction.out_msgs.scan_diff(&tx_file.out_msgs, |key: ever_block::U15, msg1, msg2| { 142 | assert_eq!(msg1, msg2, "for key {}", key.0); 143 | Ok(true) 144 | }).unwrap(); 145 | assert_eq!(transaction, tx_file); 146 | 147 | assert_eq!(acc_after_copy.serialize().unwrap(), acc_file_cells); 148 | assert_eq!(transaction.serialize().unwrap(), tx_file_cells); 149 | } else { 150 | assert!(transaction.is_none()); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | #![cfg(test)] 15 | #![allow(dead_code)] 16 | #![allow(clippy::duplicate_mod)] 17 | #![allow(clippy::field_reassign_with_default)] 18 | 19 | pub(crate) mod cross_check; 20 | 21 | use super::*; 22 | 23 | use std::sync::{atomic::AtomicU64, Arc}; 24 | 25 | use crate::{OrdinaryTransactionExecutor, TickTockTransactionExecutor}; 26 | use pretty_assertions::assert_eq; 27 | use ever_block::{ 28 | accounts::{Account, AccountStorage, StorageInfo}, 29 | config_params::GlobalCapabilities, 30 | messages::{ 31 | CommonMsgInfo, ExternalInboundMessageHeader, InternalMessageHeader, Message, MsgAddressInt, 32 | }, 33 | transactions::{ 34 | TrComputePhase, Transaction, TransactionDescr 35 | }, 36 | AddSub, ConfigParam8, ConfigParamEnum, ConfigParams, CurrencyCollection, Deserializable, Grams, 37 | Serializable, StateInit, TrBouncePhase, TransactionDescrOrdinary, TransactionTickTock, 38 | UnixTime32, VarUInteger32, AccStatusChange, CommonMessage, 39 | }; 40 | use ever_assembler::compile_code_to_cell; 41 | use ever_block::{AccountId, BuilderData, Cell, HashmapE, SliceData, UInt256, Result}; 42 | use crate::{BlockchainConfig, ExecuteParams, TransactionExecutor}; 43 | 44 | pub const BLOCK_LT: u64 = 2_000_000_000; 45 | pub const PREV_BLOCK_LT: u64 = 1_998_000_000; 46 | pub const ACCOUNT_UT: u32 = 1572169011; 47 | pub const BLOCK_UT: u32 = 1576526553; 48 | pub const MSG1_BALANCE: u64 = 50_000_000; 49 | pub const MSG2_BALANCE: u64 = 100_000_000; 50 | pub const MSG_FWD_FEE: u64 = 10_000_000; 51 | pub const MSG_MINE_FEE: u64 = 3_333_282; 52 | 53 | lazy_static::lazy_static! { 54 | pub static ref SENDER_ACCOUNT: AccountId = AccountId::from([0x11; 32]); 55 | pub static ref RECEIVER_ACCOUNT: AccountId = AccountId::from([0x22; 32]); 56 | pub static ref BLOCKCHAIN_CONFIG: BlockchainConfig = default_config(); 57 | } 58 | 59 | pub fn make_common(std_msg: Message) -> CommonMessage { 60 | CommonMessage::Std(std_msg) 61 | } 62 | 63 | pub fn read_config() -> Result { 64 | ever_block::Deserializable::construct_from_file("real_ever_boc/default_config.boc") 65 | } 66 | 67 | pub fn custom_config(version: Option, capabilities: Option) -> BlockchainConfig { 68 | let mut config = read_config().unwrap(); 69 | let mut param8 = ConfigParam8 { 70 | global_version: config.get_global_version().unwrap() 71 | }; 72 | if let Some(version) = version { 73 | param8.global_version.version = version 74 | } 75 | if let Some(capabilities) = capabilities { 76 | param8.global_version.capabilities |= capabilities 77 | } 78 | config.set_config(ConfigParamEnum::ConfigParam8(param8)).unwrap(); 79 | BlockchainConfig::with_config(config).unwrap() 80 | } 81 | 82 | pub fn default_config() -> BlockchainConfig { 83 | BlockchainConfig::with_config(read_config().unwrap()).unwrap() 84 | } 85 | 86 | pub fn execute_params( 87 | last_tr_lt: Arc, 88 | state_libs: HashmapE, 89 | seed_block: UInt256, 90 | block_version: u32, 91 | ) -> ExecuteParams { 92 | ExecuteParams { 93 | state_libs, 94 | block_unixtime: BLOCK_UT, 95 | block_lt: BLOCK_LT, 96 | last_tr_lt, 97 | seed_block, 98 | debug: true, 99 | block_version, 100 | ..ExecuteParams::default() 101 | } 102 | } 103 | 104 | pub fn execute_params_none(last_tr_lt: Arc) -> ExecuteParams { 105 | execute_params(last_tr_lt, HashmapE::default(), UInt256::default(), 0) 106 | } 107 | 108 | pub fn execute_params_block_version(last_tr_lt: Arc, block_version: u32) -> ExecuteParams { 109 | execute_params(last_tr_lt, HashmapE::default(), UInt256::default(), block_version) 110 | } 111 | 112 | pub fn create_two_internal_messages() -> (Message, Message) { 113 | let msg1 = create_int_msg( 114 | AccountId::from([0x11; 32]), 115 | AccountId::from([0x33; 32]), 116 | MSG1_BALANCE, 117 | false, 118 | BLOCK_LT + 2 119 | ); 120 | let msg2 = create_int_msg( 121 | AccountId::from([0x11; 32]), 122 | AccountId::from([0x33; 32]), 123 | MSG2_BALANCE, 124 | true, 125 | BLOCK_LT + 3 126 | ); 127 | (msg1, msg2) 128 | } 129 | 130 | pub fn create_two_messages_data() -> Cell { 131 | let (msg1, msg2) = create_two_internal_messages(); 132 | 133 | let mut b = BuilderData::with_raw(vec![0x55; 32], 256).unwrap(); 134 | b.checked_append_reference(msg2.serialize().unwrap()).unwrap(); 135 | b.checked_append_reference(msg1.serialize().unwrap()).unwrap(); 136 | b.into_cell().unwrap() 137 | } 138 | 139 | pub fn create_two_messages_data_2(src: AccountId, w_id: i8) -> Cell { 140 | let (mut msg1, mut msg2) = create_two_internal_messages(); 141 | msg1.set_src_address(MsgAddressInt::with_standart(None, w_id, src.clone()).unwrap()); 142 | msg2.set_src_address(MsgAddressInt::with_standart(None, w_id, src).unwrap()); 143 | 144 | let mut b = BuilderData::with_raw(vec![0x55; 32], 256).unwrap(); 145 | b.checked_append_reference(msg2.serialize().unwrap()).unwrap(); 146 | b.checked_append_reference(msg1.serialize().unwrap()).unwrap(); 147 | b.into_cell().unwrap() 148 | } 149 | 150 | pub fn create_int_msg_workchain(w_id: i8, src: AccountId, dest: AccountId, value: impl Into, bounce: bool, lt: u64) -> Message { 151 | let mut hdr = InternalMessageHeader::with_addresses( 152 | MsgAddressInt::with_standart(None, w_id, src).unwrap(), 153 | MsgAddressInt::with_standart(None, w_id, dest).unwrap(), 154 | CurrencyCollection::from_grams(value.into()), 155 | ); 156 | hdr.bounce = bounce; 157 | hdr.ihr_disabled = true; 158 | hdr.created_lt = lt; 159 | hdr.created_at = UnixTime32::default(); 160 | Message::with_int_header(hdr) 161 | } 162 | 163 | pub fn create_int_msg(src: AccountId, dest: AccountId, value: impl Into, bounce: bool, lt: u64) -> Message { 164 | create_int_msg_workchain(-1, src, dest, value, bounce, lt) 165 | } 166 | 167 | pub fn create_send_two_messages_code() -> Cell { 168 | compile_code_to_cell(" 169 | ACCEPT 170 | PUSHROOT 171 | CTOS 172 | LDREF 173 | PLDREF 174 | PUSHINT 0 175 | SENDRAWMSG 176 | PUSHINT 0 177 | SENDRAWMSG 178 | ").unwrap() 179 | } 180 | 181 | pub fn create_test_account_workchain(amount: impl Into, w_id: i8, address: AccountId, code: Cell, data: Cell) -> Account { 182 | let mut account = Account::with_storage( 183 | &MsgAddressInt::with_standart( 184 | None, 185 | w_id, 186 | address 187 | ).unwrap(), 188 | &StorageInfo::with_values( 189 | ACCOUNT_UT, 190 | None, 191 | ), 192 | &AccountStorage::active_by_init_code_hash(0, CurrencyCollection::from_grams(amount.into()), StateInit::default(), false), 193 | ); 194 | account.set_code(code); 195 | account.set_data(data); 196 | account.update_storage_stat().unwrap(); 197 | account 198 | } 199 | 200 | pub fn create_test_account(amount: impl Into, address: AccountId, code: Cell, data: Cell) -> Account { 201 | create_test_account_workchain(amount, -1, address, code, data) 202 | } 203 | 204 | pub fn create_test_external_msg() -> Message { 205 | let acc_id = AccountId::from([0x11; 32]); 206 | let hdr = ExternalInboundMessageHeader::new( 207 | Default::default(), 208 | MsgAddressInt::with_standart(None, -1, acc_id).unwrap() 209 | ); 210 | Message::with_ext_in_header_and_body(hdr, SliceData::default()) 211 | } 212 | 213 | pub fn check_account_and_transaction_balances( 214 | acc_before: &Account, 215 | acc_after: &Account, 216 | msg: &Message, 217 | trans: Option<&Transaction>, 218 | ) { 219 | if trans.is_none() { 220 | // no checks needed 221 | return; 222 | } 223 | 224 | let trans = trans.unwrap(); 225 | 226 | let mut left = acc_before.balance().cloned().unwrap_or_default(); 227 | if let Some(value) = msg.get_value() { 228 | left.add(value).unwrap(); 229 | } 230 | left.grams += msg.int_header().map_or(0, |header| header.ihr_fee.as_u128()); 231 | 232 | let mut right = acc_after.balance().cloned().unwrap_or_default(); 233 | right.add(trans.total_fees()).unwrap(); 234 | let copyleft = trans.copyleft_reward().clone(); 235 | let copyleft_reward_after = copyleft.map_or(0.into(), |copyleft| copyleft.reward); 236 | right.grams.add(©left_reward_after).unwrap(); 237 | trans.iterate_out_msgs(|out_msg| { 238 | if let Some(header) = out_msg.get_std().unwrap().int_header() { 239 | right.add(header.value())?; 240 | right.grams.add(header.fwd_fee())?; 241 | right.grams.add(&header.ihr_fee)?; 242 | } 243 | Ok(true) 244 | }).unwrap(); 245 | assert_eq!(left, right); 246 | 247 | // check fees 248 | let descr = trans.read_description().unwrap(); 249 | if let TransactionDescr::Ordinary(descr) = descr { 250 | let mut total_fee = trans.total_fees().clone(); 251 | 252 | let mut fees = CurrencyCollection::default(); 253 | fees.grams += descr.storage_ph.as_ref().map_or(0, |st| st.storage_fees_collected.as_u128()); 254 | if let Some(storage) = descr.storage_ph.as_ref() { 255 | if storage.status_change == AccStatusChange::Deleted { 256 | if descr.credit_first { 257 | fees.add(&msg.get_value().cloned().unwrap_or_default()).unwrap(); 258 | fees.grams -= msg.get_value().cloned().unwrap_or_default().grams; 259 | } 260 | fees.add(&acc_before.balance().cloned().unwrap_or_default()).unwrap(); 261 | fees.grams -= acc_before.balance().map_or(0, |cc| cc.grams.as_u128()); 262 | } 263 | } 264 | if let Some(cr) = descr.credit_ph.as_ref() { 265 | if let Some(g) = cr.due_fees_collected.as_ref() { 266 | fees.grams += *g 267 | } 268 | } 269 | if let TrComputePhase::Vm(cp) = &descr.compute_ph { 270 | fees.grams += cp.gas_fees 271 | } 272 | if let Some(ap) = descr.action.as_ref() { 273 | if ap.success { 274 | fees.grams += ap.total_action_fees() 275 | } 276 | } 277 | if let Some(TrBouncePhase::Ok(bp)) = descr.bounce.as_ref() { 278 | fees.grams += bp.msg_fees 279 | } 280 | let is_special = BLOCKCHAIN_CONFIG.is_special_account(&msg.dst().unwrap()).unwrap(); 281 | let is_ext_msg = matches!(msg.header(), CommonMsgInfo::ExtInMsgInfo(_)); 282 | if is_ext_msg && !is_special { 283 | let in_msg_cell = msg.serialize().unwrap(); 284 | let in_fwd_fee = BLOCKCHAIN_CONFIG.calc_fwd_fee(msg.is_masterchain(), &in_msg_cell).unwrap(); 285 | fees.grams += in_fwd_fee 286 | } 287 | 288 | total_fee.grams.add(©left_reward_after).unwrap(); 289 | 290 | assert_eq!(fees, total_fee); 291 | } 292 | 293 | // check messages fees 294 | let mut fwd_fees = Grams::zero(); 295 | trans.iterate_out_msgs(|out_msg| { 296 | if let Ok(std_msg) = out_msg.get_std() { 297 | if let Some(header) = std_msg.int_header() { 298 | fwd_fees.add(&header.fwd_fee)?; 299 | fwd_fees.add(&header.ihr_fee)?; 300 | } 301 | } 302 | Ok(true) 303 | }).unwrap(); 304 | 305 | let descr = trans.read_description().unwrap(); 306 | if let TransactionDescr::Ordinary(descr) = descr { 307 | let mut trans_fwd_fee = Grams::zero(); 308 | if let Some(ap) = descr.action.as_ref() { 309 | if ap.success { 310 | trans_fwd_fee += ap.total_fwd_fees() - ap.total_action_fees() 311 | } 312 | } 313 | if descr.bounce.is_some() { 314 | if let TrBouncePhase::Ok(bp) = &descr.bounce.as_ref().unwrap() { 315 | trans_fwd_fee += bp.fwd_fees; 316 | } 317 | } 318 | assert_eq!(fwd_fees, trans_fwd_fee); 319 | } 320 | 321 | // check logical time 322 | if !acc_after.is_none() { 323 | assert_eq!( 324 | trans.logical_time() + trans.msg_count() as u64 + 1, 325 | acc_after.last_tr_time().unwrap() 326 | ); 327 | } 328 | 329 | // other checks 330 | assert_eq!(trans.orig_status, acc_before.status()); 331 | assert_eq!(trans.end_status, acc_after.status()); 332 | } 333 | 334 | pub fn check_account_and_transaction( 335 | acc_before: &Account, 336 | acc_after: &Account, 337 | msg: &Message, 338 | trans: Option<&Transaction>, 339 | result_account_balance: impl Into, 340 | count_out_msgs: usize, 341 | ) { 342 | if let Some(trans) = trans { 343 | assert_eq!( 344 | (trans.out_msgs.len().unwrap(), acc_after.balance().cloned().unwrap_or_default()), 345 | (count_out_msgs, CurrencyCollection::from_grams(result_account_balance.into())) 346 | ); 347 | } 348 | check_account_and_transaction_balances(acc_before, acc_after, msg, trans); 349 | } 350 | 351 | pub fn execute_with_block_version( 352 | config: BlockchainConfig, 353 | msg: &Message, 354 | acc: &mut Account, 355 | tr_lt: u64, 356 | block_version: u32 357 | ) -> Result { 358 | let lt = Arc::new(AtomicU64::new(tr_lt)); 359 | let executor = OrdinaryTransactionExecutor::new(config.clone()); 360 | let acc_before = acc.clone(); 361 | let trans= executor.execute_with_params(Some(&CommonMessage::Std(msg.clone())), acc, execute_params_block_version(lt, block_version)); 362 | if cfg!(feature = "cross_check") { 363 | cross_check::cross_check( 364 | &config, 365 | &acc_before, 366 | acc, 367 | Some(msg), 368 | trans.as_ref().ok(), 369 | &execute_params_block_version(Arc::new(AtomicU64::new(tr_lt)), block_version), 370 | 0 371 | ); 372 | } 373 | trans 374 | } 375 | 376 | pub fn execute_with_params(config: BlockchainConfig, msg: &Message, acc: &mut Account, tr_lt: u64) -> Result { 377 | execute_with_block_version(config, msg, acc, tr_lt, 0) 378 | } 379 | 380 | pub fn execute_with_params_c( 381 | config: BlockchainConfig, 382 | msg: &Message, 383 | acc: &mut Account, 384 | tr_lt: u64, 385 | result_account_balance: impl Into, 386 | count_out_msgs: usize 387 | ) -> Result { 388 | let acc_before = acc.clone(); 389 | let trans = execute_with_params(config, msg, acc, tr_lt); 390 | check_account_and_transaction(&acc_before, acc, msg, trans.as_ref().ok(), result_account_balance, count_out_msgs); 391 | trans 392 | } 393 | 394 | pub fn execute(msg: &Message, acc: &mut Account, tr_lt: u64) -> Result { 395 | let acc_before = acc.clone(); 396 | let trans = execute_with_params(BLOCKCHAIN_CONFIG.to_owned(), msg, acc, tr_lt); 397 | check_account_and_transaction_balances(&acc_before, acc, msg, None); 398 | trans 399 | } 400 | 401 | pub fn execute_c(msg: &Message, acc: &mut Account, tr_lt: u64, result_account_balance: impl Into, count_out_msgs: usize) -> Result { 402 | let acc_before = acc.clone(); 403 | let trans = execute_with_params(BLOCKCHAIN_CONFIG.to_owned(), msg, acc, tr_lt); 404 | check_account_and_transaction(&acc_before, acc, msg, trans.as_ref().ok(), result_account_balance, count_out_msgs); 405 | trans 406 | } 407 | 408 | pub fn execute_custom_transaction( 409 | start_balance: u64, 410 | code: &str, 411 | data: Cell, 412 | msg_balance: u64, 413 | bounce: bool, 414 | result_account_balance: impl Into, 415 | count_out_msgs: usize, 416 | ) -> (Account, Transaction) { 417 | let acc_id = AccountId::from([0x11; 32]); 418 | let code = compile_code_to_cell(code).unwrap(); 419 | let mut acc = create_test_account(start_balance, acc_id.clone(), code, data); 420 | let msg = create_int_msg( 421 | AccountId::from([0x33; 32]), 422 | acc_id, 423 | msg_balance, 424 | bounce, 425 | BLOCK_LT - 2 426 | ); 427 | 428 | let trans = execute_c(&msg, &mut acc, BLOCK_LT + 1, result_account_balance, count_out_msgs).unwrap(); 429 | (acc, trans) 430 | } 431 | 432 | #[allow(clippy::too_many_arguments)] 433 | pub fn execute_custom_transaction_with_extra_balance( 434 | start_balance: u64, 435 | start_extra_balance: u64, 436 | code: &str, 437 | data: Cell, 438 | msg_balance: u64, 439 | result_account_balance: u64, 440 | result_account_extra_balance: u64, 441 | count_out_msgs: usize, 442 | ) { 443 | let acc_id = AccountId::from([0x11; 32]); 444 | let code = compile_code_to_cell(code).unwrap(); 445 | let mut acc = create_test_account(start_balance, acc_id.clone(), code, data); 446 | let mut acc_balance = acc.balance().unwrap().clone(); 447 | acc_balance.other.set(&11111111u32, &VarUInteger32::from_two_u128(0, start_extra_balance.into()).unwrap()).unwrap(); 448 | acc.set_balance(acc_balance); 449 | let msg = create_int_msg( 450 | AccountId::from([0x33; 32]), 451 | acc_id, 452 | msg_balance, 453 | false, 454 | BLOCK_LT - 2 455 | ); 456 | 457 | let tr_lt = BLOCK_LT + 1; 458 | let trans = execute_with_params(BLOCKCHAIN_CONFIG.to_owned(), &msg, &mut acc, tr_lt).unwrap(); 459 | 460 | assert_eq!(trans.out_msgs.len().unwrap(), count_out_msgs); 461 | let mut answer = CurrencyCollection::with_grams(result_account_balance); 462 | answer.other.set(&11111111u32, &VarUInteger32::from_two_u128(0, result_account_extra_balance.into()).unwrap()).unwrap(); 463 | assert_eq!(acc.balance().unwrap_or(&CurrencyCollection::default()), &answer); 464 | } 465 | 466 | pub fn get_tr_descr(tr: &Transaction) -> TransactionDescrOrdinary { 467 | if let TransactionDescr::Ordinary(descr) = tr.read_description().unwrap() { 468 | descr 469 | } else { 470 | panic!("Not found description") 471 | } 472 | } 473 | 474 | pub fn replay_transaction_by_files(c: Option<(&mut criterion::Criterion, &str)>, account: &str, account_after: &str, transaction: &str, config: &str) { 475 | let mut account = Account::construct_from_file(account).unwrap(); 476 | let transaction = Transaction::construct_from_file(transaction).unwrap(); 477 | let message = transaction.read_in_msg().unwrap(); 478 | let account_after = Account::construct_from_file(account_after).unwrap(); 479 | let config = ConfigParams::construct_from_file(config).unwrap(); 480 | 481 | let mut left = account.balance().cloned().unwrap_or_default().grams; 482 | if let Some(msg) = message.as_ref() { 483 | let msg = msg.get_std().unwrap(); 484 | if let Some(value) = msg.get_value() { 485 | left.add(&value.grams).unwrap(); 486 | } 487 | // here can be fault in future in IHR will work 488 | if let Some(header) = msg.int_header() { 489 | left.add(&header.ihr_fee).unwrap(); // ihr is disabled, so we return ihr_fee 490 | } 491 | } 492 | 493 | let at = transaction.now(); 494 | let lt = transaction.logical_time(); 495 | let old_hash = account.serialize().unwrap().repr_hash(); 496 | 497 | if let Some((c, name)) = c { 498 | c.bench_function(name, |b| b.iter(|| { 499 | let m = message.as_ref().map(|m| m.get_std().unwrap()).map(|m|m.clone()); 500 | try_replay_transaction(&mut account.clone(), m, config.clone(), at, lt).unwrap(); 501 | })); 502 | } 503 | let m = message.as_ref().map(|m| m.get_std().unwrap()).map(|m|m.clone()); 504 | let mut our_transaction = try_replay_transaction(&mut account, m, config.clone(), at, lt).unwrap(); 505 | 506 | let mut right = account.balance().cloned().unwrap_or_default().grams; 507 | right.add(&our_transaction.total_fees().grams).unwrap(); 508 | our_transaction.iterate_out_msgs(|out_msg| { 509 | if let Some(header) = out_msg.get_std().unwrap().int_header() { 510 | right.add(&header.value().grams)?; 511 | right.add(header.fwd_fee())?; 512 | right.add(&header.ihr_fee)?; 513 | } 514 | Ok(true) 515 | }).unwrap(); 516 | assert_eq!(left, right); 517 | 518 | our_transaction.set_prev_trans_hash(transaction.prev_trans_hash().clone()); 519 | our_transaction.set_prev_trans_lt(transaction.prev_trans_lt()); 520 | // our_transaction.write_to_file("real_ever_boc/transaction_my.boc").unwrap(); 521 | 522 | let max = std::cmp::max(transaction.msg_count(), our_transaction.msg_count()); 523 | for i in 0..max { 524 | assert_eq!(our_transaction.get_out_msg(i).ok(), transaction.get_out_msg(i).ok()); 525 | } 526 | assert_eq!(our_transaction.read_description().unwrap(), transaction.read_description().unwrap()); 527 | let hash_update = transaction.read_state_update().unwrap(); 528 | assert_eq!(hash_update.old_hash, old_hash); 529 | 530 | our_transaction.write_state_update(&hash_update).unwrap(); 531 | our_transaction.set_prev_trans_hash(transaction.prev_trans_hash().clone()); 532 | our_transaction.set_prev_trans_lt(transaction.prev_trans_lt()); 533 | 534 | // determine correct mechanism of storage calculation 535 | if config.has_capability(GlobalCapabilities::CapFastStorageStat) { 536 | account.update_storage_stat_fast().unwrap(); 537 | } else { 538 | account.update_storage_stat().unwrap(); 539 | } 540 | let new_hash = account.serialize().unwrap().repr_hash(); 541 | if hash_update.new_hash == new_hash { 542 | assert_eq!(our_transaction, transaction); 543 | assert_eq!(account, account_after); 544 | return 545 | } 546 | // the new account can be unrechable in the blockchain - try to save new one 547 | // it will be correct because of hash cheking in state update of transaction 548 | // account.write_to_file("real_ever_boc/account_new.boc").unwrap(); 549 | assert_eq!(account, account_after); 550 | } 551 | 552 | pub fn try_replay_transaction( 553 | account: &mut Account, 554 | message: Option, 555 | config_params: ConfigParams, 556 | at: u32, 557 | tx_lt: u64 558 | ) -> Result { 559 | let config = BlockchainConfig::with_config(config_params).unwrap(); 560 | let executor: Box = if message.is_none() { 561 | let tt = account.get_tick_tock().unwrap(); 562 | let tt = match (tt.tick, tt.tock) { 563 | (true, false) => TransactionTickTock::Tick, 564 | (false, true) => TransactionTickTock::Tock, 565 | (_tick, _tock) => panic!("must be tick or tock") 566 | }; 567 | Box::new(TickTockTransactionExecutor::new(config.clone(), tt)) 568 | } else { 569 | Box::new(OrdinaryTransactionExecutor::new(config.clone())) 570 | }; 571 | 572 | let block_lt = tx_lt - tx_lt % 1_000_000; 573 | let lt = Arc::new(AtomicU64::new(tx_lt)); 574 | let params = ExecuteParams { 575 | block_unixtime: at, 576 | block_lt, 577 | last_tr_lt: lt, 578 | debug: true, 579 | ..ExecuteParams::default() 580 | }; 581 | let acc_before = account.clone(); 582 | let cmn_msg = message.map(make_common); 583 | let tx = executor.execute_with_params(cmn_msg.as_ref(), account, params); 584 | if cfg!(feature = "cross_check") { 585 | let params = ExecuteParams { 586 | block_unixtime: at, 587 | block_lt, 588 | last_tr_lt: Arc::new(AtomicU64::new(tx_lt)), 589 | debug: true, 590 | ..ExecuteParams::default() 591 | }; 592 | 593 | common::cross_check::cross_check(&config, &acc_before, account, cmn_msg.as_ref().map(|m| m.get_std().unwrap()), tx.as_ref().ok(), ¶ms, 0); 594 | } 595 | tx 596 | } 597 | -------------------------------------------------------------------------------- /src/tests/log_cfg.yml: -------------------------------------------------------------------------------- 1 | # Scan this file for changes every 30 seconds 2 | # https://docs.rs/log4rs/0.12.0/log4rs/encode/pattern/index.html 3 | refresh_rate: 30 seconds 4 | 5 | appenders: 6 | stdout: 7 | kind: console 8 | encoder: 9 | pattern: "{d(%s.%f)} {l} [{h({t})}] {I}: {m}{n}" 10 | 11 | stdout_ref: 12 | kind: console 13 | encoder: 14 | pattern: "{d(%H:%M:%S)}|{f:>45.45}:{L:<4.4}| {l:<5.5}: {m}{n}" 15 | 16 | logfile: 17 | kind: file 18 | path: "target/check/output.log" 19 | encoder: 20 | pattern: "{d(%H:%M:%S)}|{h({t:<20})}|{l:<5.5}: {m}{n}" 21 | 22 | tvm_logfile: 23 | kind: file 24 | path: "target/check/tvm.log" 25 | encoder: 26 | pattern: "{m}{n}" 27 | 28 | loggers: 29 | 30 | executor: 31 | level: debug 32 | appenders: 33 | - stdout_ref 34 | 35 | tvm: 36 | level: off 37 | appenders: 38 | - tvm_logfile 39 | -------------------------------------------------------------------------------- /src/tests/test_bounced_action_phase.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | #![allow(clippy::field_reassign_with_default)] 14 | 15 | mod common; 16 | 17 | use ever_block::{ 18 | Account, AccountStatus, AccStatusChange, AnycastInfo, CurrencyCollection, GetRepresentationHash, 19 | GlobalCapabilities, Grams, InternalMessageHeader, Message, MsgAddressInt, 20 | MsgAddressIntOrNone, OutAction, OutActions, SENDMSG_ORDINARY, Serializable, 21 | StorageUsedShort, TrActionPhase, Transaction, TransactionDescr, TransactionDescrOrdinary, 22 | TrBouncePhase, TrBouncePhaseNofunds, TrBouncePhaseOk, TrComputePhase, TrComputePhaseVm, 23 | TrCreditPhase, TrStoragePhase, UnixTime32 24 | }; 25 | use ever_block::TrBouncePhase::Nofunds; 26 | use ever_assembler::compile_code_to_cell; 27 | use ever_block::{AccountId, Cell, SliceData}; 28 | use common::*; 29 | use crate::ordinary_transaction::tests7::common::cross_check::disable_cross_check; 30 | 31 | #[test] 32 | fn test_action_phase_failed() { 33 | let start_balance = 100000000; 34 | let msg_income = 1_000_000_u64; 35 | let storage_fee = 6618; 36 | let total_balance = 87923382; 37 | let gas_used = 1307u32; 38 | let gas_fees = 13070000; 39 | let msg_fwd_fee = MSG_FWD_FEE; 40 | let msg_mine_fee = MSG_MINE_FEE; 41 | let total_fees = gas_fees + storage_fee; 42 | 43 | let code = compile_code_to_cell(" 44 | ACCEPT 45 | PUSHROOT 46 | CTOS 47 | LDREF 48 | PLDREF 49 | PUSHINT 0 50 | SENDRAWMSG 51 | PUSHINT 0 52 | SENDRAWMSG 53 | ").unwrap(); 54 | let acc_id = AccountId::from([0x11; 32]); 55 | let mut acc = create_test_account(start_balance, acc_id.clone(), code.clone(), create_two_messages_data()); 56 | acc.set_last_paid(BLOCK_UT - 100); 57 | let msg = create_int_msg( 58 | AccountId::from([0x33; 32]), 59 | AccountId::from([0x11; 32]), 60 | msg_income, 61 | true, 62 | PREV_BLOCK_LT 63 | ); 64 | 65 | let mut new_acc = create_test_account(total_balance, acc_id, code, create_two_messages_data()); 66 | new_acc.set_last_paid(BLOCK_UT); 67 | new_acc.set_last_tr_time(BLOCK_LT + 2); 68 | 69 | let tr_lt = BLOCK_LT + 1; 70 | let mut good_trans = Transaction::with_account_and_message(&new_acc, &msg, tr_lt).unwrap(); 71 | 72 | let trans = execute_c(&msg, &mut acc, tr_lt, new_acc.balance().unwrap().grams, 0).unwrap(); 73 | assert_eq!(acc, new_acc); 74 | 75 | let mut description = TransactionDescrOrdinary::default(); 76 | description.storage_ph = Some(TrStoragePhase { 77 | storage_fees_collected: storage_fee.into(), 78 | storage_fees_due: None, 79 | status_change: AccStatusChange::Unchanged 80 | }); 81 | description.credit_ph = Some(TrCreditPhase { 82 | due_fees_collected: None, 83 | credit: CurrencyCollection::with_grams(msg_income), 84 | }); 85 | let mut vm_phase = TrComputePhaseVm::default(); 86 | vm_phase.success = true; 87 | vm_phase.gas_used = gas_used.into(); 88 | vm_phase.gas_limit = 100u32.into(); 89 | vm_phase.gas_fees = gas_fees.into(); 90 | vm_phase.vm_steps = 10; 91 | description.compute_ph = TrComputePhase::Vm(vm_phase); 92 | 93 | let mut actions = OutActions::default(); 94 | let (msg1, msg2) = create_two_internal_messages(); 95 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg1)); 96 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg2)); 97 | 98 | let mut action_ph = TrActionPhase::default(); 99 | action_ph.success = false; 100 | action_ph.valid = true; 101 | action_ph.status_change = AccStatusChange::Unchanged; 102 | action_ph.tot_actions = 2; 103 | action_ph.msgs_created = 1; // incorrect value ignore, if success == false 104 | action_ph.add_fwd_fees((msg_fwd_fee).into()); // incorrect value ignore, if success == false 105 | action_ph.add_action_fees(msg_mine_fee.into()); // incorrect value ignore, if success == false 106 | action_ph.action_list_hash = actions.hash().unwrap(); 107 | action_ph.result_code = 37; 108 | action_ph.result_arg = Some(1); 109 | action_ph.no_funds = true; 110 | action_ph.tot_msg_size = StorageUsedShort::with_values_checked(1, 705).unwrap(); // incorrect value ignore, if success == false 111 | 112 | description.action = Some(action_ph); 113 | 114 | description.credit_first = false; 115 | description.bounce = None; 116 | description.aborted = true; 117 | description.destroyed = false; 118 | let description = TransactionDescr::Ordinary(description); 119 | 120 | good_trans.set_total_fees(CurrencyCollection::with_grams(total_fees)); 121 | good_trans.set_now(BLOCK_UT); 122 | 123 | good_trans.write_description(&description).unwrap(); 124 | 125 | assert_eq!(trans.read_description().unwrap(), description); 126 | assert_eq!(trans, good_trans); 127 | } 128 | 129 | #[test] 130 | fn test_action_phase_failed_with_cap_but_unsuccess_bounce() { 131 | let start_balance = 100000000; 132 | let msg_income = 1_000_000_u64; 133 | let storage_fee = 6618; 134 | let gas_used = 1307u32; 135 | let gas_fees = 13070000; 136 | let msg_fwd_fee = MSG_FWD_FEE; 137 | let msg_mine_fee = MSG_MINE_FEE; 138 | let total_fees = gas_fees + storage_fee; 139 | let total_balance = start_balance - storage_fee - gas_fees + msg_income; 140 | 141 | let code = compile_code_to_cell(" 142 | ACCEPT 143 | PUSHROOT 144 | CTOS 145 | LDREF 146 | PLDREF 147 | PUSHINT 0 148 | SENDRAWMSG 149 | PUSHINT 0 150 | SENDRAWMSG 151 | ").unwrap(); 152 | let acc_id = AccountId::from([0x11; 32]); 153 | let mut acc = create_test_account(start_balance, acc_id.clone(), code.clone(), create_two_messages_data()); 154 | acc.set_last_paid(BLOCK_UT - 100); 155 | let msg = create_int_msg( 156 | AccountId::from([0x33; 32]), 157 | AccountId::from([0x11; 32]), 158 | msg_income, 159 | true, 160 | PREV_BLOCK_LT 161 | ); 162 | 163 | let mut new_acc = create_test_account(total_balance, acc_id, code, create_two_messages_data()); 164 | new_acc.set_last_paid(BLOCK_UT); 165 | new_acc.set_last_tr_time(BLOCK_LT + 2); 166 | 167 | let tr_lt = BLOCK_LT + 1; 168 | let mut good_trans = Transaction::with_account_and_message(&new_acc, &msg, tr_lt).unwrap(); 169 | 170 | let config = custom_config(None, Some(GlobalCapabilities::CapBounceAfterFailedAction as u64)); 171 | let acc_before = acc.clone(); 172 | let trans = execute_with_params(config, &msg, &mut acc, tr_lt).unwrap(); 173 | check_account_and_transaction(&acc_before, &acc, &msg, Some(&trans), new_acc.balance().unwrap().grams, 0); 174 | assert_eq!(acc, new_acc); 175 | 176 | let mut description = TransactionDescrOrdinary::default(); 177 | description.storage_ph = Some(TrStoragePhase { 178 | storage_fees_collected: storage_fee.into(), 179 | storage_fees_due: None, 180 | status_change: AccStatusChange::Unchanged 181 | }); 182 | description.credit_ph = Some(TrCreditPhase { 183 | due_fees_collected: None, 184 | credit: CurrencyCollection::with_grams(msg_income), 185 | }); 186 | let mut vm_phase = TrComputePhaseVm::default(); 187 | vm_phase.success = true; 188 | vm_phase.gas_used = gas_used.into(); 189 | vm_phase.gas_limit = 100u32.into(); 190 | vm_phase.gas_fees = gas_fees.into(); 191 | vm_phase.vm_steps = 10; 192 | description.compute_ph = TrComputePhase::Vm(vm_phase); 193 | 194 | let mut actions = OutActions::default(); 195 | let (msg1, msg2) = create_two_internal_messages(); 196 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg1)); 197 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg2)); 198 | 199 | let mut action_ph = TrActionPhase::default(); 200 | action_ph.success = false; 201 | action_ph.valid = true; 202 | action_ph.status_change = AccStatusChange::Unchanged; 203 | action_ph.tot_actions = 2; 204 | action_ph.msgs_created = 1; // incorrect value ignore, if success == false 205 | action_ph.add_fwd_fees((msg_fwd_fee).into()); // incorrect value ignore, if success == false 206 | action_ph.add_action_fees(msg_mine_fee.into()); // incorrect value ignore, if success == false 207 | action_ph.action_list_hash = actions.hash().unwrap(); 208 | action_ph.result_code = 37; 209 | action_ph.result_arg = Some(1); 210 | action_ph.no_funds = true; 211 | action_ph.tot_msg_size = StorageUsedShort::with_values_checked(1, 705).unwrap(); // incorrect value ignore, if success == false 212 | 213 | description.action = Some(action_ph); 214 | 215 | description.credit_first = false; 216 | description.bounce = Some( 217 | Nofunds( 218 | TrBouncePhaseNofunds { 219 | msg_size: StorageUsedShort::with_values_checked(0, 0).unwrap(), 220 | req_fwd_fees: Grams::from(10000000) 221 | } 222 | ) 223 | ); 224 | description.aborted = true; 225 | description.destroyed = false; 226 | let description = TransactionDescr::Ordinary(description); 227 | 228 | good_trans.set_total_fees(CurrencyCollection::with_grams(total_fees)); 229 | good_trans.set_now(BLOCK_UT); 230 | 231 | good_trans.write_description(&description).unwrap(); 232 | 233 | assert_eq!(trans.read_description().unwrap(), description); 234 | assert_eq!(trans, good_trans); 235 | } 236 | 237 | #[test] 238 | fn test_action_phase_failed_with_cap() { 239 | let start_balance = 10_000_000; 240 | let msg_income = 150_000_000_u64; 241 | let storage_fee = 6606; 242 | let total_balance = start_balance - storage_fee; 243 | let gas_used = 1307u32; 244 | let gas_fees = 13_070_000; 245 | let msg_fwd_fee = MSG_FWD_FEE; 246 | let msg_mine_fee = MSG_MINE_FEE; 247 | let total_fees = 16409888; 248 | 249 | let code = compile_code_to_cell(" 250 | ACCEPT 251 | PUSHROOT 252 | CTOS 253 | LDREF 254 | PLDREF 255 | PUSHINT 0 256 | SENDRAWMSG 257 | PUSHINT 0 258 | SENDRAWMSG 259 | ").unwrap(); 260 | let acc_id = AccountId::from([0x11; 32]); 261 | let mut acc = create_test_account(start_balance, acc_id.clone(), code.clone(), create_two_messages_data()); 262 | acc.set_last_paid(BLOCK_UT - 100); 263 | let msg = create_int_msg( 264 | AccountId::from([0x33; 32]), 265 | AccountId::from([0x11; 32]), 266 | msg_income, 267 | true, 268 | PREV_BLOCK_LT 269 | ); 270 | 271 | let mut new_acc = create_test_account(total_balance, acc_id, code, create_two_messages_data()); 272 | new_acc.set_last_paid(BLOCK_UT); 273 | new_acc.set_last_tr_time(BLOCK_LT + 3); 274 | 275 | let tr_lt = BLOCK_LT + 1; 276 | let mut good_trans = Transaction::with_account_and_message(&new_acc, &msg, tr_lt).unwrap(); 277 | 278 | let config = custom_config(None, Some(GlobalCapabilities::CapBounceAfterFailedAction as u64)); 279 | let acc_before = acc.clone(); 280 | let trans = execute_with_params(config, &msg, &mut acc, tr_lt).unwrap(); 281 | check_account_and_transaction(&acc_before, &acc, &msg, Some(&trans), new_acc.balance().unwrap().grams, 1); 282 | assert_eq!(acc, new_acc); 283 | 284 | let mut description = TransactionDescrOrdinary::default(); 285 | description.storage_ph = Some(TrStoragePhase { 286 | storage_fees_collected: storage_fee.into(), 287 | storage_fees_due: None, 288 | status_change: AccStatusChange::Unchanged 289 | }); 290 | description.credit_ph = Some(TrCreditPhase { 291 | due_fees_collected: None, 292 | credit: CurrencyCollection::with_grams(msg_income), 293 | }); 294 | let mut vm_phase = TrComputePhaseVm::default(); 295 | vm_phase.success = true; 296 | vm_phase.gas_used = gas_used.into(); 297 | vm_phase.gas_limit = 15000u32.into(); 298 | vm_phase.gas_fees = gas_fees.into(); 299 | vm_phase.vm_steps = 10; 300 | description.compute_ph = TrComputePhase::Vm(vm_phase); 301 | 302 | let mut actions = OutActions::default(); 303 | let (msg1, msg2) = create_two_internal_messages(); 304 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg1)); 305 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg2)); 306 | 307 | let mut action_ph = TrActionPhase::default(); 308 | action_ph.success = false; 309 | action_ph.valid = true; 310 | action_ph.status_change = AccStatusChange::Unchanged; 311 | action_ph.tot_actions = 2; 312 | action_ph.msgs_created = 1; // incorrect value ignore, if success == false 313 | action_ph.add_fwd_fees((msg_fwd_fee).into()); // incorrect value ignore, if success == false 314 | action_ph.add_action_fees(msg_mine_fee.into()); // incorrect value ignore, if success == false 315 | action_ph.action_list_hash = actions.hash().unwrap(); 316 | action_ph.result_code = 37; 317 | action_ph.result_arg = Some(1); 318 | action_ph.no_funds = true; 319 | action_ph.tot_msg_size = StorageUsedShort::with_values_checked(1, 705).unwrap(); // incorrect value ignore, if success == false 320 | 321 | description.action = Some(action_ph); 322 | 323 | description.credit_first = false; 324 | description.bounce = Some( 325 | TrBouncePhase::Ok(TrBouncePhaseOk { 326 | msg_size: StorageUsedShort::with_values_checked(0, 0).unwrap(), 327 | msg_fees: Grams::from(3333282), 328 | fwd_fees: Grams::from(6666718), 329 | }), 330 | ); 331 | description.aborted = true; 332 | description.destroyed = false; 333 | let description = TransactionDescr::Ordinary(description); 334 | 335 | good_trans.set_total_fees(CurrencyCollection::with_grams(total_fees)); 336 | good_trans.set_now(BLOCK_UT); 337 | 338 | let mut message = Message::with_int_header(InternalMessageHeader{ 339 | ihr_disabled: true, 340 | bounce: false, 341 | bounced: true, 342 | src: MsgAddressIntOrNone::Some(MsgAddressInt::with_standart(None, -1, AccountId::from([0x11; 32])).unwrap()), 343 | dst: MsgAddressInt::with_standart(None, -1, AccountId::from([0x33; 32])).unwrap(), 344 | value: CurrencyCollection::with_grams(126930000), 345 | ihr_fee: Default::default(), 346 | fwd_fee: Grams::from(6666718), 347 | created_lt: 2000000002, 348 | created_at: BLOCK_UT.into() 349 | }); 350 | let builder = (-1i32).write_to_new_cell().unwrap(); 351 | message.set_body(SliceData::load_builder(builder).unwrap()); 352 | good_trans.add_out_message(&make_common(message)).unwrap(); 353 | 354 | good_trans.write_description(&description).unwrap(); 355 | 356 | trans.out_msgs.scan_diff(&good_trans.out_msgs, |key: ever_block::U15, msg1, msg2| { 357 | assert_eq!(msg1, msg2, "for key {}", key.0); 358 | Ok(true) 359 | }).unwrap(); 360 | assert_eq!(trans.read_description().unwrap(), description); 361 | assert_eq!(trans, good_trans); 362 | } 363 | 364 | #[test] 365 | fn test_message_with_anycast_output_address_bounced_action() { 366 | let start_balance = 300_000_000_000u64; 367 | let msg_income = 100_000_000_000u64; 368 | 369 | let dst = MsgAddressInt::with_standart( 370 | Some(AnycastInfo::with_rewrite_pfx(SliceData::new(vec![0x22; 3])).unwrap()), 371 | 0, 372 | AccountId::from([0x11; 32]) 373 | ).unwrap(); 374 | let mut hdr = InternalMessageHeader::with_addresses( 375 | MsgAddressInt::with_standart(None, 0, AccountId::from([0x11; 32])).unwrap(), 376 | dst, 377 | CurrencyCollection::with_grams(msg_income), 378 | ); 379 | hdr.bounce = false; 380 | hdr.ihr_disabled = true; 381 | hdr.created_lt = PREV_BLOCK_LT; 382 | hdr.created_at = UnixTime32::default(); 383 | let out_msg = Message::with_int_header(hdr); 384 | 385 | let data = out_msg.serialize().unwrap(); 386 | let code = compile_code_to_cell(" 387 | PUSHROOT 388 | PUSHINT 0 389 | SENDRAWMSG 390 | ").unwrap(); 391 | let acc_id = AccountId::from([0x11; 32]); 392 | let mut acc = create_test_account_workchain(start_balance, 0, acc_id.clone(), code, data); 393 | acc.set_last_paid(BLOCK_UT - 100); 394 | let msg = create_int_msg_workchain( 395 | 0, 396 | AccountId::from([0x33; 32]), 397 | AccountId::from([0x11; 32]), 398 | msg_income, 399 | true, 400 | PREV_BLOCK_LT 401 | ); 402 | 403 | disable_cross_check(); 404 | let tr_lt = BLOCK_LT + 1; 405 | let config = custom_config(None, Some(GlobalCapabilities::CapBounceAfterFailedAction as u64)); 406 | let trans = execute_with_params(config, &msg, &mut acc, tr_lt).unwrap(); 407 | 408 | let mut new_acc = create_test_account_workchain(299999999996u64, 0, acc_id, acc.get_code().unwrap(), acc.get_data().unwrap()); 409 | new_acc.set_last_paid(BLOCK_UT); 410 | new_acc.set_last_tr_time(BLOCK_LT + 3); 411 | assert_eq!(acc, new_acc); 412 | 413 | let mut description = TransactionDescrOrdinary::default(); 414 | description.storage_ph = Some(TrStoragePhase { 415 | storage_fees_collected: 4u64.into(), 416 | storage_fees_due: None, 417 | status_change: AccStatusChange::Unchanged 418 | }); 419 | description.credit_ph = Some(TrCreditPhase { 420 | due_fees_collected: None, 421 | credit: CurrencyCollection::with_grams(msg_income), 422 | }); 423 | let mut vm_phase = TrComputePhaseVm::default(); 424 | vm_phase.success = true; 425 | vm_phase.gas_used = 575u32.into(); 426 | vm_phase.gas_limit = 1000000u32.into(); 427 | vm_phase.gas_fees = 575000u64.into(); 428 | vm_phase.vm_steps = 4; 429 | description.compute_ph = TrComputePhase::Vm(vm_phase); 430 | 431 | let mut action_ph = TrActionPhase::default(); 432 | action_ph.success = false; 433 | action_ph.valid = true; 434 | action_ph.status_change = AccStatusChange::Unchanged; 435 | action_ph.tot_actions = 1; 436 | action_ph.msgs_created = 0; // incorrect value ignore, if success == false 437 | action_ph.action_list_hash = "0x221ff324345e3bb2b1f6d6277d3613c9a17048d4d3b6b34f6103c93cf4b427d1".parse().unwrap(); 438 | action_ph.result_code = 50; 439 | action_ph.result_arg = None; 440 | action_ph.no_funds = false; 441 | action_ph.tot_msg_size = StorageUsedShort::default(); // incorrect value ignore, if success == false 442 | 443 | description.action = Some(action_ph); 444 | 445 | description.credit_first = false; 446 | description.bounce = Some( 447 | TrBouncePhase::Ok(TrBouncePhaseOk { 448 | msg_size: StorageUsedShort::with_values_checked(0, 0).unwrap(), 449 | msg_fees: Grams::from(333328), 450 | fwd_fees: Grams::from(666672), 451 | }), 452 | ); 453 | description.aborted = true; 454 | description.destroyed = false; 455 | let description = TransactionDescr::Ordinary(description); 456 | 457 | let mut good_trans = Transaction::with_account_and_message(&new_acc, &msg, tr_lt).unwrap(); 458 | 459 | let mut message = Message::with_int_header(InternalMessageHeader{ 460 | ihr_disabled: true, 461 | bounce: false, 462 | bounced: true, 463 | src: MsgAddressIntOrNone::Some(MsgAddressInt::with_standart(None, 0, AccountId::from([0x11; 32])).unwrap()), 464 | dst: MsgAddressInt::with_standart(None, 0, AccountId::from([0x33; 32])).unwrap(), 465 | value: CurrencyCollection::with_grams(99998425000), 466 | ihr_fee: Default::default(), 467 | fwd_fee: Grams::from(666672), 468 | created_lt: 2000000002, 469 | created_at: BLOCK_UT.into() 470 | }); 471 | let builder = (-1i32).write_to_new_cell().unwrap(); 472 | message.set_body(SliceData::load_builder(builder).unwrap()); 473 | good_trans.add_out_message(&make_common(message)).unwrap(); 474 | 475 | good_trans.set_total_fees(CurrencyCollection::with_grams(908332)); 476 | good_trans.set_now(BLOCK_UT); 477 | 478 | good_trans.write_description(&description).unwrap(); 479 | 480 | trans.out_msgs.scan_diff(&good_trans.out_msgs, |key: ever_block::U15, msg1, msg2| { 481 | assert_eq!(msg1, msg2, "for key {}", key.0); 482 | Ok(true) 483 | }).unwrap(); 484 | assert_eq!(trans.read_description().unwrap(), description); 485 | assert_eq!(trans, good_trans); 486 | } 487 | 488 | #[allow(clippy::too_many_arguments)] 489 | pub fn execute_custom_transaction_with_caps( 490 | capabilities: u64, 491 | start_balance: u64, 492 | code: &str, 493 | data: Cell, 494 | msg_balance: u64, 495 | bounce: bool, 496 | result_account_balance: impl Into, 497 | count_out_msgs: usize, 498 | ) -> (Account, Transaction) { 499 | let acc_id = AccountId::from([0x11; 32]); 500 | let code = compile_code_to_cell(code).unwrap(); 501 | let mut acc = create_test_account(start_balance, acc_id.clone(), code, data); 502 | let msg = create_int_msg( 503 | AccountId::from([0x33; 32]), 504 | acc_id, 505 | msg_balance, 506 | bounce, 507 | BLOCK_LT - 2 508 | ); 509 | 510 | let config = custom_config(None, Some(capabilities)); 511 | 512 | let acc_before = acc.clone(); 513 | let trans = execute_with_params(config, &msg, &mut acc, BLOCK_LT + 1); 514 | check_account_and_transaction(&acc_before, &acc, &msg, trans.as_ref().ok(), result_account_balance, count_out_msgs); 515 | (acc, trans.unwrap()) 516 | } 517 | 518 | #[test] 519 | fn test_send_bouncable_messages_to_account_without_enough_money_to_pay_storage() { 520 | // account balance is not enough to pay for storage fee 521 | 522 | // i. Bad code without accept 523 | let code = " 524 | PUSHCTR C4 525 | PUSHINT 0 526 | SENDRAWMSG 527 | "; 528 | let out_msg = create_int_msg( 529 | AccountId::from([0x11; 32]), 530 | AccountId::from([0x33; 32]), 531 | 11_000_000, 532 | false, 533 | BLOCK_LT + 1 534 | ); 535 | let data = out_msg.serialize().unwrap(); 536 | let due = 1000u64; 537 | let start_balance = 154258689 - due; 538 | // message value is enough to pay for storage and compute, but not enough to send value 539 | let (acc, tr) = execute_custom_transaction_with_caps( 540 | GlobalCapabilities::CapBounceAfterFailedAction as u64, 541 | start_balance, 542 | code, 543 | data, 544 | 10_000_000, 545 | true, 546 | 4_250_000 - due, 547 | 0 548 | ); 549 | assert!(matches!(get_tr_descr(&tr).compute_ph, TrComputePhase::Vm(_))); 550 | assert!(get_tr_descr(&tr).action.unwrap().no_funds); 551 | assert!(matches!(get_tr_descr(&tr).bounce, Some(TrBouncePhase::Nofunds(_)))); 552 | assert_eq!(acc.status(), AccountStatus::AccStateActive); 553 | 554 | // vi. good code with accept and with sendmsg 555 | let code = " 556 | ACCEPT 557 | PUSHCTR C4 558 | PUSHINT 0 559 | SENDRAWMSG 560 | "; 561 | let out_msg = create_int_msg( 562 | AccountId::from([0x11; 32]), 563 | AccountId::from([0x33; 32]), 564 | 11_000_000, 565 | false, 566 | BLOCK_LT + 1 567 | ); 568 | let data = out_msg.serialize().unwrap(); 569 | let due = 1063853u64; 570 | let start_balance = 154258689u64; 571 | // message value is enough to pay for storage and compute, but not enough to send value 572 | let (acc, tr) = execute_custom_transaction_with_caps( 573 | GlobalCapabilities::CapBounceAfterFailedAction as u64, 574 | start_balance, 575 | code, 576 | data, 577 | 10_000_000, 578 | true, 579 | 3_990_000 - due, 580 | 0 581 | ); 582 | assert!(matches!(get_tr_descr(&tr).compute_ph, TrComputePhase::Vm(_))); 583 | assert!(get_tr_descr(&tr).action.unwrap().no_funds); 584 | assert!(matches!(get_tr_descr(&tr).bounce, Some(TrBouncePhase::Nofunds(_)))); 585 | assert_eq!(acc.status(), AccountStatus::AccStateActive); 586 | } 587 | 588 | fn execute_sendrawmsg_message(mode: u8, send_value: u64, result_balance: u64, count_out_msgs: usize) { 589 | let code = format!(" 590 | ACCEPT 591 | PUSHCTR C4 592 | PUSHINT {} 593 | SENDRAWMSG 594 | ", mode); 595 | let out_msg = create_int_msg( 596 | AccountId::from([0x11; 32]), 597 | AccountId::from([0x33; 32]), 598 | send_value, 599 | false, 600 | BLOCK_LT 601 | ); 602 | let data = out_msg.serialize().unwrap(); 603 | let start_balance = 200_000_000; 604 | execute_custom_transaction_with_caps( 605 | GlobalCapabilities::CapBounceAfterFailedAction as u64, 606 | start_balance, 607 | &code, 608 | data, 609 | 20_000_000, 610 | true, 611 | result_balance, 612 | count_out_msgs 613 | ); 614 | } 615 | 616 | #[test] 617 | fn test_sendrawmsg_message() { 618 | execute_sendrawmsg_message(0, 0, 46_273_237, 1); 619 | } 620 | -------------------------------------------------------------------------------- /src/tests/test_ordinary_rawreserve.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | #![allow(clippy::field_reassign_with_default)] 14 | 15 | use super::*; 16 | 17 | mod common; 18 | use common::*; 19 | 20 | use pretty_assertions::assert_eq; 21 | use ever_block::{ 22 | messages::CommonMsgInfo, 23 | out_actions::{OutAction, OutActions, RESERVE_EXACTLY, SENDMSG_ALL_BALANCE, SENDMSG_ORDINARY}, 24 | transactions::{ 25 | AccStatusChange, TrActionPhase, TrComputePhase, TrComputePhaseVm, TrCreditPhase, 26 | TrStoragePhase, Transaction, TransactionDescr, 27 | }, 28 | CurrencyCollection, GetRepresentationHash, Grams, Serializable, StorageUsedShort, 29 | }; 30 | use ever_assembler::compile_code_to_cell; 31 | use ever_block::AccountId; 32 | 33 | #[test] 34 | fn test_trexecutor_active_acc_with_rawreserve_and_sendmsg() { 35 | let used = 1864u32; //gas units 36 | let storage_fees = 293158000; 37 | let msg_mine_fee = MSG_MINE_FEE; 38 | let msg_fwd_fee = MSG_FWD_FEE; 39 | let msg_remain_fee = msg_fwd_fee - msg_mine_fee; 40 | let gas_fees = used as u64 * 10000; 41 | let reserve = 133000000; 42 | 43 | let acc_id = AccountId::from([0x11; 32]); 44 | let start_balance = 310000000u64; 45 | let msg_income = 1230000000u64; 46 | let code = compile_code_to_cell(&format!(" 47 | PUSHINT {} 48 | PUSHINT 0 49 | RAWRESERVE 50 | PUSHROOT 51 | CTOS 52 | LDREF 53 | PLDREF 54 | PUSHINT 0 55 | SENDRAWMSG 56 | PUSHINT 128 57 | SENDRAWMSG 58 | ", reserve)).unwrap(); 59 | let mut acc = create_test_account(start_balance, acc_id.clone(), code.clone(), create_two_messages_data()); 60 | let end_balance = reserve; 61 | let mut new_acc = create_test_account(end_balance, acc_id, code, create_two_messages_data()); 62 | let msg = create_int_msg( 63 | AccountId::from([0x22; 32]), 64 | AccountId::from([0x11; 32]), 65 | msg_income, 66 | true, 67 | BLOCK_LT - 1_000_000 + 1, 68 | ); 69 | let tr_lt = BLOCK_LT + 1; 70 | new_acc.set_last_tr_time(tr_lt + 3); 71 | new_acc.set_last_paid(BLOCK_UT); 72 | let trans = execute_c(&msg, &mut acc, tr_lt, new_acc.balance().unwrap().grams, 2).unwrap(); 73 | acc.update_storage_stat().unwrap(); 74 | 75 | assert_eq!(acc, new_acc); 76 | let mut good_trans = Transaction::with_account_and_message(&acc, &msg, tr_lt).unwrap(); 77 | good_trans.set_now(BLOCK_UT); 78 | 79 | let (mut msg1, mut msg2) = create_two_internal_messages(); 80 | let mut actions = OutActions::default(); 81 | actions.push_back(OutAction::new_reserve(RESERVE_EXACTLY, CurrencyCollection::with_grams(reserve))); 82 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg1.clone())); 83 | actions.push_back(OutAction::new_send(SENDMSG_ALL_BALANCE, msg2.clone())); 84 | if let CommonMsgInfo::IntMsgInfo(int_header) = msg1.header_mut() { 85 | if let CommonMsgInfo::IntMsgInfo(int_header2) = msg2.header_mut() { 86 | int_header.value.grams = Grams::from(MSG1_BALANCE - msg_fwd_fee); 87 | int_header2.value.grams = Grams::from(start_balance + msg_income - reserve - MSG1_BALANCE - msg_fwd_fee - gas_fees - storage_fees); 88 | int_header.fwd_fee = msg_remain_fee.into(); 89 | int_header2.fwd_fee = msg_remain_fee.into(); 90 | int_header.created_at = BLOCK_UT.into(); 91 | int_header2.created_at = BLOCK_UT.into(); 92 | } 93 | } 94 | 95 | good_trans.add_out_message(&make_common(msg1.clone())).unwrap(); 96 | good_trans.add_out_message(&make_common(msg2.clone())).unwrap(); 97 | good_trans.set_total_fees((storage_fees + gas_fees + msg_mine_fee * 2).into()); 98 | 99 | let mut description = TransactionDescrOrdinary::default(); 100 | description.storage_ph = Some(TrStoragePhase::with_params(storage_fees.into(), None, AccStatusChange::Unchanged)); 101 | description.credit_ph = Some(TrCreditPhase { 102 | due_fees_collected: None, 103 | credit: CurrencyCollection::with_grams(msg_income), 104 | }); 105 | let mut vm_phase = TrComputePhaseVm::default(); 106 | vm_phase.success = true; 107 | vm_phase.msg_state_used = false; 108 | vm_phase.account_activated = false; 109 | vm_phase.gas_used = used.into(); 110 | vm_phase.gas_limit = 123000u32.into(); 111 | vm_phase.gas_credit = None; 112 | vm_phase.gas_fees = gas_fees.into(); 113 | vm_phase.vm_steps = 12; 114 | description.compute_ph = TrComputePhase::Vm(vm_phase); 115 | 116 | let mut action_ph = TrActionPhase::default(); 117 | action_ph.success = true; 118 | action_ph.valid = true; 119 | action_ph.status_change = AccStatusChange::Unchanged; 120 | action_ph.tot_actions = 3; 121 | action_ph.spec_actions = 1; 122 | action_ph.msgs_created = 2; 123 | action_ph.add_fwd_fees((2 * msg_fwd_fee).into()); 124 | action_ph.add_action_fees((2 * msg_mine_fee).into()); 125 | action_ph.action_list_hash = actions.hash().unwrap(); 126 | action_ph.tot_msg_size = StorageUsedShort::calculate_for_struct(&msg1).unwrap(); 127 | action_ph.tot_msg_size.append(&msg2.serialize().unwrap()); 128 | 129 | description.action = Some(action_ph); 130 | description.credit_first = false; 131 | description.bounce = None; 132 | description.aborted = false; 133 | description.destroyed = false; 134 | good_trans.write_description(&TransactionDescr::Ordinary(description)).unwrap(); 135 | 136 | assert_eq!( 137 | trans.read_description().unwrap().compute_phase_ref().unwrap().clone().get_vmphase_mut().unwrap().gas_used, 138 | good_trans.read_description().unwrap().compute_phase_ref().unwrap().clone().get_vmphase_mut().unwrap().gas_used); 139 | 140 | assert_eq!(trans.read_description().unwrap(), good_trans.read_description().unwrap()); 141 | 142 | trans.out_msgs.scan_diff(&good_trans.out_msgs, |key: ever_block::U15, msg1, msg2| { 143 | assert_eq!(msg1, msg2, "for key {}", key.0); 144 | Ok(true) 145 | }).unwrap(); 146 | assert_eq!(trans, good_trans); 147 | } 148 | 149 | struct MsgFlag { 150 | f: u64 151 | } 152 | 153 | fn execute_rawreserve_transaction(start_balance: u64, reserve: u64, r_type: u64, msg_flag: MsgFlag, result_account_balance: u64, count_out_msgs: usize) { 154 | let code = &format!(" 155 | PUSHINT {} 156 | PUSHINT {} 157 | RAWRESERVE 158 | PUSHROOT 159 | CTOS 160 | LDREF 161 | PLDREF 162 | PUSHINT 0 163 | SENDRAWMSG 164 | PUSHINT {} 165 | SENDRAWMSG 166 | ", reserve, r_type, msg_flag.f); 167 | let data = create_two_messages_data(); 168 | execute_custom_transaction(start_balance, code, data, 41_000_000, false, result_account_balance, count_out_msgs); 169 | } 170 | 171 | #[test] 172 | fn test_send_rawreserve_messages() { 173 | execute_rawreserve_transaction(1_502_586_890, 1_080_012_743, 0, MsgFlag{f: 0}, 1_083_012_743, 2); 174 | execute_rawreserve_transaction(1_502_586_890, 1_083_012_743, 0, MsgFlag{f: 0}, 1_083_012_743, 2); 175 | execute_rawreserve_transaction(1_502_586_890, 1_084_012_743, 0, MsgFlag{f: 0}, 1_233_012_743, 0); 176 | execute_rawreserve_transaction(1_502_586_890, 1_000_000_000, 0, MsgFlag{f: 128}, 1_000_000_000, 2); 177 | execute_rawreserve_transaction(1_502_586_890, 1_084_012_743, 0, MsgFlag{f: 128}, 1_084_012_743, 2); 178 | execute_rawreserve_transaction(1_502_586_890, 1_000_000_000, 0, MsgFlag{f: 64}, 1_059_960_816, 2); 179 | execute_rawreserve_transaction(1_502_586_890, 1_104_012_743, 0, MsgFlag{f: 64}, 1_232_400_816, 0); 180 | 181 | execute_rawreserve_transaction(1_502_586_890, 1_084_012_743, 0, MsgFlag{f: 2}, 1_183_012_743, 1); 182 | execute_rawreserve_transaction(1_502_586_890, 1_104_012_743, 0, MsgFlag{f: 64+2}, 1_182_400_816, 1); 183 | 184 | execute_rawreserve_transaction(530_000_000 - 40_000_000, 200_000_000, 1, MsgFlag{f: 0}, 70_425_853, 2); 185 | execute_rawreserve_transaction(530_000_000 - 40_000_000, 200_000_000, 0, MsgFlag{f: 0}, 220_425_853, 0); 186 | 187 | execute_rawreserve_transaction(630_000_000 - 40_000_000, 100_000_000, 2, MsgFlag{f: 0}, 170_425_853, 2); 188 | execute_rawreserve_transaction(630_000_000 - 40_000_000, 400_000_000, 2, MsgFlag{f: 0}, 320_425_853, 0); 189 | execute_rawreserve_transaction(630_000_000 - 40_000_000, 100_000_000, 0, MsgFlag{f: 0}, 170_425_853, 2); 190 | 191 | execute_rawreserve_transaction(1_502_586_890, 3_084_012_743, 13, MsgFlag{f: 0}, 1_232_400_816, 0); 192 | 193 | execute_rawreserve_transaction(630_000_000, 300_000_000, 12, MsgFlag{f: 0}, 209_813_926, 2); 194 | execute_rawreserve_transaction(630_000_000, 300_000_000, 0, MsgFlag{f: 0}, 360_425_853, 0); 195 | } 196 | -------------------------------------------------------------------------------- /src/tests/test_random_gen.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use super::*; 15 | 16 | mod common; 17 | use common::*; 18 | use std::sync::{atomic::AtomicU64, Arc}; 19 | use rand::prelude::Rng; 20 | use rand::prelude::StdRng; 21 | use ever_block::{ 22 | accounts::Account, 23 | CurrencyCollection, ExternalInboundMessageHeader, Grams, 24 | InternalMessageHeader, MsgAddressInt, Serializable, StateInit, UnixTime32, 25 | }; 26 | use ever_assembler::compile_code_to_cell; 27 | use ever_block::{AccountId, SliceData, UInt256, BuilderData}; 28 | use std::panic; 29 | 30 | fn gen_bool(rng: &mut StdRng) -> bool { 31 | rng.gen_bool(0.5) 32 | } 33 | 34 | fn gen_simple_int(rng: &mut StdRng, n: u64) -> u64 { 35 | rng.gen_range(0..n) 36 | } 37 | 38 | fn gen_int(rng: &mut StdRng, max_digits: u8) -> u64 { 39 | assert!(max_digits <= 19); 40 | let count_digits = rng.gen_range(0..max_digits + 1) as u32; 41 | if count_digits == max_digits as u32 { 42 | 0 43 | } else { 44 | rng.gen_range(u64::pow(10, count_digits)..10 * u64::pow(10, count_digits)) 45 | } 46 | } 47 | 48 | fn gen_states(state_num: u8, accept: bool, ihr_disabled: bool, src: &AccountId, w_id: i8) -> StateInit { 49 | match state_num { 50 | 0 => StateInit::default(), 51 | 1 => { 52 | let code = compile_code_to_cell(&(if accept { "ACCEPT " } else { "" }.to_string() + " 53 | NOP 54 | ")).unwrap(); 55 | let mut state_correct = StateInit::default(); 56 | state_correct.set_code(code); 57 | state_correct 58 | }, 59 | 2 => { 60 | let code = compile_code_to_cell(&(if accept { "ACCEPT " } else { "" }.to_string() + " 61 | PUSHROOT 62 | SENDRAWMSG 63 | ")).unwrap(); 64 | let mut state_incorrect = StateInit::default(); 65 | state_incorrect.set_code(code); 66 | state_incorrect 67 | }, 68 | 3 => { 69 | let code = compile_code_to_cell(&(if accept { "ACCEPT " } else { "" }.to_string() + " 70 | PUSHROOT 71 | PUSHINT 128 72 | SENDRAWMSG 73 | ")).unwrap(); 74 | let mut state_send_msg = StateInit::default(); 75 | state_send_msg.set_code(code); 76 | let mut out_msg = create_int_msg(src.clone(), [0; 32].into(), 100, false, 0); 77 | if let CommonMsgInfo::IntMsgInfo(header) = out_msg.header_mut() { 78 | header.ihr_disabled = ihr_disabled; 79 | } else { 80 | unreachable!() 81 | } 82 | out_msg.set_src_address(MsgAddressInt::with_standart(None, w_id, src.clone()).unwrap()); 83 | state_send_msg.set_data(out_msg.serialize().unwrap()); 84 | state_send_msg 85 | }, 86 | 4 => { 87 | let code = compile_code_to_cell(&(if accept { "ACCEPT " } else { "" }.to_string() + " 88 | PUSHROOT 89 | PUSHINT 0 90 | SENDRAWMSG 91 | PUSHINT 1 92 | SENDRAWMSG 93 | ")).unwrap(); 94 | let mut state_send_two_msg = StateInit::default(); 95 | state_send_two_msg.set_code(code); 96 | state_send_two_msg.set_data(create_two_messages_data_2(src.clone(), w_id)); 97 | state_send_two_msg 98 | }, 99 | 5 => { 100 | let code = compile_code_to_cell(&(if accept { "ACCEPT " } else { "" }.to_string() + " 101 | PUSHINT 1000000000 102 | PUSHINT 0 103 | RAWRESERVE 104 | PUSHROOT 105 | CTOS 106 | LDREF 107 | PLDREF 108 | PUSHINT 0 109 | SENDRAWMSG 110 | PUSHINT 1 111 | SENDRAWMSG 112 | ")).unwrap(); 113 | let mut state_rawreserve = StateInit::default(); 114 | state_rawreserve.set_code(code); 115 | state_rawreserve.set_data(create_two_messages_data_2(src.clone(), w_id)); 116 | state_rawreserve 117 | } 118 | _ => panic!("Incorrect value") 119 | } 120 | } 121 | 122 | fn gen_state(rng: &mut StdRng, src: &AccountId, w_id: i8) -> StateInit { 123 | gen_states(gen_simple_int(rng, 6) as u8, gen_bool(rng), gen_bool(rng), src, w_id) 124 | } 125 | 126 | #[test] 127 | fn test_rand_1() { 128 | let mut rng = rand::SeedableRng::seed_from_u64(234235253453); 129 | 130 | let ordinary_acc_id = AccountId::from([0x11; 32]); 131 | let special_acc_id = AccountId::from([0x66u8; 32]); 132 | 133 | let blockchain_config = BLOCKCHAIN_CONFIG.to_owned(); 134 | assert!( 135 | blockchain_config.is_special_account( 136 | &MsgAddressInt::with_standart(None, -1, special_acc_id.clone()).unwrap() 137 | ).unwrap() 138 | ); 139 | 140 | for _iteration in 0..60_000 { 141 | let is_special = gen_bool(&mut rng); 142 | let acc_id = if !is_special {&ordinary_acc_id} else {&special_acc_id}; 143 | let workchain_id = -(gen_simple_int(&mut rng, 2) as i8); 144 | 145 | let mut state_init: Option = None; 146 | let mut acc = match gen_simple_int(&mut rng, 4) { 147 | 0 => { 148 | state_init = Some(gen_state(&mut rng, acc_id, workchain_id)); 149 | Account::active_by_init_code_hash( 150 | MsgAddressInt::with_standart(None, workchain_id, acc_id.clone()).unwrap(), 151 | CurrencyCollection::with_grams(gen_int(&mut rng, 19)), 152 | BLOCK_UT - std::cmp::min(gen_int(&mut rng, 11) as u32, BLOCK_UT), 153 | state_init.clone().unwrap(), 154 | gen_bool(&mut rng) 155 | ).unwrap() 156 | } 157 | 1 => { 158 | Account::default() 159 | } 160 | 2 => { 161 | Account::uninit( 162 | MsgAddressInt::with_standart(None, workchain_id, acc_id.clone()).unwrap(), 163 | BLOCK_LT, 164 | BLOCK_UT - std::cmp::min(gen_int(&mut rng, 11) as u32, BLOCK_UT), 165 | CurrencyCollection::with_grams(gen_int(&mut rng, 19)) 166 | ) 167 | } 168 | 3 => { 169 | state_init = Some(gen_state(&mut rng, acc_id, workchain_id)); 170 | let state_hash = state_init.clone().unwrap().serialize().unwrap().repr_hash(); 171 | let due = Grams::from(gen_int(&mut rng, 19)); 172 | Account::frozen( 173 | MsgAddressInt::with_standart(None, workchain_id, acc_id.clone()).unwrap(), 174 | BLOCK_LT, 175 | BLOCK_UT - std::cmp::min(gen_int(&mut rng, 11) as u32, BLOCK_UT), 176 | if gen_bool(&mut rng) {state_hash} else {UInt256::default()}, 177 | if due.is_zero() {None} else {Some(due)}, 178 | CurrencyCollection::with_grams(gen_int(&mut rng, 19)) 179 | ) 180 | } 181 | _ => panic!("Incorrect value") 182 | }; 183 | 184 | let mut msg = if gen_bool(&mut rng) { 185 | let dst = MsgAddressInt::with_standart(None, workchain_id, acc_id.clone()).unwrap(); 186 | let hdr = ExternalInboundMessageHeader::new(Default::default(), dst); 187 | Message::with_ext_in_header_and_body(hdr, SliceData::default()) 188 | } else { 189 | let workchain_id_sender = -(gen_simple_int(&mut rng, 2) as i8); 190 | let mut hdr = InternalMessageHeader::with_addresses( 191 | MsgAddressInt::with_standart(None, workchain_id_sender, AccountId::from([0x33; 32])).unwrap(), 192 | MsgAddressInt::with_standart(None, workchain_id, acc_id.clone()).unwrap(), 193 | CurrencyCollection::with_grams(gen_int(&mut rng, 19)), 194 | ); 195 | hdr.bounce = gen_bool(&mut rng); 196 | hdr.ihr_disabled = gen_bool(&mut rng); 197 | hdr.created_lt = PREV_BLOCK_LT; 198 | hdr.created_at = UnixTime32::default(); 199 | hdr.fwd_fee = Grams::from(gen_int(&mut rng, 10)); 200 | hdr.ihr_fee = Grams::from(gen_int(&mut rng, 10)); 201 | Message::with_int_header(hdr) 202 | }; 203 | 204 | if gen_bool(&mut rng) { 205 | msg.set_state_init(gen_state(&mut rng, acc_id, workchain_id).clone()); 206 | } else if let Some(state_init) = state_init { 207 | msg.set_state_init(state_init); 208 | } 209 | 210 | let tr_lt = BLOCK_LT + 1; 211 | let lt = Arc::new(AtomicU64::new(tr_lt)); 212 | 213 | let acc_copy = acc.clone(); 214 | let executor = OrdinaryTransactionExecutor::new(blockchain_config.clone()); 215 | let trans = executor.execute_with_params(Some(&make_common(msg.clone())), &mut acc, execute_params_none(lt)); 216 | if cfg!(feature = "cross_check") { 217 | let mut skip_cross_check = false; 218 | 219 | // init_code_hash is not supported in cpp node 220 | if acc_copy.init_code_hash().is_some() { 221 | skip_cross_check = true; 222 | } 223 | 224 | if !skip_cross_check { 225 | cross_check::cross_check( 226 | &blockchain_config, 227 | &acc_copy, 228 | &acc, 229 | Some(&msg), 230 | trans.as_ref().ok(), 231 | &execute_params_none(Arc::new(AtomicU64::new(tr_lt))), 232 | _iteration 233 | ) 234 | } 235 | } 236 | if let Ok(trans) = &trans { 237 | assert_ne!(trans.read_description().unwrap().action_phase_ref().map_or(0, |ap| ap.result_code), 35); 238 | } 239 | 240 | check_account_and_transaction_balances(&acc_copy, &acc, &msg, trans.as_ref().ok()); 241 | } 242 | } 243 | 244 | fn gen_reserve_code(value: u64, t: u8) -> String { 245 | format!( 246 | "PUSHINT {} 247 | PUSHINT {} 248 | RAWRESERVE 249 | ", 250 | value, t 251 | ) 252 | } 253 | 254 | fn gen_message_code(t: u8) -> String { 255 | format!( 256 | "PUSHINT {} 257 | SENDRAWMSG 258 | ", 259 | t 260 | ) 261 | } 262 | 263 | fn gen_messages_code(rng: &mut StdRng, count_msgs: u8) -> String { 264 | let mut str = "".to_string(); 265 | for _i in 0..count_msgs { 266 | let mut flags= 0; 267 | flags += match gen_simple_int(rng, 3) { 268 | 0 => 0, 269 | 1 => 64, 270 | 2 => 128, 271 | _ => panic!("Incorrect value") 272 | }; 273 | flags += if gen_bool(rng) {1} else {0}; 274 | flags += if gen_bool(rng) {2} else {0}; 275 | flags += if gen_bool(rng) {32} else {0}; 276 | 277 | flags += if gen_simple_int(rng, 50) == 0 {21} else {0}; 278 | 279 | str += &*(gen_message_code(flags)); 280 | }; 281 | str 282 | } 283 | 284 | fn gen_state_reserve_and_sendmsgs(rng: &mut StdRng, src: &AccountId, w_id: i8) -> StateInit { 285 | let mut code = "".to_string(); 286 | 287 | if gen_bool(rng) { 288 | code += "ACCEPT 289 | " 290 | } 291 | 292 | code += "PUSHROOT 293 | "; 294 | 295 | let msgs_before = gen_simple_int(rng, 10) as u8; 296 | let msgs_after = gen_simple_int(rng, 10) as u8; 297 | let msgs_all = msgs_after + msgs_before; 298 | if msgs_all > 0 { 299 | code += &*"CTOS 300 | LDREF 301 | PLDREF 302 | ".repeat((msgs_all - 1) as usize); 303 | code += " 304 | CTOS 305 | PLDREF 306 | "; 307 | } 308 | 309 | code += &*gen_messages_code(rng, msgs_before); 310 | 311 | if gen_simple_int(rng, 10) != 0 { 312 | let reserve_flags = gen_simple_int(rng, 17) as u8; 313 | code += &*gen_reserve_code(gen_int(rng, 19), reserve_flags); 314 | } 315 | 316 | code += &*gen_messages_code(rng, msgs_after); 317 | 318 | let mut b = BuilderData::with_raw(vec![0x55; 32], 256).unwrap(); 319 | let mut fst = true; 320 | for _i in 0..msgs_all { 321 | let cell_b = b.into_cell().unwrap(); 322 | b = BuilderData::with_raw(vec![0x55; 32], 256).unwrap(); 323 | 324 | let mut msg1 = create_int_msg( 325 | AccountId::from([0x11; 32]), 326 | AccountId::from([0x33; 32]), 327 | gen_int(rng, 19), 328 | false, 329 | BLOCK_LT + 2 330 | ); 331 | msg1.set_src_address(MsgAddressInt::with_standart(None, w_id, src.clone()).unwrap()); 332 | 333 | b.checked_append_reference(msg1.serialize().unwrap()).unwrap(); 334 | if !fst { 335 | b.checked_append_reference(cell_b).unwrap(); 336 | } 337 | fst = false; 338 | } 339 | 340 | let mut state_rawreserve = StateInit::default(); 341 | state_rawreserve.set_code(compile_code_to_cell(&code).unwrap()); 342 | state_rawreserve.set_data(b.into_cell().unwrap()); 343 | state_rawreserve 344 | } 345 | 346 | #[test] 347 | fn test_rand_reverse_and_messages() { 348 | let mut rng = rand::SeedableRng::seed_from_u64(234235253453); 349 | 350 | let ordinary_acc_id = AccountId::from([0x11; 32]); 351 | let special_acc_id = AccountId::from([0x66u8; 32]); 352 | 353 | let blockchain_config = BLOCKCHAIN_CONFIG.to_owned(); 354 | assert!( 355 | blockchain_config.is_special_account( 356 | &MsgAddressInt::with_standart(None, -1, special_acc_id.clone()).unwrap() 357 | ).unwrap() 358 | ); 359 | 360 | for _iteration in 0..50_000 { 361 | let is_special = gen_bool(&mut rng); 362 | let acc_id = if !is_special {&ordinary_acc_id} else {&special_acc_id}; 363 | let workchain_id = -(gen_simple_int(&mut rng, 2) as i8); 364 | 365 | let mut acc = Account::active_by_init_code_hash( 366 | MsgAddressInt::with_standart(None, workchain_id, acc_id.clone()).unwrap(), 367 | CurrencyCollection::with_grams(gen_int(&mut rng, 19)), 368 | BLOCK_UT, 369 | gen_state_reserve_and_sendmsgs(&mut rng, acc_id, workchain_id), 370 | false 371 | ).unwrap(); 372 | 373 | let msg = if gen_bool(&mut rng) { 374 | let hdr = ExternalInboundMessageHeader::new( 375 | Default::default(), 376 | MsgAddressInt::with_standart(None, workchain_id, acc_id.clone()).unwrap(), 377 | ); 378 | Message::with_ext_in_header_and_body(hdr, SliceData::default()) 379 | } else { 380 | let mut hdr = InternalMessageHeader::with_addresses( 381 | MsgAddressInt::with_standart(None, workchain_id, AccountId::from([0x33; 32])).unwrap(), 382 | MsgAddressInt::with_standart(None, workchain_id, acc_id.clone()).unwrap(), 383 | CurrencyCollection::with_grams(gen_int(&mut rng, 19)), 384 | ); 385 | hdr.bounce = gen_bool(&mut rng); 386 | hdr.ihr_disabled = true; 387 | hdr.created_lt = PREV_BLOCK_LT; 388 | hdr.created_at = UnixTime32::default(); 389 | Message::with_int_header(hdr) 390 | }; 391 | 392 | let tr_lt = BLOCK_LT + 1; 393 | let lt = Arc::new(AtomicU64::new(tr_lt)); 394 | 395 | let acc_copy = acc.clone(); 396 | let executor = OrdinaryTransactionExecutor::new(blockchain_config.clone()); 397 | let trans = executor.execute_with_params(Some(&make_common(msg.clone())), &mut acc, execute_params_none(lt)); 398 | if cfg!(feature = "cross_check") { 399 | cross_check::cross_check( 400 | &blockchain_config, 401 | &acc_copy, 402 | &acc, 403 | Some(&msg), 404 | trans.as_ref().ok(), 405 | &execute_params_none(Arc::new(AtomicU64::new(tr_lt))), 406 | _iteration 407 | ) 408 | } 409 | if let Ok(trans) = &trans { 410 | assert_ne!(trans.read_description().unwrap().action_phase_ref().map_or(0, |ap| ap.result_code), 35); 411 | } 412 | 413 | check_account_and_transaction_balances(&acc_copy, &acc, &msg, trans.as_ref().ok()); 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /src/tests/test_tick_tock_transaction.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | #![allow(clippy::field_reassign_with_default)] 14 | 15 | use super::*; 16 | 17 | use std::sync::{atomic::AtomicU64, Arc}; 18 | 19 | mod common; 20 | use common::*; 21 | use pretty_assertions::assert_eq; 22 | use ever_block::{ 23 | accounts::{Account, AccountStorage, StorageInfo}, 24 | messages::{CommonMsgInfo, InternalMessageHeader, Message, MsgAddressInt}, 25 | out_actions::{OutAction, OutActions, SENDMSG_ORDINARY}, 26 | transactions::{ 27 | AccStatusChange, TrActionPhase, TrComputePhase, TrComputePhaseVm, TrStoragePhase, 28 | Transaction, TransactionDescr, TransactionDescrTickTock, TransactionTickTock, 29 | }, 30 | types::Grams, 31 | CurrencyCollection, GetRepresentationHash, Serializable, StateInit, StorageUsedShort, TickTock, 32 | UnixTime32, 33 | }; 34 | use ever_assembler::compile_code_to_cell; 35 | use ever_block::{AccountId, BuilderData, Cell}; 36 | use ever_vm::{ 37 | int, 38 | stack::{integer::IntegerData, Stack, StackItem}, 39 | }; 40 | 41 | const BLOCK_LT: u64 = 1_000_000_000; 42 | const ACCOUNT_UT: u32 = 1572169011; 43 | const BLOCK_UT: u32 = 1576526553; 44 | const MSG1_BALANCE: u64 = 50000000; 45 | const MSG2_BALANCE: u64 = 100000000; 46 | const INIT_CODE_HASH: bool = false; 47 | 48 | lazy_static::lazy_static! { 49 | static ref SENDER_ACCOUNT: AccountId = AccountId::from([0x11; 32]); 50 | static ref RECEIVER_ACCOUNT: AccountId = AccountId::from([0x22; 32]); 51 | } 52 | 53 | fn create_test_data() -> Cell { 54 | let (msg1, msg2) = create_two_internal_messages(); 55 | 56 | let mut b = BuilderData::with_raw(vec![0x55; 32], 256).unwrap(); 57 | b.checked_append_reference(msg2.serialize().unwrap()).unwrap(); 58 | b.checked_append_reference(msg1.serialize().unwrap()).unwrap(); 59 | b.into_cell().unwrap() 60 | } 61 | 62 | fn create_two_internal_messages() -> (Message, Message) { 63 | let msg1 = create_int_msg( 64 | AccountId::from([0x33; 32]), 65 | AccountId::from([0x11; 32]), 66 | MSG1_BALANCE, 67 | false, 68 | BLOCK_LT + 2 69 | ); 70 | let msg2 = create_int_msg( 71 | AccountId::from([0x33; 32]), 72 | AccountId::from([0x11; 32]), 73 | MSG2_BALANCE, 74 | true, 75 | BLOCK_LT + 3 76 | ); 77 | (msg1, msg2) 78 | } 79 | 80 | const INTERNAL_FWD_FEE: u64 = 5; 81 | fn create_int_msg(src: AccountId, dest: AccountId, value: u64, bounce: bool, lt: u64) -> Message { 82 | let balance = CurrencyCollection::with_grams(value); 83 | let mut hdr = InternalMessageHeader::with_addresses( 84 | MsgAddressInt::with_standart(None, -1, src).unwrap(), 85 | MsgAddressInt::with_standart(None, -1, dest).unwrap(), 86 | balance, 87 | ); 88 | hdr.bounce = bounce; 89 | hdr.ihr_disabled = true; 90 | hdr.ihr_fee = Grams::zero(); 91 | hdr.fwd_fee = INTERNAL_FWD_FEE.into(); 92 | hdr.created_lt = lt; 93 | hdr.created_at = UnixTime32::default(); 94 | Message::with_int_header(hdr) 95 | } 96 | 97 | fn create_test_code() -> Cell { 98 | let code = " 99 | ACCEPT 100 | PUSHROOT 101 | CTOS 102 | LDREF 103 | PLDREF 104 | PUSHINT 0 105 | SENDRAWMSG 106 | PUSHINT 0 107 | SENDRAWMSG 108 | "; 109 | 110 | compile_code_to_cell(code).unwrap() 111 | } 112 | 113 | fn create_test_account(amount: u64, address: AccountId, code: Cell, data: Cell) -> Account { 114 | let mut state = StateInit::default(); 115 | state.set_special(TickTock::with_values(true, true)); 116 | let mut account = Account::with_storage( 117 | &MsgAddressInt::with_standart( 118 | None, 119 | -1, 120 | address 121 | ).unwrap(), 122 | &StorageInfo::with_values( 123 | ACCOUNT_UT, 124 | None, 125 | ), 126 | &AccountStorage::active_by_init_code_hash(0, CurrencyCollection::with_grams(amount), state, INIT_CODE_HASH), 127 | ); 128 | account.set_code(code); 129 | account.set_data(data); 130 | account.update_storage_stat().unwrap(); 131 | account 132 | } 133 | 134 | #[test] 135 | fn test_tick_tock_executor_active_acc_with_code1() { 136 | let used = 1307u32; //gas units 137 | let storage_fees = 0; 138 | let msg_mine_fee = 1; 139 | let msg_fwd_fee = 5; 140 | let msg_remain_fee = msg_fwd_fee - msg_mine_fee; 141 | let gas_fees = 0u64; 142 | 143 | let config = BLOCKCHAIN_CONFIG.to_owned(); 144 | let acc_id = AccountId::from([0x33; 32]); 145 | let start_balance = 2000000000u64; 146 | let mut acc = create_test_account(start_balance, acc_id.clone(), create_test_code(), create_test_data()); 147 | // balance - (balance of 2 output messages + input msg fee + storage_fee + gas_fee) 148 | let mut new_acc = create_test_account(start_balance, acc_id.clone(), create_test_code(), create_test_data()); 149 | let tr_lt = BLOCK_LT + 1; 150 | new_acc.set_last_tr_time(tr_lt); 151 | 152 | let executor = TickTockTransactionExecutor::new(config.clone(), TransactionTickTock::Tick); 153 | let acc_copy = acc.clone(); 154 | let trans = executor.execute_with_params(None, &mut acc, execute_params_none(Arc::new(AtomicU64::new(tr_lt)))).unwrap(); 155 | 156 | if cfg!(feature = "cross_check") { 157 | cross_check::cross_check(&config, &acc_copy, &acc, None, Some(&trans), &execute_params_none(Arc::new(AtomicU64::new(tr_lt))), 0); 158 | } 159 | 160 | let mut good_trans = Transaction::with_address_and_status(acc_id, acc.status()); 161 | good_trans.set_logical_time(tr_lt); 162 | good_trans.set_now(BLOCK_UT); 163 | 164 | let (mut msg1, mut msg2) = create_two_internal_messages(); 165 | let mut actions = OutActions::default(); 166 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg1.clone())); 167 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg2.clone())); 168 | let hash = actions.hash().unwrap(); 169 | msg1.get_value_mut().unwrap().grams = Grams::from(MSG1_BALANCE - msg_fwd_fee); 170 | msg2.get_value_mut().unwrap().grams = Grams::from(MSG2_BALANCE - msg_fwd_fee); 171 | if let CommonMsgInfo::IntMsgInfo(int_header) = msg1.header_mut() { 172 | if let CommonMsgInfo::IntMsgInfo(int_header2) = msg2.header_mut() { 173 | int_header.fwd_fee = msg_remain_fee.into(); 174 | int_header2.fwd_fee = msg_remain_fee.into(); 175 | int_header.created_at = BLOCK_UT.into(); 176 | int_header2.created_at = BLOCK_UT.into(); 177 | } 178 | } 179 | 180 | good_trans.add_out_message(&make_common(msg1)).unwrap(); 181 | good_trans.add_out_message(&make_common(msg2)).unwrap(); 182 | good_trans.set_total_fees((storage_fees + gas_fees + msg_mine_fee * 2).into()); 183 | 184 | let mut description = TransactionDescrTickTock::default(); 185 | description.storage = TrStoragePhase::with_params(0u64.into(), None, AccStatusChange::Unchanged); 186 | 187 | let mut vm_phase = TrComputePhaseVm::default(); 188 | vm_phase.success = true; 189 | vm_phase.msg_state_used = false; 190 | vm_phase.account_activated = false; 191 | vm_phase.gas_used = used.into(); 192 | vm_phase.gas_limit = 10000000u32.into(); 193 | vm_phase.gas_fees = gas_fees.into(); 194 | vm_phase.vm_steps = 10; 195 | description.compute_ph = TrComputePhase::Vm(vm_phase); 196 | 197 | let mut action_ph = TrActionPhase::default(); 198 | action_ph.success = true; 199 | action_ph.valid = true; 200 | action_ph.status_change = AccStatusChange::Unchanged; 201 | action_ph.tot_actions = 2; 202 | action_ph.msgs_created = 2; 203 | action_ph.add_fwd_fees((2 * msg_fwd_fee).into()); 204 | action_ph.add_action_fees((2 * msg_mine_fee).into()); 205 | action_ph.action_list_hash = hash; 206 | action_ph.tot_msg_size = StorageUsedShort::with_values_checked(2, 1378).unwrap(); 207 | 208 | description.action = Some(action_ph); 209 | description.aborted = false; 210 | description.destroyed = false; 211 | good_trans.write_description(&TransactionDescr::TickTock(description)).unwrap(); 212 | 213 | assert_eq!( 214 | trans.read_description().unwrap().compute_phase_ref().unwrap().clone().get_vmphase_mut().unwrap().gas_used, 215 | good_trans.read_description().unwrap().compute_phase_ref().unwrap().clone().get_vmphase_mut().unwrap().gas_used); 216 | 217 | assert_eq!(trans.read_description().unwrap(), good_trans.read_description().unwrap()); 218 | 219 | // account isn't changed in state for tick-tock 220 | trans.out_msgs.scan_diff(&good_trans.out_msgs, |key: ever_block::U15, msg1, msg2| { 221 | assert_eq!(msg1, msg2, "for key {}", key.0); 222 | Ok(true) 223 | }).unwrap(); 224 | assert_eq!(trans, good_trans); 225 | } 226 | /* 227 | fn create_wallet_data() -> Cell { 228 | //test public key 229 | BuilderData::with_raw(vec![0x00; 32], 256).unwrap().into() 230 | } 231 | 232 | fn create_wallet_code() -> Cell { 233 | let code = " 234 | ; s1 - body slice 235 | IFNOTRET 236 | ACCEPT 237 | BLOCKLT 238 | LTIME 239 | INC ; increase logical time by 1 240 | PUSH s2 ; body to top 241 | PUSHINT 96 ; internal header in body, cut unixtime and lt 242 | SDSKIPLAST 243 | 244 | NEWC 245 | STSLICE 246 | STU 64 ; store tr lt 247 | STU 32 ; store unixtime 248 | STSLICECONST 0 ; no init 249 | STSLICECONST 0 ; body (Either X) 250 | ENDC 251 | PUSHINT 0 252 | SENDRAWMSG 253 | "; 254 | compile_code_to_cell(code).unwrap() 255 | } 256 | 257 | #[test] 258 | fn test_light_wallet_contract() { 259 | let contract_code = create_wallet_code(); 260 | let contract_data = create_wallet_data(); 261 | let acc1_id = AccountId::from([0x11; 32]); 262 | let acc2_id = AccountId::from([0x22; 32]); 263 | 264 | let gas_used1 = 1387; 265 | let gas_fee1 = gas_used1 * 10000; 266 | let gas_fee2 = 1000000; // flat_gas_price 267 | let start_balance1 = 1000000000; 268 | let start_balance2 = 500000000; 269 | let fwd_fee = 10000000; 270 | let storage_fee1 = 138234403; 271 | let storage_fee2 = 138234403; // TODO: check here!!! 272 | 273 | let acc1 = create_test_account(start_balance1.clone(), acc1_id.clone(), contract_code.clone(), contract_data.clone()); 274 | let mut shard_acc1 = Some(ShardAccount::with_params(acc1.clone(), UInt256::default(), 0).unwrap()); 275 | let acc2 = create_test_account(start_balance2, acc2_id.clone(), contract_code.clone(), contract_data.clone()); 276 | let mut shard_acc2 = Some(ShardAccount::with_params(acc2, UInt256::default(), 0).unwrap()); 277 | 278 | let config = BLOCKCHAIN_CONFIG.to_owned(); 279 | 280 | let transfer = 100000000; 281 | let lt = Arc::new(AtomicU64::new(BLOCK_LT + 1)); 282 | 283 | let executor = TickTockTransactionExecutor::new(config, TransactionTickTock::Tick); 284 | let trans = executor.execute( 285 | &mut shard_acc1, BLOCK_UT, BLOCK_LT, lt, true 286 | ).unwrap(); 287 | let msg = trans.get_out_msg(0).unwrap(); 288 | println!("{:?}", msg); 289 | //new acc.balance = acc.balance - in_fwd_fee - transfer_value - storage_fees - gas_fee 290 | //transfer_value is reduced by fwd_fees: 291 | //new transfer_value = transfer_value - msg.fwd.fee 292 | let newbalance1 = start_balance1 - fwd_fee - transfer - storage_fee1 - gas_fee1; 293 | assert_eq!(shard_acc1.clone().unwrap().read_account().unwrap().balance().unwrap().clone(), CurrencyCollection::with_grams(newbalance1)); 294 | assert_ne!(shard_acc1.clone().unwrap().last_trans_lt(), 0); 295 | assert_ne!(shard_acc1.unwrap().last_trans_hash(), &UInt256::default()); 296 | 297 | let config = BLOCKCHAIN_CONFIG.to_owned(); 298 | let executor = TickTockTransactionExecutor::new(config, TransactionTickTock::Tick); 299 | let _trans = executor.execute(&mut shard_acc2, BLOCK_UT, BLOCK_LT, lt, true).unwrap(); 300 | 301 | //new acc.balance = acc.balance + transfer_value - fwd_fee - storage_fee - gas_fee 302 | let newbalance2 = start_balance2 + transfer - fwd_fee - storage_fee2 - gas_fee2; 303 | assert_eq!(shard_acc2.clone().unwrap().read_account().unwrap().balance().unwrap().clone(), CurrencyCollection::with_grams(newbalance2)); 304 | assert_ne!(shard_acc2.clone().unwrap().last_trans_lt(), 0); 305 | assert_ne!(shard_acc2.unwrap().last_trans_hash(), &UInt256::default()); 306 | 307 | } 308 | 309 | fn test_transfer_code(mode: u8, ending: &str) -> Cell { 310 | let code = format!(" 311 | PUSHCONT {{ 312 | ACCEPT 313 | NEWC ; create builder 314 | STSLICE ; store internal msg slice into builder (next in stack - internal message body like a slice) 315 | ENDC ; finish cell creating 316 | PUSHINT {x} 317 | SENDRAWMSG ; send message with created cell as a root 318 | {e} 319 | }} 320 | IF ; top-of-stack value is function selector, it is non zero - message is external 321 | ", 322 | x = mode, 323 | e = ending 324 | ); 325 | 326 | compile_code_to_cell(&code).unwrap() 327 | } 328 | 329 | fn create_test_transfer_account(amount: u64, mode: u8) -> Account { 330 | create_test_transfer_account_with_ending(amount, mode, "") 331 | } 332 | 333 | fn create_test_transfer_account_with_ending(amount: u64, mode: u8, ending: &str) -> Account { 334 | let acc_id = AccountId::from([0x11; 32]); 335 | 336 | let mut state = StateInit::default(); 337 | state.set_code(test_transfer_code(mode, ending)); 338 | Account::with_storage( 339 | &MsgAddressInt::with_standart( 340 | None, 341 | -1, 342 | acc_id 343 | ).unwrap(), 344 | &StorageInfo::with_values( 345 | ACCOUNT_UT, 346 | None, 347 | ), 348 | &AccountStorage { 349 | last_trans_lt: 0, 350 | balance: CurrencyCollection::with_grams(amount), 351 | state: AccountState::with_state(state), 352 | } 353 | ) 354 | } 355 | 356 | fn create_test_external_msg_with_int(transfer_value: u64) -> Message { 357 | let acc_id = SENDER_ACCOUNT.clone(); 358 | let mut hdr = ExternalInboundMessageHeader::default(); 359 | hdr.dst = MsgAddressInt::with_standart(None, -1, acc_id.clone()).unwrap(); 360 | hdr.import_fee = Grams::zero(); 361 | let mut msg = Message::with_ext_in_header(hdr); 362 | 363 | let int_msg = create_int_msg( 364 | acc_id.clone(), 365 | RECEIVER_ACCOUNT.clone(), 366 | transfer_value, 367 | false, 368 | BLOCK_LT + 2 369 | ); 370 | msg.set_body(int_msg.serialize().unwrap().into()); 371 | 372 | msg 373 | } 374 | 375 | #[test] 376 | fn test_trexecutor_active_acc_with_code2() { 377 | let start_balance = 2000000000; 378 | let gas_used = 1170; 379 | let gas_fees = gas_used * 10000; 380 | let transfer = 50000000; 381 | let storage_fee = 78924597; 382 | let msg_mine_fee = 3333282; 383 | let msg_fwd_fee = 10000000; 384 | let msg_remain_fee = msg_fwd_fee - msg_mine_fee; 385 | 386 | let acc = create_test_transfer_account(start_balance, SENDMSG_ORDINARY); 387 | let old_acc = ShardAccount::with_params(acc.clone(), UInt256::default(), 0).unwrap(); 388 | let config = BLOCKCHAIN_CONFIG.to_owned(); 389 | let mut new_acc = create_test_transfer_account( 390 | start_balance - (msg_fwd_fee + transfer + storage_fee + gas_fees), 0); 391 | let msg = create_test_external_msg_with_int(transfer); 392 | let tr_lt = BLOCK_LT + 1; 393 | let lt = Arc::new(AtomicU64::new(tr_lt)); 394 | new_acc.set_last_tr_time(tr_lt); 395 | 396 | let executor = TickTockTransactionExecutor::new(config, TransactionTickTock::Tick); 397 | let mut shard_acc = Some(old_acc.clone()); 398 | let trans = executor.execute( 399 | &mut shard_acc, BLOCK_UT, BLOCK_LT, lt, true 400 | ).unwrap(); 401 | //println!("{:#?}", trans.read_description().unwrap()); 402 | 403 | let mut good_trans = Transaction::with_account_and_message(&old_acc.read_account().unwrap(), &msg, tr_lt).unwrap(); 404 | good_trans.set_now(BLOCK_UT); 405 | 406 | let msg1 = create_int_msg( 407 | AccountId::from([0x11; 32]), 408 | AccountId::from([0x22; 32]), 409 | transfer, 410 | false, 411 | BLOCK_LT + 2 412 | ); 413 | let mut msg1_new_value = create_int_msg( 414 | AccountId::from([0x11; 32]), 415 | AccountId::from([0x22; 32]), 416 | transfer - msg_fwd_fee, 417 | false, 418 | BLOCK_LT + 2 419 | ); 420 | if let CommonMsgInfo::IntMsgInfo(int_header) = msg1_new_value.header_mut() { 421 | int_header.fwd_fee = msg_remain_fee.into(); 422 | int_header.created_at = BLOCK_UT.into(); 423 | } 424 | 425 | good_trans.add_out_message(&msg1_new_value.clone()).unwrap(); 426 | good_trans.set_total_fees((msg_fwd_fee + storage_fee + gas_fees + msg_mine_fee).into()); 427 | 428 | let old = old_acc.read_account().unwrap().serialize().unwrap()); 429 | let new = new_acc.serialize().unwrap()); 430 | 431 | let mut description = TransactionDescrTickTock::default(); 432 | description.storage_ph = TrStoragePhase::with_params(storage_fee.into(), None, AccStatusChange::Unchanged); 433 | 434 | let mut vm_phase = TrComputePhaseVm::default(); 435 | vm_phase.success = true; 436 | vm_phase.msg_state_used = false; 437 | vm_phase.account_activated = false; 438 | vm_phase.gas_used = gas_used.into(); 439 | vm_phase.gas_limit = 0u64.into(); 440 | vm_phase.gas_credit = Some(10000.into()); 441 | vm_phase.gas_fees = gas_fees.into(); 442 | description.compute_ph = TrComputePhase::Vm(vm_phase); 443 | 444 | let mut action_ph = TrActionPhase::default(); 445 | action_ph.success = true; 446 | action_ph.valid = true; 447 | action_ph.status_change = AccStatusChange::Unchanged; 448 | action_ph.tot_actions = 1; 449 | action_ph.msgs_created = 1; 450 | action_ph.add_fwd_fees(msg_fwd_fee.into()); 451 | action_ph.add_action_fees(msg_mine_fee.into()); 452 | action_ph.tot_msg_size = StorageUsedShort::calculate_for_struct(&msg1_new_value).unwrap(); 453 | 454 | let mut actions = OutActions::default(); 455 | actions.push_back(OutAction::new_send(SENDMSG_ORDINARY, msg1)); 456 | action_ph.action_list_hash = actions.hash().unwrap(); 457 | description.action = Some(action_ph); 458 | description.aborted = false; 459 | description.destroyed = false; 460 | good_trans.write_description(&TransactionDescr::TickTock(description)).unwrap(); 461 | 462 | assert_eq!( 463 | trans.read_description().unwrap().compute_phase_ref().unwrap().clone().get_vmphase_mut().unwrap().gas_used, 464 | good_trans.read_description().unwrap().compute_phase_ref().unwrap().clone().get_vmphase_mut().unwrap().gas_used); 465 | 466 | assert_eq!(trans.read_description().unwrap(), good_trans.read_description().unwrap()); 467 | 468 | // TODO: New fields in StorageInfo were added, so now worck incorrect 469 | //assert_eq!(shard_acc.unwrap().read_account().unwrap(), new_acc); 470 | assert_eq!(trans, good_trans); 471 | } 472 | 473 | #[test] 474 | fn test_trexecutor_active_acc_credit_first_false() { 475 | let start_balance = 1000000000; 476 | let acc = create_test_transfer_account(start_balance, SENDMSG_ORDINARY); 477 | 478 | let mut shard_acc = Some(ShardAccount::with_params(acc, UInt256::default(), 0).unwrap()); 479 | let lt = Arc::new(AtomicU64::new(BLOCK_LT + 1)); 480 | 481 | let config = BLOCKCHAIN_CONFIG.to_owned(); 482 | let executor = TickTockTransactionExecutor::new(config, TransactionTickTock::Tick); 483 | let trans = executor.execute(&mut shard_acc, BLOCK_UT, BLOCK_LT, lt, false).unwrap(); 484 | assert_eq!(trans.read_description().unwrap().is_credit_first().unwrap(), false); 485 | } 486 | 487 | #[test] 488 | fn test_trexecutor_active_acc_with_zero_balance() { 489 | let start_balance = 0; 490 | let acc = create_test_transfer_account(start_balance, SENDMSG_ORDINARY); 491 | let transfer = 1000000000; 492 | let storage_fee = 76796891; 493 | let gas_fee = 1000000; // flat_gas_price 494 | 495 | let mut shard_acc = Some(ShardAccount::with_params(acc, UInt256::default(), 0).unwrap()); 496 | let lt = Arc::new(AtomicU64::new(BLOCK_LT + 1)); 497 | 498 | let config = BLOCKCHAIN_CONFIG.to_owned(); 499 | let executor = TickTockTransactionExecutor::new(config, TransactionTickTock::Tick); 500 | let trans = executor.execute(&mut shard_acc, BLOCK_UT, BLOCK_LT, lt, false).unwrap(); 501 | assert_eq!(trans.read_description().unwrap().is_aborted(), false); 502 | let vm_phase_success = trans.read_description().unwrap().compute_phase_ref().unwrap().clone().get_vmphase_mut().unwrap().success; 503 | assert_eq!(vm_phase_success, true); 504 | assert_eq!( 505 | shard_acc.unwrap().read_account().unwrap().balance().unwrap(), 506 | &CurrencyCollection::with_grams(transfer - storage_fee - gas_fee)); 507 | } 508 | 509 | //contract send all its balance to another account using special mode in SENDRAWMSG. 510 | //contract balance must equal to zero after transaction. 511 | fn active_acc_send_all_balance(ending: &str) { 512 | let start_balance = 10_000_000_000; //10 grams 513 | let acc = create_test_transfer_account_with_ending(start_balance, SENDMSG_ALL_BALANCE, ending); 514 | 515 | let mut shard_acc = Some(ShardAccount::with_params(acc, UInt256::from(SENDER_ACCOUNT.get_bytestring(0)), 0).unwrap()); 516 | let lt = Arc::new(AtomicU64::new(BLOCK_LT + 1)); 517 | 518 | let config = BLOCKCHAIN_CONFIG.to_owned(); 519 | let executor = TickTockTransactionExecutor::new(config, TransactionTickTock::Tick); 520 | let trans = executor.execute(&mut shard_acc, BLOCK_UT, BLOCK_LT, lt, false).unwrap(); 521 | assert_eq!(trans.read_description().unwrap().is_aborted(), false); 522 | let vm_phase_success = trans.read_description().unwrap().compute_phase_ref().unwrap().clone().get_vmphase_mut().unwrap().success; 523 | assert_eq!(vm_phase_success, true); 524 | assert_eq!(shard_acc.unwrap().read_account().unwrap().balance().unwrap(), &CurrencyCollection::with_grams(0)); 525 | assert!(trans.get_out_msg(0).unwrap().is_some()); 526 | assert!(trans.get_out_msg(1).unwrap().is_none()); 527 | } 528 | 529 | #[test] 530 | fn test_trexecutor_active_acc_send_all_balance() { 531 | active_acc_send_all_balance(""); 532 | } 533 | 534 | #[test] 535 | fn test_trexecutor_active_acc_send_all_balance_with_commit_and_throw() { 536 | active_acc_send_all_balance("COMMIT THROW 11"); 537 | } 538 | 539 | #[test] 540 | fn test_trexecutor_active_acc_send_all_balance_with_commit_and_secondmsg_with_throw() { 541 | active_acc_send_all_balance( 542 | "COMMIT 543 | NEWC 544 | STSLICECONST x1234_ 545 | ENDC 546 | PUSHINT 10 547 | SENDRAWMSG 548 | THROW 11" 549 | ); 550 | } 551 | */ 552 | #[test] 553 | fn test_build_ticktock_stack() { 554 | let acc_balance = 10_000_000; 555 | let acc_id = AccountId::from([0x22; 32]); 556 | let account = create_test_account(acc_balance, acc_id, create_test_code(), create_test_data()); 557 | 558 | let executor = TickTockTransactionExecutor::new(BLOCKCHAIN_CONFIG.clone(), TransactionTickTock::Tock); 559 | let test_stack1 = executor.build_stack(None, &account).unwrap(); 560 | 561 | //stack for internal msg 562 | let mut etalon_stack1 = Stack::new(); 563 | etalon_stack1 564 | .push(int!(10_000_000)) 565 | .push(StackItem::integer(IntegerData::from_unsigned_bytes_be([0x22; 32]))) 566 | .push(int!(-1)) 567 | .push(int!(-2)); 568 | 569 | assert_eq!(test_stack1, etalon_stack1); 570 | 571 | let executor = TickTockTransactionExecutor::new(BLOCKCHAIN_CONFIG.clone(), TransactionTickTock::Tick); 572 | let test_stack2 = executor.build_stack(None, &account).unwrap(); 573 | 574 | //stack for external msg 575 | let mut etalon_stack2 = Stack::new(); 576 | etalon_stack2 577 | .push(int!(10_000_000)) 578 | .push(StackItem::integer(IntegerData::from_unsigned_bytes_be([0x22; 32]))) 579 | .push(int!(0)) 580 | .push(int!(-2)); 581 | 582 | assert_eq!(test_stack2, etalon_stack2); 583 | } 584 | -------------------------------------------------------------------------------- /src/tests/test_transaction_executor_with_real_data.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use self::common::make_common; 15 | 16 | use super::*; 17 | 18 | use std::{ 19 | io::{BufRead, BufReader}, 20 | sync::{Arc, atomic::AtomicU64} 21 | }; 22 | 23 | use pretty_assertions::assert_eq; 24 | use ever_block::{ 25 | Account, AccountStorage, Block, ConfigParams, CurrencyCollection, 26 | Deserializable, Message, MsgAddressInt, Serializable, StateInit, 27 | StorageInfo, Transaction, 28 | }; 29 | use ever_block::{Cell, Result, read_single_root_boc}; 30 | 31 | use crate::{ 32 | blockchain_config::BlockchainConfig, 33 | OrdinaryTransactionExecutor, TransactionExecutor 34 | }; 35 | 36 | mod common; 37 | 38 | use common::try_replay_transaction; 39 | use crate::transaction_executor::tests_with_real_data::common::custom_config; 40 | 41 | fn replay_transaction_by_files(account: &str, account_after: &str, transaction: &str, config: &str) { 42 | common::replay_transaction_by_files(None, account, account_after, transaction, config) 43 | } 44 | 45 | #[ignore] 46 | #[test] 47 | fn test_sample_replay_transaction() { 48 | let code = "code_boc_file_name_here"; 49 | let data = "data_boc_file_name_here"; 50 | let message = "msg_boc_file_name_here"; 51 | let key_block = "key_block_boc_file_name_here"; 52 | replay_contract_by_files(code, data, message, key_block).unwrap() 53 | } 54 | 55 | // account_code and account_data - filenames with bocs of code and data for account 56 | // in_message - filename with boc of message 57 | // key_block - filename with masterchain keyblock 58 | fn replay_contract_by_files(account_code: &str, account_data: &str, in_message: &str, key_block: &str) -> Result<()> { 59 | let code = read_single_root_boc(std::fs::read(account_code)?)?; 60 | let data = read_single_root_boc(std::fs::read(account_data)?)?; 61 | let block = Block::construct_from_file(key_block)?; 62 | let mc_block_extra = block.read_extra()?.read_custom()?.expect("must be key block"); 63 | let config = mc_block_extra.config().cloned().expect("must be in key block"); 64 | let message = Message::construct_from_file(in_message)?; 65 | try_replay_contract_as_transaction(code, data, message, config)?; 66 | Ok(()) 67 | } 68 | 69 | #[ignore] 70 | #[test] 71 | fn test_sample_replay_many_transactions() { 72 | let code = "code_boc_file_name_here"; 73 | let data = "data_boc_file_name_here"; 74 | let message = "msg_text_file_name_here"; 75 | let key_block = "key_block_boc_file_name_here"; 76 | many_replay_contract_by_files(code, data, message, key_block).unwrap() 77 | } 78 | 79 | // account_code and account_data - filenames with bocs of code and data for account 80 | // in_message - filename with serialized messages as base64 81 | // key_block - filename with masterchain keyblock 82 | fn many_replay_contract_by_files(account_code: &str, account_data: &str, in_message: &str, key_block: &str) -> Result<()> { 83 | let code = read_single_root_boc(std::fs::read(account_code)?)?; 84 | let data = read_single_root_boc(std::fs::read(account_data)?)?; 85 | let cell = read_single_root_boc(std::fs::read(key_block)?)?; 86 | let block = Block::construct_from_cell(cell).unwrap(); 87 | let mc_block_extra = block.read_extra()?.read_custom()?.expect("must be key block"); 88 | let config = mc_block_extra.config().cloned().expect("must be in key block"); 89 | let file = std::fs::File::open(in_message)?; 90 | let mut result = vec![]; 91 | let mut idx = 1; 92 | for ln in BufReader::new(file).lines() { 93 | let contents = ln.unwrap(); 94 | println!("Message no. #{} len: {}", idx, contents.len()); 95 | if idx > 0 { 96 | let message = Message::construct_from_base64(&contents)?; 97 | let transaction = try_replay_contract_as_transaction(code.clone(), data.clone(), message, config.clone())?; 98 | let descr = transaction.read_description()?; 99 | let compute_ph = descr.compute_phase_ref().expect("no compute phase"); 100 | match compute_ph { 101 | ever_block::TrComputePhase::Vm(vm) => { 102 | let steps = vm.vm_steps; 103 | let gas = vm.gas_used; 104 | result.push((idx, contents.len(), steps, gas)); 105 | } 106 | _ => panic!("compute skipped") 107 | } 108 | } 109 | idx += 1; 110 | } 111 | panic!("{:?}", result) 112 | } 113 | 114 | // creates account with code and data and address from message 115 | // then executes with config params 116 | fn try_replay_contract_as_transaction(code: Cell, data: Cell, message: Message, config: ConfigParams) -> Result { 117 | let at = std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap().as_secs() as u32; 118 | let lt = 1_000_005; 119 | 120 | let mut state = StateInit::default(); 121 | state.set_code(code); 122 | state.set_data(data); 123 | 124 | let account_id = message.int_dst_account_id().expect("must be exetrnal inbound message"); 125 | let addr = MsgAddressInt::with_standart(None, 0, account_id)?; 126 | let balance = CurrencyCollection::with_grams(100_000_000_000); 127 | 128 | let mut account = Account::with_storage( 129 | &addr, 130 | &StorageInfo::with_values(at - 100, None), 131 | &AccountStorage::active_by_init_code_hash(0, balance, state, config.has_capability(GlobalCapabilities::CapInitCodeHash)), 132 | ); 133 | account.update_storage_stat().unwrap(); 134 | try_replay_transaction(&mut account, Some(message), config, at, lt) 135 | } 136 | 137 | #[test] 138 | fn test_real_transaction() { 139 | replay_transaction_by_files( 140 | "real_ever_boc/simple_account_old.boc", 141 | "real_ever_boc/simple_account_new.boc", 142 | "real_ever_boc/simple_transaction.boc", 143 | "real_ever_boc/config.boc" 144 | ) 145 | } 146 | 147 | #[test] 148 | fn test_real_deploy_transaction() { 149 | replay_transaction_by_files( 150 | "real_ever_boc/deploy_account_old.boc", 151 | "real_ever_boc/deploy_account_new.boc", 152 | "real_ever_boc/deploy_transaction.boc", 153 | "real_ever_boc/config.boc" 154 | ) 155 | } 156 | 157 | #[test] 158 | fn test_init_account_transaction() { 159 | replay_transaction_by_files( 160 | "real_ever_boc/init_account_old.boc", 161 | "real_ever_boc/init_account_new.boc", 162 | "real_ever_boc/init_transaction.boc", 163 | "real_ever_boc/config.boc" 164 | ) 165 | } 166 | 167 | #[test] 168 | fn test_check_execute_bounced_message() { 169 | replay_transaction_by_files( 170 | "real_ever_boc/bounce_msg_account_old.boc", 171 | "real_ever_boc/bounce_msg_account_new.boc", 172 | "real_ever_boc/bounce_msg_transaction.boc", 173 | "real_ever_boc/config.boc" 174 | ) 175 | } 176 | 177 | #[test] 178 | fn test_check_execute_out_message_with_body_in_ref() { 179 | replay_transaction_by_files( 180 | "real_ever_boc/msg_body_ref_account_old.boc", 181 | "real_ever_boc/msg_body_ref_account_new.boc", 182 | "real_ever_boc/msg_body_ref_transaction.boc", 183 | "real_ever_boc/config.boc" 184 | ) 185 | } 186 | 187 | #[test] 188 | fn test_check_execute_uninit_account() { 189 | replay_transaction_by_files( 190 | "real_ever_boc/uninit_account_old.boc", 191 | "real_ever_boc/uninit_account_new.boc", 192 | "real_ever_boc/uninit_account_transaction.boc", 193 | "real_ever_boc/config.boc" 194 | ) 195 | } 196 | 197 | #[test] 198 | fn test_check_send_remainig_msg_balance() { 199 | replay_transaction_by_files( 200 | "real_ever_boc/send_remainig_msg_balance_account_old.boc", 201 | "real_ever_boc/send_remainig_msg_balance_account_new.boc", 202 | "real_ever_boc/send_remainig_msg_balance_transaction.boc", 203 | "real_ever_boc/config.boc" 204 | ) 205 | } 206 | 207 | #[test] 208 | fn test_check_out_of_gas_transaction() { 209 | replay_transaction_by_files( 210 | "real_ever_boc/out_of_gas_account_old.boc", 211 | "real_ever_boc/out_of_gas_account_new.boc", 212 | "real_ever_boc/out_of_gas_transaction.boc", 213 | "real_ever_boc/config.boc" 214 | ) 215 | } 216 | 217 | #[test] 218 | fn test_check_wrong_skip_reason() { 219 | replay_transaction_by_files( 220 | "real_ever_boc/wrong_skip_reason_account_old.boc", 221 | "real_ever_boc/wrong_skip_reason_account_new.boc", 222 | "real_ever_boc/wrong_skip_reason_transaction.boc", 223 | "real_ever_boc/config.boc" 224 | ) 225 | } 226 | 227 | #[test] 228 | fn test_check_wrong_compute_phase() { 229 | replay_transaction_by_files( 230 | "real_ever_boc/wrong_compute_phase_account_old.boc", 231 | "real_ever_boc/wrong_compute_phase_account_new.boc", 232 | "real_ever_boc/wrong_compute_phase_transaction.boc", 233 | "real_ever_boc/config.boc" 234 | ) 235 | } 236 | 237 | #[test] 238 | fn test_check_nofunds_to_send_message_without_error() { 239 | replay_transaction_by_files( 240 | "real_ever_boc/nofunds_without_error_account_old.boc", 241 | "real_ever_boc/nofunds_without_error_account_new.boc", 242 | "real_ever_boc/nofunds_without_error_transaction.boc", 243 | "real_ever_boc/config.boc" 244 | ) 245 | } 246 | 247 | #[test] 248 | fn test_bounce_message_to_new_account() { 249 | replay_transaction_by_files( 250 | "real_ever_boc/bounce_message_to_new_account_account_old.boc", 251 | "real_ever_boc/bounce_message_to_new_account_account_new.boc", 252 | "real_ever_boc/bounce_message_to_new_account_transaction.boc", 253 | "real_ever_boc/config.boc" 254 | ) 255 | } 256 | 257 | #[test] 258 | fn test_out_of_gas_in_cmd() { 259 | replay_transaction_by_files( 260 | "real_ever_boc/bounce_message_to_new_account_account_old.boc", 261 | "real_ever_boc/bounce_message_to_new_account_account_new.boc", 262 | "real_ever_boc/bounce_message_to_new_account_transaction.boc", 263 | "real_ever_boc/config.boc" 264 | ) 265 | } 266 | 267 | #[test] 268 | fn test_freeze_account() { 269 | replay_transaction_by_files( 270 | "real_ever_boc/freeze_account_old.boc", 271 | "real_ever_boc/freeze_account_new.boc", 272 | "real_ever_boc/freeze_transaction.boc", 273 | "real_ever_boc/config.boc" 274 | ) 275 | } 276 | 277 | #[test] 278 | fn test_send_to_frozen_account() { 279 | replay_transaction_by_files( 280 | "real_ever_boc/send_to_frozen_account_old.boc", 281 | "real_ever_boc/send_to_frozen_account_new.boc", 282 | "real_ever_boc/send_to_frozen_transaction.boc", 283 | "real_ever_boc/config.boc" 284 | ) 285 | } 286 | 287 | #[test] 288 | fn test_unfreeze_account() { 289 | replay_transaction_by_files( 290 | "real_ever_boc/unfreeze_account_old.boc", 291 | "real_ever_boc/unfreeze_account_new.boc", 292 | "real_ever_boc/unfreeze_transaction.boc", 293 | "real_ever_boc/config.boc" 294 | ) 295 | } 296 | 297 | #[test] 298 | fn test_bounce_to_empty_account() { 299 | replay_transaction_by_files( 300 | "real_ever_boc/bounce_to_empty_account_old.boc", 301 | "real_ever_boc/bounce_to_empty_account_new.boc", 302 | "real_ever_boc/bounce_to_empty_transaction.boc", 303 | "real_ever_boc/config.boc" 304 | ) 305 | } 306 | 307 | #[test] 308 | fn test_bounce_to_low_balance_account() { 309 | replay_transaction_by_files( 310 | "real_ever_boc/bounce_to_low_balance_account_old.boc", 311 | "real_ever_boc/bounce_to_low_balance_account_new.boc", 312 | "real_ever_boc/bounce_to_low_balance_transaction.boc", 313 | "real_ever_boc/config.boc" 314 | ) 315 | } 316 | 317 | #[test] 318 | fn test_depool_balance_check() { 319 | replay_transaction_by_files( 320 | "real_ever_boc/depool_balance_check_account_old.boc", 321 | "real_ever_boc/depool_balance_check_account_new.boc", 322 | "real_ever_boc/depool_balance_check_transaction.boc", 323 | "real_ever_boc/config.boc" 324 | ) 325 | } 326 | 327 | #[test] 328 | fn test_no_balance_to_send_transaction() { 329 | replay_transaction_by_files( 330 | "real_ever_boc/no_balance_to_send_account_old.boc", 331 | "real_ever_boc/no_balance_to_send_account_new.boc", 332 | "real_ever_boc/no_balance_to_send_transaction.boc", 333 | "real_ever_boc/config.boc" 334 | ) 335 | } 336 | 337 | #[test] 338 | fn test_int_message_to_elector_transaction() { 339 | replay_transaction_by_files( 340 | "real_ever_boc/int_message_to_elector_account_old.boc", 341 | "real_ever_boc/int_message_to_elector_account_new.boc", 342 | "real_ever_boc/int_message_to_elector_transaction.boc", 343 | "real_ever_boc/config.boc" 344 | ) 345 | } 346 | 347 | #[test] 348 | fn test_int_message_to_elector2_transaction() { 349 | replay_transaction_by_files( 350 | "real_ever_boc/int_message_to_elector2_account_old.boc", 351 | "real_ever_boc/int_message_to_elector2_account_new.boc", 352 | "real_ever_boc/int_message_to_elector2_transaction.boc", 353 | "real_ever_boc/config.boc" 354 | ) 355 | } 356 | 357 | #[test] 358 | fn test_ihr_message() { 359 | replay_transaction_by_files( 360 | "real_ever_boc/ihr_message_account_old.boc", 361 | "real_ever_boc/ihr_message_account_new.boc", 362 | "real_ever_boc/ihr_message_transaction.boc", 363 | "real_ever_boc/config.boc" 364 | ) 365 | } 366 | 367 | #[test] 368 | fn test_tick_tock_message() { 369 | replay_transaction_by_files( 370 | "real_ever_boc/tick_tock_acc_old.boc", 371 | "real_ever_boc/tick_tock_acc_new.boc", 372 | "real_ever_boc/tick_tock_tx.boc", 373 | "real_ever_boc/config.boc" 374 | ) 375 | } 376 | 377 | #[test] 378 | fn test_count_steps_vm() { 379 | replay_transaction_by_files( 380 | "real_ever_boc/count_steps_acc_old.boc", 381 | "real_ever_boc/count_steps_acc_new.boc", 382 | "real_ever_boc/count_steps_tx.boc", 383 | "real_ever_boc/config.boc" 384 | ) 385 | } 386 | 387 | #[test] 388 | fn test_not_aborted_accepted_transaction() { 389 | replay_transaction_by_files( 390 | "real_ever_boc/not_abort_accept_account_account_old.boc", 391 | "real_ever_boc/not_abort_accept_account_account_new.boc", 392 | "real_ever_boc/not_abort_accept_account_transaction.boc", 393 | "real_ever_boc/config.boc" 394 | ) 395 | } 396 | 397 | #[test] 398 | fn test_ihr_fee_output_msg() { 399 | if !cfg!(feature = "ihr_disabled") { 400 | replay_transaction_by_files( 401 | "real_ever_boc/ihr_fee_account_old.boc", 402 | "real_ever_boc/ihr_fee_account_new.boc", 403 | "real_ever_boc/ihr_fee_transaction.boc", 404 | "real_ever_boc/config.boc" 405 | ) 406 | } 407 | } 408 | 409 | #[ignore = "test for replay transaction by files"] 410 | #[test] 411 | fn test_replay_transaction_by_files() { 412 | log4rs::init_file("src/tests/log_cfg.yml", Default::default()).ok(); 413 | let mut account = Account::construct_from_file("real_ever_boc/account_old.boc").unwrap(); 414 | let message = Message::construct_from_file("real_ever_boc/message.boc").unwrap(); 415 | let key_block = Block::construct_from_file("real_ever_boc/config.boc").unwrap(); 416 | let config_params = key_block.read_extra().unwrap().read_custom().unwrap().unwrap().config().unwrap().clone(); 417 | let at = match message.int_header() { 418 | Some(hdr) => hdr.created_at.as_u32(), 419 | None => std::time::SystemTime::now().duration_since(std::time::SystemTime::UNIX_EPOCH).unwrap().as_secs() as u32 420 | }; 421 | let lt = account.last_tr_time().unwrap() + 100; 422 | 423 | let config = BlockchainConfig::with_config(config_params).unwrap(); 424 | let executor = OrdinaryTransactionExecutor::new(config); 425 | 426 | let block_lt = lt - lt % 1_000_000; 427 | let lt = Arc::new(AtomicU64::new(lt)); 428 | 429 | let params = ExecuteParams { 430 | state_libs: HashmapE::with_bit_len(32), 431 | block_unixtime: at, 432 | block_lt, 433 | last_tr_lt: lt, 434 | seed_block: UInt256::default(), 435 | debug: true, 436 | ..ExecuteParams::default() 437 | }; 438 | let tr = executor.execute_with_params(Some(&make_common(message)), &mut account, params).unwrap(); 439 | tr.write_to_file("real_ever_boc/transaction_my.boc").unwrap(); 440 | account.write_to_file("real_ever_boc/account_new_my.boc").unwrap(); 441 | } 442 | 443 | #[test] 444 | fn test_reserve_value_message() { 445 | let mut account = Account::construct_from_file("real_ever_boc/reserve_value_from_account.boc").unwrap(); 446 | let message = Message::construct_from_file("real_ever_boc/reserve_value_message.boc").unwrap(); 447 | let config = ConfigParams::construct_from_file("real_ever_boc/config.boc").unwrap(); 448 | let (at, lt) = message.at_and_lt().unwrap(); 449 | 450 | let our_transaction = try_replay_transaction(&mut account, Some(message), config, at, lt).unwrap(); 451 | assert_eq!(our_transaction.out_msgs.len().unwrap(), 1); 452 | } 453 | 454 | #[test] 455 | fn test_revert_action_phase() { 456 | let mut account = Account::construct_from_file("real_ever_boc/revert_action_account.boc").unwrap(); 457 | let transaction = Transaction::construct_from_file("real_ever_boc/revert_action_transaction.boc").unwrap(); 458 | let message = transaction.read_in_msg().unwrap().unwrap(); 459 | let config = ConfigParams::construct_from_file("real_ever_boc/config.boc").unwrap(); 460 | 461 | let lt: u64 = 241181000001; 462 | let at: u32 = 1626694478; 463 | 464 | let mut answer = account.clone(); 465 | if let CommonMessage::Std(message) = message { 466 | try_replay_transaction(&mut account, Some(message), config, at, lt).unwrap(); 467 | } 468 | answer.set_data(account.get_data().unwrap()); 469 | answer.set_balance(account.get_balance().unwrap().clone()); 470 | answer.set_last_tr_time(account.last_tr_time().unwrap_or(0)); 471 | assert_eq!(answer, account); 472 | } 473 | 474 | #[test] 475 | fn test_deploy_contract_with_lib() { 476 | let account = Account::construct_from_file("real_ever_boc/account_with_deploy_contract_with_lib_function.boc").unwrap(); 477 | let message = Message::construct_from_file("real_ever_boc/msg_with_deploy_contract_with_lib_function.boc").unwrap(); 478 | let config = ConfigParams::construct_from_file("real_ever_boc/config.boc").unwrap(); 479 | 480 | let lt: u64 = account.last_tr_time().unwrap() + 1; 481 | let at: u32 = 1668059542 - 10; 482 | 483 | let mut answer = account; 484 | let tx= try_replay_transaction(&mut answer, Some(message), config, at, lt).unwrap(); 485 | 486 | let deploy_message = tx.get_out_msg(0).unwrap().unwrap(); 487 | let mut new_account = Account::default(); 488 | 489 | let lt: u64 = 1; 490 | let at: u32 = 1668059542 - 9; 491 | 492 | let config = custom_config(None, Some(GlobalCapabilities::CapSetLibCode as u64)); 493 | let config = config.raw_config().clone(); 494 | 495 | if let CommonMessage::Std(msg) = deploy_message { 496 | try_replay_transaction(&mut new_account, Some(msg), config, at, lt).unwrap(); 497 | } 498 | assert_eq!(0, new_account.libraries().len().unwrap()); 499 | assert_eq!(AccountStatus::AccStateUninit, new_account.status()); 500 | } 501 | -------------------------------------------------------------------------------- /src/tick_tock_transaction.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use crate::{ 15 | blockchain_config::BlockchainConfig, ExecuteParams, TransactionExecutor, error::ExecutorError, 16 | ActionPhaseResult 17 | }; 18 | 19 | use std::sync::atomic::Ordering; 20 | use ever_block::{ 21 | Account, CurrencyCollection, Grams, Message, TrComputePhase, Transaction, GlobalCapabilities, 22 | TransactionDescr, TransactionDescrTickTock, TransactionTickTock, Serializable, CommonMessage 23 | }; 24 | use ever_block::{error, fail, Result, HashmapType, SliceData}; 25 | use ever_vm::{ 26 | boolean, int, 27 | stack::{integer::IntegerData, Stack, StackItem}, SmartContractInfo, 28 | }; 29 | 30 | pub struct TickTockTransactionExecutor { 31 | pub config: BlockchainConfig, 32 | pub tt: TransactionTickTock, 33 | } 34 | 35 | impl TickTockTransactionExecutor { 36 | pub fn new(config: BlockchainConfig, tt: TransactionTickTock) -> Self { 37 | Self { 38 | config, 39 | tt, 40 | } 41 | } 42 | } 43 | 44 | impl TransactionExecutor for TickTockTransactionExecutor { 45 | /// 46 | /// Create end execute tick or tock transaction for special account 47 | fn execute_with_params( 48 | &self, 49 | in_msg: Option<&CommonMessage>, 50 | account: &mut Account, 51 | params: ExecuteParams, 52 | ) -> Result { 53 | if in_msg.is_some() { 54 | fail!("Tick Tock transaction must not have input message") 55 | } 56 | let account_id = match account.get_id() { 57 | Some(addr) => addr, 58 | None => fail!("Tick Tock contract should have Standard address") 59 | }; 60 | match account.get_tick_tock() { 61 | Some(tt) => if tt.tock != self.tt.is_tock() && tt.tick != self.tt.is_tick() { 62 | fail!("wrong type of account's tick tock flag") 63 | } 64 | None => fail!("Account {:x} is not special account for tick tock", account_id) 65 | } 66 | let account_address = account.get_addr().cloned().unwrap_or_default(); 67 | log::debug!(target: "executor", "tick tock transation account {:x}", account_id); 68 | let mut acc_balance = account.balance().cloned().unwrap_or_default(); 69 | 70 | let is_masterchain = true; 71 | let is_special = true; 72 | let lt = std::cmp::max( 73 | account.last_tr_time().unwrap_or_default(), 74 | params.last_tr_lt.load(Ordering::Relaxed) 75 | ); 76 | let mut tr = self.create_transaction(account_id.clone()); 77 | tr.orig_status = account.status(); 78 | tr.set_logical_time(lt); 79 | tr.set_now(params.block_unixtime); 80 | account.set_last_paid(0); 81 | let due_before_storage = account.due_payment().map_or(0, |due| due.as_u128()); 82 | let storage = self.storage_phase( 83 | account, 84 | &mut acc_balance, 85 | &mut tr, 86 | is_masterchain, 87 | is_special, 88 | ).map_err(|e| error!(ExecutorError::TrExecutorError( 89 | format!("cannot create storage phase of a new transaction for \ 90 | smart contract for reason {}", e 91 | ) 92 | )))?; 93 | let mut storage_fee = storage.storage_fees_collected.as_u128(); 94 | if !self.config().has_capability(GlobalCapabilities::CapDuePaymentFix) { 95 | if let Some(due) = &storage.storage_fees_due { 96 | storage_fee += due.as_u128() 97 | } 98 | storage_fee -= due_before_storage; 99 | } 100 | let mut description = TransactionDescrTickTock { 101 | tt: self.tt.clone(), 102 | storage, 103 | ..TransactionDescrTickTock::default() 104 | }; 105 | 106 | let old_account = account.clone(); 107 | let original_acc_balance = acc_balance.clone(); 108 | 109 | let config_params = self.config().raw_config().config_params.data().cloned(); 110 | let mut smc_info = SmartContractInfo { 111 | capabilities: self.config().raw_config().capabilities(), 112 | myself: SliceData::load_builder(account_address.write_to_new_cell().unwrap_or_default())?, 113 | block_lt: params.block_lt, 114 | trans_lt: lt, 115 | unix_time: params.block_unixtime, 116 | seq_no: params.seq_no, 117 | balance: acc_balance.clone(), 118 | config_params, 119 | ..Default::default() 120 | }; 121 | smc_info.calc_rand_seed(params.seed_block.clone(), &account_address.address().get_bytestring(0)); 122 | let mut stack = Stack::new(); 123 | stack 124 | .push(int!(account.balance().map_or(0, |value| value.grams.as_u128()))) 125 | .push(StackItem::integer(IntegerData::from_unsigned_bytes_be(account_id.get_bytestring(0)))) 126 | .push(boolean!(self.tt.is_tock())) 127 | .push(int!(-2)); 128 | log::debug!(target: "executor", "compute_phase {}", lt); 129 | let (compute_ph, actions, new_data) = match self.compute_phase( 130 | None, 131 | account, 132 | &mut acc_balance, 133 | &CurrencyCollection::default(), 134 | smc_info, 135 | stack, 136 | storage_fee, 137 | is_masterchain, 138 | is_special, 139 | ¶ms, 140 | ) { 141 | Ok((compute_ph, actions, new_data)) => (compute_ph, actions, new_data), 142 | Err(e) => { 143 | log::debug!(target: "executor", "compute_phase error: {}", e); 144 | match e.downcast_ref::() { 145 | Some(ExecutorError::NoAcceptError(_, _)) => return Err(e), 146 | _ => fail!(ExecutorError::TrExecutorError(e.to_string())) 147 | } 148 | } 149 | }; 150 | let mut out_msgs = vec![]; 151 | description.compute_ph = compute_ph; 152 | description.action = match description.compute_ph { 153 | TrComputePhase::Vm(ref phase) => { 154 | tr.add_fee_grams(&phase.gas_fees)?; 155 | if phase.success { 156 | log::debug!(target: "executor", "compute_phase: TrComputePhase::Vm success"); 157 | log::debug!(target: "executor", "action_phase {}", lt); 158 | match self.action_phase_with_copyleft( 159 | &mut tr, 160 | account, 161 | &original_acc_balance, 162 | &mut acc_balance, 163 | &mut CurrencyCollection::default(), 164 | &Grams::zero(), 165 | actions.unwrap_or_default(), 166 | new_data, 167 | &account_address, 168 | is_special 169 | ) { 170 | Ok(ActionPhaseResult{phase, messages, .. }) => { 171 | out_msgs = messages; 172 | // ignore copyleft reward because account is special 173 | Some(phase) 174 | } 175 | Err(e) => fail!( 176 | ExecutorError::TrExecutorError( 177 | format!( 178 | "cannot create action phase of a new transaction \ 179 | for smart contract for reason {}", e 180 | ) 181 | ) 182 | ) 183 | } 184 | } else { 185 | log::debug!(target: "executor", "compute_phase: TrComputePhase::Vm failed"); 186 | None 187 | } 188 | } 189 | TrComputePhase::Skipped(ref skipped) => { 190 | log::debug!(target: "executor", 191 | "compute_phase: skipped: reason {:?}", skipped.reason); 192 | None 193 | } 194 | }; 195 | 196 | description.aborted = match description.action { 197 | Some(ref phase) => { 198 | log::debug!(target: "executor", 199 | "action_phase: present: success={}, err_code={}", phase.success, phase.result_code); 200 | !phase.success 201 | } 202 | None => { 203 | log::debug!(target: "executor", "action_phase: none"); 204 | true 205 | } 206 | }; 207 | 208 | log::debug!(target: "executor", "Desciption.aborted {}", description.aborted); 209 | tr.set_end_status(account.status()); 210 | account.set_balance(acc_balance); 211 | if description.aborted { 212 | *account = old_account; 213 | } 214 | params.last_tr_lt.store(lt, Ordering::Relaxed); 215 | let lt = self.add_messages(&mut tr, out_msgs, params.last_tr_lt)?; 216 | account.set_last_tr_time(lt); 217 | tr.write_description(&TransactionDescr::TickTock(description))?; 218 | Ok(tr) 219 | } 220 | fn ordinary_transaction(&self) -> bool { false } 221 | fn config(&self) -> &BlockchainConfig { &self.config } 222 | fn build_stack(&self, _in_msg: Option<&Message>, account: &Account) -> Result { 223 | let account_balance = account.balance().ok_or_else(|| error!("Can't get account balance."))?.grams.as_u128(); 224 | let account_id = account.get_id().ok_or_else(|| error!("Can't get account id."))?; 225 | let mut stack = Stack::new(); 226 | stack 227 | .push(int!(account_balance)) 228 | .push(StackItem::integer(IntegerData::from_unsigned_bytes_be(account_id.get_bytestring(0)))) 229 | .push(boolean!(self.tt.is_tock())) 230 | .push(int!(-2)); 231 | Ok(stack) 232 | } 233 | } 234 | 235 | #[cfg(test)] 236 | #[path = "tests/test_tick_tock_transaction.rs"] 237 | mod tests; 238 | -------------------------------------------------------------------------------- /src/vmsetup.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2019-2023 EverX. All Rights Reserved. 3 | * 4 | * Licensed under the SOFTWARE EVALUATION License (the "License"); you may not use 5 | * this file except in compliance with the License. 6 | * 7 | * Unless required by applicable law or agreed to in writing, software 8 | * distributed under the License is distributed on an "AS IS" BASIS, 9 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | * See the License for the specific EVERX DEV software governing permissions and 11 | * limitations under the License. 12 | */ 13 | 14 | use ever_block::GlobalCapabilities; 15 | use ever_block::{Cell, HashmapE, SliceData, Result}; 16 | use ever_vm::{ 17 | executor::{Engine, gas::gas_state::Gas}, smart_contract_info::SmartContractInfo, 18 | stack::{Stack, StackItem, savelist::SaveList} 19 | }; 20 | use crate::BlockchainConfig; 21 | 22 | pub struct VMSetupContext { 23 | pub capabilities: u64, 24 | pub block_version: u32, 25 | #[cfg(feature = "signature_with_id")] 26 | pub signature_id: i32, 27 | } 28 | 29 | /// Builder for virtual machine engine. Initialises registers, 30 | /// stack and code of VM engine. Returns initialized instance of TVM. 31 | pub struct VMSetup { 32 | vm: Engine, 33 | code: SliceData, 34 | ctrls: SaveList, 35 | stack: Option, 36 | gas: Option, 37 | libraries: Vec, 38 | ctx: VMSetupContext, 39 | } 40 | 41 | impl VMSetup { 42 | 43 | /// Creates new instance of VMSetup with contract code. 44 | /// Initializes some registers of TVM with predefined values. 45 | pub fn with_context(code: SliceData, ctx: VMSetupContext) -> Self { 46 | VMSetup { 47 | vm: Engine::with_capabilities(ctx.capabilities), 48 | code, 49 | ctrls: SaveList::new(), 50 | stack: None, 51 | gas: Some(Gas::empty()), 52 | libraries: vec![], 53 | ctx, 54 | } 55 | } 56 | 57 | pub fn set_smart_contract_info(mut self, sci: SmartContractInfo) -> Result { 58 | debug_assert_ne!(sci.capabilities, 0); 59 | let mut sci = sci.into_temp_data_item(); 60 | self.ctrls.put(7, &mut sci)?; 61 | Ok(self) 62 | } 63 | 64 | /// Sets SmartContractInfo for TVM register c7 65 | #[deprecated] 66 | pub fn set_contract_info_with_config( 67 | self, 68 | mut sci: SmartContractInfo, 69 | config: &BlockchainConfig 70 | ) -> Result { 71 | sci.capabilities |= config.raw_config().capabilities(); 72 | self.set_smart_contract_info(sci) 73 | } 74 | 75 | /// Sets SmartContractInfo for TVM register c7 76 | #[deprecated] 77 | pub fn set_contract_info( 78 | self, 79 | mut sci: SmartContractInfo, 80 | with_init_code_hash: bool 81 | ) -> Result { 82 | if with_init_code_hash { 83 | sci.capabilities |= GlobalCapabilities::CapInitCodeHash as u64; 84 | } 85 | self.set_smart_contract_info(sci) 86 | } 87 | 88 | /// Sets persistent data for contract in register c4 89 | pub fn set_data(mut self, data: Cell) -> Result { 90 | self.ctrls.put(4, &mut StackItem::Cell(data))?; 91 | Ok(self) 92 | } 93 | 94 | /// Sets initial stack for TVM 95 | pub fn set_stack(mut self, stack: Stack) -> VMSetup { 96 | self.stack = Some(stack); 97 | self 98 | } 99 | 100 | /// Sets gas for TVM 101 | pub fn set_gas(mut self, gas: Gas) -> VMSetup { 102 | self.gas = Some(gas); 103 | self 104 | } 105 | 106 | /// Sets libraries for TVM 107 | pub fn set_libraries(mut self, libraries: Vec) -> VMSetup { 108 | self.libraries = libraries; 109 | self 110 | } 111 | 112 | /// Sets trace flag to TVM for printing stack and commands 113 | pub fn set_debug(mut self, enable: bool) -> VMSetup { 114 | if enable { 115 | self.vm.set_trace(Engine::TRACE_ALL); 116 | } else { 117 | self.vm.set_trace(0); 118 | } 119 | self 120 | } 121 | 122 | /// Creates new instance of TVM with defined stack, registers and code. 123 | pub fn create(self) -> Engine { 124 | if cfg!(debug_assertions) { 125 | // account balance is duplicated in stack and in c7 - so check 126 | let balance_in_smc = self 127 | .ctrls 128 | .get(7) 129 | .unwrap() 130 | .as_tuple() 131 | .unwrap()[0] 132 | .as_tuple() 133 | .unwrap()[7] 134 | .as_tuple() 135 | .unwrap()[0] 136 | .as_integer() 137 | .unwrap(); 138 | let stack_depth = self.stack.as_ref().unwrap().depth(); 139 | let balance_in_stack = self 140 | .stack 141 | .as_ref() 142 | .unwrap() 143 | .get(stack_depth - 1) 144 | .as_integer() 145 | .unwrap(); 146 | assert_eq!(balance_in_smc, balance_in_stack); 147 | } 148 | let mut vm = self.vm.setup_with_libraries( 149 | self.code, 150 | Some(self.ctrls), 151 | self.stack, 152 | self.gas, 153 | self.libraries 154 | ); 155 | vm.set_block_version(self.ctx.block_version); 156 | #[cfg(feature = "signature_with_id")] 157 | vm.set_signature_id(self.ctx.signature_id); 158 | vm 159 | } 160 | } 161 | --------------------------------------------------------------------------------