├── .github └── workflows │ └── master.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── abi-proc ├── Cargo.toml └── src │ ├── derive_from_abi.rs │ ├── derive_into_abi.rs │ ├── derive_with_abi_type.rs │ ├── internals │ ├── container.rs │ ├── context.rs │ ├── field.rs │ └── mod.rs │ └── lib.rs ├── benches ├── big_cell.rs ├── boc.rs ├── callgrind_boc.rs ├── callgrind_dict.rs ├── callgrind_dict_from_slice.rs ├── callgrind_slice_uniform.rs ├── callgrind_usage_cell.rs ├── data │ ├── external_message │ ├── internal_message_empty │ ├── internal_message_with_body │ ├── internal_message_with_deploy │ ├── masterchain_block │ ├── masterchain_block_proof │ ├── masterchain_key_block │ ├── shard_block_empty │ ├── shard_block_proof │ └── shard_block_with_messages ├── dict.rs ├── dict_from_slice.rs ├── dict_modify.rs ├── dict_modify_aug.rs ├── mine.rs ├── slice_uniform.rs └── usage_cell.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ ├── base64_addr.rs │ ├── boc_decode.rs │ ├── boc_decode_encode.rs │ ├── boc_decode_pair.rs │ ├── boc_dict.rs │ ├── boc_encode.rs │ ├── boc_message.rs │ ├── common.rs │ ├── dict_modify.rs │ ├── dict_modify_aug.rs │ └── dict_modify_aug_with_cells.rs ├── proc ├── Cargo.toml └── src │ ├── bound.rs │ ├── derive_load.rs │ ├── derive_store.rs │ ├── internals │ ├── ast.rs │ ├── attr.rs │ ├── ctxt.rs │ ├── mod.rs │ └── symbol.rs │ └── lib.rs ├── rustfmt.toml └── src ├── abi ├── contract.rs ├── error.rs ├── mod.rs ├── signature.rs ├── tests │ ├── depool.abi.json │ └── mod.rs ├── traits.rs ├── ty.rs └── value │ ├── de.rs │ ├── mod.rs │ └── ser.rs ├── arbitrary.rs ├── boc ├── de.rs ├── mod.rs ├── ser.rs ├── serde.rs └── tests │ ├── block.boc.2 │ └── mod.rs ├── cell ├── builder.rs ├── cell_context.rs ├── cell_impl │ ├── mod.rs │ ├── rc.rs │ └── sync.rs ├── lazy.rs ├── mod.rs ├── slice.rs └── usage_tree.rs ├── crc.rs ├── dict ├── aug.rs ├── mod.rs ├── ops │ ├── build.rs │ ├── find.rs │ ├── get.rs │ ├── insert.rs │ ├── modify.rs │ ├── remove.rs │ └── split_merge.rs ├── raw.rs ├── tests │ └── account_blocks_aug_dict.boc └── typed.rs ├── error.rs ├── lib.rs ├── merkle ├── mod.rs ├── proof.rs ├── pruned_branch.rs ├── tests │ ├── mod.rs │ └── simple_proof.boc └── update.rs ├── models ├── account │ └── mod.rs ├── block │ ├── block_extra.rs │ ├── block_id.rs │ ├── block_proof.rs │ ├── mod.rs │ ├── shard_hashes.rs │ └── tests │ │ ├── empty_shard_block.boc │ │ ├── mc_block_proof.boc │ │ ├── mc_block_with_shards.boc │ │ ├── mc_key_block.boc │ │ ├── mc_simple_block.boc │ │ ├── mod.rs │ │ ├── shard_block_proof.boc │ │ └── simple_shard_block.boc ├── config │ ├── mod.rs │ ├── params.rs │ └── tests │ │ ├── mod.rs │ │ ├── new_config.boc │ │ ├── old_config.boc │ │ ├── simple_config.boc │ │ └── test_state_2_master.boc ├── currency.rs ├── global_version.rs ├── message │ ├── address.rs │ ├── envelope.rs │ ├── in_message.rs │ ├── mod.rs │ ├── out_message.rs │ └── tests │ │ ├── empty_internal_message.boc │ │ ├── external_message.boc │ │ ├── external_message_body.boc │ │ ├── external_out_message.boc │ │ ├── internal_message_body.boc │ │ ├── internal_message_with_body.boc │ │ ├── internal_message_with_deploy.boc │ │ ├── internal_message_with_deploy_body.boc │ │ ├── internal_message_with_deploy_special.boc │ │ ├── internal_message_with_deploy_state_init.boc │ │ └── mod.rs ├── mod.rs ├── shard │ ├── mod.rs │ ├── shard_accounts.rs │ ├── shard_extra.rs │ └── tests │ │ ├── everscale_zerostate.boc │ │ ├── first_block.boc │ │ ├── mod.rs │ │ └── new_zerostate.boc ├── transaction │ ├── mod.rs │ ├── phases.rs │ └── tests │ │ ├── mod.rs │ │ ├── ordinary_tx_bounce_no_funds.boc │ │ ├── ordinary_tx_bounce_no_state.boc │ │ ├── ordinary_tx_recursive.boc │ │ ├── ordinary_tx_with_external.boc │ │ ├── ordinary_tx_with_outgoing.boc │ │ ├── ordinary_tx_without_outgoing.boc │ │ ├── tick_tx.boc │ │ └── tock_tx.boc └── vm │ ├── mod.rs │ └── out_actions.rs ├── num ├── mod.rs └── varuint248.rs ├── prelude.rs └── util.rs /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: master 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | check: 10 | name: Check 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@v2 15 | 16 | - name: Install stable toolchain 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | profile: minimal 20 | toolchain: stable 21 | override: true 22 | 23 | - name: Run cargo check 24 | uses: actions-rs/cargo@v1 25 | with: 26 | command: check 27 | args: --features base64,serde,models,sync,abi 28 | 29 | test: 30 | name: Test Suite 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Checkout sources 34 | uses: actions/checkout@v2 35 | 36 | - name: Install stable toolchain 37 | uses: actions-rs/toolchain@v1 38 | with: 39 | profile: minimal 40 | toolchain: stable 41 | override: true 42 | 43 | - name: Run cargo test 44 | uses: actions-rs/cargo@v1 45 | with: 46 | command: test 47 | args: --features base64,serde,models,sync,abi,rayon 48 | 49 | lints: 50 | name: Lints 51 | runs-on: ubuntu-latest 52 | steps: 53 | - name: Checkout sources 54 | uses: actions/checkout@v2 55 | 56 | - name: Install stable toolchain 57 | uses: actions-rs/toolchain@v1 58 | with: 59 | profile: minimal 60 | toolchain: stable 61 | override: true 62 | components: clippy 63 | 64 | - name: Install nightly toolchain 65 | uses: actions-rs/toolchain@v1 66 | with: 67 | profile: minimal 68 | toolchain: nightly 69 | components: rustfmt 70 | 71 | - name: Run cargo fmt 72 | uses: actions-rs/cargo@v1 73 | with: 74 | command: fmt 75 | toolchain: nightly 76 | args: --all -- --check 77 | 78 | - name: Run cargo clippy 79 | uses: actions-rs/cargo@v1 80 | with: 81 | command: clippy 82 | args: --features base64,serde,models,sync,abi -- -D warnings 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.vscode 3 | /target 4 | **/Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "everscale-types" 3 | description = "A set of primitive types and utilities for the Everscale blockchain." 4 | authors = ["Ivan Kalinin "] 5 | repository = "https://github.com/broxus/everscale-types" 6 | version = "0.1.2" 7 | edition = "2021" 8 | rust-version = "1.77" 9 | include = ["src/**/*.rs", "benches/**/*.rs", "LICENSE-*", "README.md"] 10 | license = "MIT OR Apache-2.0" 11 | 12 | [[bench]] 13 | name = "boc" 14 | harness = false 15 | 16 | [[bench]] 17 | name = "mine" 18 | harness = false 19 | 20 | [[bench]] 21 | name = "dict_from_slice" 22 | harness = false 23 | 24 | [[bench]] 25 | name = "dict_modify" 26 | harness = false 27 | 28 | [[bench]] 29 | name = "dict_modify_aug" 30 | harness = false 31 | 32 | [[bench]] 33 | name = "dict" 34 | harness = false 35 | 36 | [[bench]] 37 | name = "slice_uniform" 38 | harness = false 39 | 40 | [[bench]] 41 | name = "usage_cell" 42 | harness = false 43 | 44 | # callgrind benchmarks 45 | 46 | [[bench]] 47 | name = "callgrind_boc" 48 | harness = false 49 | 50 | [[bench]] 51 | name = "callgrind_dict_from_slice" 52 | harness = false 53 | 54 | [[bench]] 55 | name = "callgrind_dict" 56 | harness = false 57 | 58 | [[bench]] 59 | name = "callgrind_slice_uniform" 60 | harness = false 61 | 62 | [[bench]] 63 | name = "callgrind_usage_cell" 64 | harness = false 65 | 66 | [[bench]] 67 | name = "big_cell" 68 | harness = false 69 | 70 | [workspace] 71 | members = ["abi-proc", "proc", "fuzz"] 72 | 73 | [dependencies] 74 | ahash = "0.8.11" 75 | anyhow = { version = "1.0", optional = true } 76 | arbitrary = { version = "1", features = ["derive"], optional = true } 77 | base64 = { version = "0.22", optional = true } 78 | bitflags = "2.3" 79 | blake3 = { version = "1.5", optional = true } 80 | bytes = { version = "1.4", optional = true } 81 | crc32c = "0.6.8" 82 | ed25519-dalek = { version = "2.1.1", optional = true } 83 | everscale-crypto = { version = "0.3", features = ["tl-proto"], optional = true } 84 | hex = "0.4" 85 | num-bigint = { version = "0.4", optional = true } 86 | num-traits = { version = "0.2", optional = true } 87 | rand = { version = "0.8", optional = true } 88 | rayon = { version = "1.10", optional = true } 89 | scc = { version = "2.1", optional = true } 90 | serde = { version = "1", features = ["derive"], optional = true } 91 | sha2 = "0.10" 92 | smallvec = { version = "1.9", features = ["union"] } 93 | thiserror = "2.0" 94 | tl-proto = { version = "0.5.3", optional = true } 95 | typeid = "1.0" 96 | 97 | everscale-types-proc = { version = "=0.1.5", path = "proc" } 98 | everscale-types-abi-proc = { version = "=0.1.0", path = "abi-proc", optional = true } 99 | 100 | [dev-dependencies] 101 | anyhow = "1.0" 102 | base64 = "0.22" 103 | criterion = "0.5" 104 | rand = "0.8" 105 | rand_xorshift = "0.3" 106 | serde = { version = "1", features = ["derive"] } 107 | serde_json = "1" 108 | iai-callgrind = "0.14" 109 | paste = "1.0.15" 110 | 111 | [features] 112 | default = ["base64", "serde", "models", "sync"] 113 | sync = ["dep:scc"] 114 | stats = [] 115 | serde = ["dep:serde", "base64"] 116 | rand = ["dep:rand"] 117 | models = ["dep:everscale-crypto", "dep:tl-proto"] 118 | blake3 = ["dep:blake3"] 119 | rayon = ["dep:rayon", "blake3?/rayon"] 120 | arbitrary = ["dep:arbitrary"] 121 | bigint = ["dep:num-bigint", "dep:num-traits"] 122 | abi = [ 123 | "dep:anyhow", 124 | "dep:bytes", 125 | "dep:ed25519-dalek", 126 | "bigint", 127 | "dep:serde", 128 | "models", 129 | "dep:everscale-types-abi-proc", 130 | ] 131 | tycho = [] 132 | 133 | [profile.release] 134 | debug = true 135 | 136 | [profile.dev.package.hex] 137 | opt-level = 3 138 | [profile.dev.package.base64] 139 | opt-level = 3 140 | [profile.dev.package.rand] 141 | opt-level = 3 142 | [profile.dev.package.sha2] 143 | opt-level = 3 144 | [profile.dev.package.everscale-crypto] 145 | opt-level = 3 146 | [profile.dev.package.curve25519-dalek] 147 | opt-level = 3 148 | [profile.dev.package.ed25519] 149 | opt-level = 3 150 | [profile.dev.package."*"] 151 | opt-level = 1 152 | 153 | [package.metadata.docs.rs] 154 | features = ["base64", "serde", "models", "sync", "stats", "abi"] 155 | 156 | [lints.rust] 157 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] } 158 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Everscale types   [![crates-io-batch]][crates-io-link] [![docs-badge]][docs-url] [![rust-version-badge]][rust-version-link] [![workflow-badge]][workflow-link] 2 | 3 | [crates-io-batch]: https://img.shields.io/crates/v/everscale-types.svg 4 | 5 | [crates-io-link]: https://crates.io/crates/everscale-types 6 | 7 | [docs-badge]: https://docs.rs/everscale-types/badge.svg 8 | 9 | [docs-url]: https://docs.rs/everscale-types 10 | 11 | [rust-version-badge]: https://img.shields.io/badge/rustc-1.65+-lightgray.svg 12 | 13 | [rust-version-link]: https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html 14 | 15 | [workflow-badge]: https://img.shields.io/github/actions/workflow/status/broxus/everscale-types/master.yml?branch=master 16 | 17 | [workflow-link]: https://github.com/broxus/everscale-types/actions?query=workflow%3Amaster 18 | 19 | > Status: WIP 20 | 21 | ## About 22 | 23 | A set of primitive types and utilities for the Everscale blockchain. 24 | 25 | Heavily inspired by [`ton-labs-types`](https://github.com/tonlabs/ton-labs-types), 26 | but with much more emphasis on speed. 27 | 28 | ## Basic usage 29 | 30 | Get `Cell` from `Vec` representation of bytes 31 | ```rust 32 | use everscale_types::boc::Boc; 33 | 34 | let cell: Cell = Boc::decode(bytes)?; 35 | ``` 36 | 37 | Encode any model e.g.`MerkleProof` to `base64` BOC representation and vice versa 38 | ```rust 39 | use everscale_types::boc::BocRepr; 40 | 41 | let cell = MerkleProof::create_for_cell(cell.as_ref(), EMPTY_CELL_HASH) 42 | .build() 43 | .unwrap(); 44 | 45 | let encoded = BocRepr::encode_base64(&cell).unwrap(); 46 | 47 | let decoded = Boc::decode_base64(encoded)?.as_ref().parse::()?: 48 | ``` 49 | 50 | Get specific everscale type from `Cell` 51 | ```rust 52 | use everscale_types::models::BlockProof; 53 | 54 | let proof: BlockProof = cell.parse::()?; 55 | ``` 56 | Same usage for virtualized cell 57 | ```rust 58 | use everscale_types::prelude::DynCell; 59 | use everscale_types::models::Block; 60 | 61 | let virt_cell: &DynCell = cell.virtualize(); 62 | let block = virt_cell.parse::()?; 63 | ``` 64 | 65 | You can also use `CellBuilder` to create any `Cell` 66 | ```rust 67 | let mut builder = CellBuilder::new(); 68 | builder.store_bit_one()?; 69 | builder.store_u32(100u32)? 70 | builder.store_slice(slice)?; 71 | builder.store_raw(&[0xdd, 0x55], 10)?; 72 | 73 | // store references to another cells 74 | builder.store_reference(cell)?; 75 | builder.store_reference(another_cell)?; 76 | 77 | let final_cell = builder.build()?; 78 | ``` 79 | 80 | ## Development 81 | 82 | ### How to bench 83 | 84 | ```bash 85 | cargo bench boc 86 | cargo bench dict 87 | ``` 88 | 89 | ### How to miri check 90 | 91 | ```bash 92 | # Add Miri component 93 | rustup +nightly component add miri 94 | 95 | # Run all tests with Miri 96 | cargo +nightly miri test 97 | ``` 98 | 99 | ### How to fuzz 100 | 101 | ```bash 102 | # Install fuzzer 103 | cargo install cargo-fuzz 104 | 105 | # Run any of the fuzzer targets 106 | cargo +nightly fuzz run boc_decode -j 12 107 | cargo +nightly fuzz run boc_decode_encode -j 12 108 | cargo +nightly fuzz run boc_decode_pair -j 12 109 | cargo +nightly fuzz run boc_dict -j 12 110 | cargo +nightly fuzz run boc_message -j 12 111 | ``` 112 | 113 | ## Contributing 114 | 115 | We welcome contributions to the project! If you notice any issues or errors, feel free to open an issue or submit a pull request. 116 | 117 | ## License 118 | 119 | Licensed under either of 120 | 121 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or ) 122 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 123 | 124 | at your option. 125 | -------------------------------------------------------------------------------- /abi-proc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "everscale-types-abi-proc" 3 | description = "Proc-macro abi helpers for everscale-types" 4 | authors = ["Stanislav Eliseev Result> { 11 | let ctx = Ctxt::new(); 12 | let container_opt = Container::from_ast(&ctx, &input); 13 | 14 | let Some(container) = container_opt else { 15 | return Err(ctx.check().unwrap_err()); 16 | }; 17 | 18 | let Some((named, struct_fields)) = construct_from_abi(&container.data.fields, &ctx) else { 19 | return Err(ctx.check().unwrap_err()); 20 | }; 21 | 22 | let generics = container::with_bound( 23 | &container.data.fields, 24 | &container.generics, 25 | &syn::parse_quote!(::everscale_types::abi::FromAbi), 26 | ); 27 | 28 | let ident = container.name_ident; 29 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 30 | 31 | let slf = if named { 32 | quote!(Ok(Self { #(#struct_fields),* })) 33 | } else { 34 | quote!(Ok(Self ( #(#struct_fields),* ))) 35 | }; 36 | 37 | let token_stream = quote! { 38 | impl #impl_generics ::everscale_types::abi::FromAbi for #ident #ty_generics #where_clause { 39 | fn from_abi(value: ::everscale_types::abi::AbiValue) -> everscale_types::abi::__export::anyhow::Result { 40 | let ::everscale_types::abi::AbiValue::Tuple(inner) = value else { 41 | return Err(everscale_types::abi::__export::anyhow::anyhow!( 42 | "expected tuple while parsing AbiValue" 43 | )); 44 | }; 45 | let mut __iter = inner.into_iter(); 46 | #slf 47 | } 48 | } 49 | }; 50 | 51 | Ok(token_stream) 52 | } 53 | 54 | pub fn construct_from_abi(fields: &syn::Fields, ctx: &Ctxt) -> Option<(bool, Vec)> { 55 | let mut struct_fields = Vec::new(); 56 | 57 | let mut named = true; 58 | for (index, field) in fields.iter().enumerate() { 59 | let struct_field = match &field.ident { 60 | Some(named) => StructField::named(named), 61 | None => { 62 | named = false; 63 | StructField::unnamed(index) 64 | } 65 | }; 66 | 67 | let attributes = extract_field_attributes(ctx, field.attrs.as_slice()); 68 | if !attributes.extracted { 69 | return None; 70 | } 71 | 72 | let token = construct_from_abi_inner(&struct_field, &attributes, named); 73 | struct_fields.push(token); 74 | } 75 | 76 | Some((named, struct_fields)) 77 | } 78 | 79 | fn construct_from_abi_inner( 80 | struct_field: &StructField, 81 | attrs: &FieldAttributes, 82 | named: bool, 83 | ) -> TokenStream { 84 | let field_name = &struct_field.field_name; 85 | 86 | let extractor = if let Some(path) = &attrs.with_handlers.from_abi_handler { 87 | quote! { #path } 88 | } else if let Some(path) = &attrs.mod_handler { 89 | quote! { #path::from_abi } 90 | } else { 91 | quote! { <_ as ::everscale_types::abi::FromAbi>::from_abi } 92 | }; 93 | 94 | let base = quote! { 95 | match __iter.next() { 96 | Some(__item) => #extractor(__item.value)?, 97 | None => return Err(everscale_types::abi::__export::anyhow::anyhow!( 98 | "not enough tuple items while parsing AbiValue", 99 | )), 100 | } 101 | }; 102 | 103 | if named { 104 | quote! { 105 | #field_name: #base 106 | } 107 | } else { 108 | base 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /abi-proc/src/derive_into_abi.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use syn::{Error, Fields}; 4 | 5 | use crate::internals::container; 6 | use crate::internals::container::Container; 7 | use crate::internals::context::Ctxt; 8 | use crate::internals::field::{extract_field_attributes, FieldAttributes, StructField}; 9 | 10 | pub fn impl_derive(input: syn::DeriveInput) -> Result> { 11 | let ctx = Ctxt::new(); 12 | let container_opt = Container::from_ast(&ctx, &input); 13 | let Some(container) = container_opt else { 14 | return Err(ctx.check().unwrap_err()); 15 | }; 16 | let Some((into_fields, as_fields)) = construct_into_abi(&container.data.fields, &ctx) else { 17 | return Err(ctx.check().unwrap_err()); 18 | }; 19 | 20 | let into_abi_items = quote! { 21 | vec![#(#into_fields),*] 22 | }; 23 | let as_abi_items = quote! { 24 | vec![#(#as_fields),*] 25 | }; 26 | 27 | let generics = container::with_bound( 28 | &container.data.fields, 29 | &container.generics, 30 | &syn::parse_quote!(::everscale_types::abi::IntoAbi), 31 | ); 32 | 33 | let ident = container.name_ident; 34 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 35 | 36 | let token_stream = quote! { 37 | impl #impl_generics ::everscale_types::abi::IntoAbi for #ident #ty_generics #where_clause { 38 | fn into_abi(self) -> ::everscale_types::abi::AbiValue 39 | where 40 | Self: Sized, 41 | { 42 | ::everscale_types::abi::AbiValue::Tuple(#into_abi_items) 43 | } 44 | 45 | fn as_abi(&self) -> ::everscale_types::abi::AbiValue { 46 | ::everscale_types::abi::AbiValue::Tuple(#as_abi_items) 47 | } 48 | } 49 | }; 50 | 51 | Ok(token_stream) 52 | } 53 | 54 | fn construct_into_abi(fields: &Fields, ctx: &Ctxt) -> Option<(Vec, Vec)> { 55 | let mut into_fields = Vec::new(); 56 | let mut as_fields = Vec::new(); 57 | 58 | for (index, field) in fields.iter().enumerate() { 59 | let struct_field = match &field.ident { 60 | Some(named) => StructField::named(named), 61 | None => StructField::unnamed(index), 62 | }; 63 | 64 | let attributes = extract_field_attributes(ctx, field.attrs.as_slice()); 65 | if !attributes.extracted { 66 | return None; 67 | } 68 | 69 | let (into_abi, as_abi) = construct_named_abi_value_inner(&struct_field, &attributes); 70 | into_fields.push(into_abi); 71 | as_fields.push(as_abi); 72 | } 73 | 74 | Some((into_fields, as_fields)) 75 | } 76 | 77 | // Returns `into_abi` and `as_abi` items. 78 | fn construct_named_abi_value_inner( 79 | struct_field: &StructField, 80 | custom_attributes: &FieldAttributes, 81 | ) -> (TokenStream, TokenStream) { 82 | let name = match &custom_attributes.custom_name { 83 | Some(custom) => custom, 84 | None => &struct_field.name_ident, 85 | } 86 | .to_string(); 87 | 88 | let field_name = &struct_field.field_name; 89 | 90 | let (into_abi, as_abi) = if let Some(path) = &custom_attributes.with_handlers.into_abi_handler { 91 | let path_as_abi = custom_attributes 92 | .with_handlers 93 | .as_abi_handler 94 | .as_ref() 95 | .expect("must be checked"); 96 | 97 | ( 98 | quote! { #path(self.#field_name) }, 99 | quote! { #path_as_abi(&self.#field_name) }, 100 | ) 101 | } else if let Some(path) = &custom_attributes.with_handlers.as_abi_handler { 102 | ( 103 | quote! { #path(&self.#field_name) }, 104 | quote! { #path(&self.#field_name) }, 105 | ) 106 | } else if let Some(path) = &custom_attributes.mod_handler { 107 | ( 108 | quote! { #path::into_abi(self.#field_name) }, 109 | quote! { #path::as_abi(&self.#field_name) }, 110 | ) 111 | } else { 112 | let path = quote! { ::everscale_types::abi::IntoAbi }; 113 | ( 114 | quote! { #path::into_abi(self.#field_name) }, 115 | quote! { #path::as_abi(&self.#field_name) }, 116 | ) 117 | }; 118 | 119 | ( 120 | quote! { #into_abi.named(#name) }, 121 | quote! { #as_abi.named(#name) }, 122 | ) 123 | } 124 | -------------------------------------------------------------------------------- /abi-proc/src/derive_with_abi_type.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use syn::Fields; 4 | 5 | use crate::internals::container; 6 | use crate::internals::container::Container; 7 | use crate::internals::context::Ctxt; 8 | use crate::internals::field::{extract_field_attributes, FieldAttributes, StructField}; 9 | 10 | pub fn impl_derive(input: syn::DeriveInput) -> Result> { 11 | let ctx = Ctxt::new(); 12 | let Some(container) = Container::from_ast(&ctx, &input) else { 13 | return Err(ctx.check().unwrap_err()); 14 | }; 15 | 16 | let Some(tuple) = construct_with_abi_type(&container.data.fields, &ctx) else { 17 | return Err(ctx.check().unwrap_err()); 18 | }; 19 | 20 | let generics = container::with_bound( 21 | &container.data.fields, 22 | &container.generics, 23 | &syn::parse_quote!(::everscale_types::abi::WithAbiType), 24 | ); 25 | 26 | let ident = &input.ident; 27 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 28 | 29 | let abi_values = quote! { 30 | vec![#(#tuple),*] 31 | }; 32 | 33 | let token_stream = quote! { 34 | impl #impl_generics ::everscale_types::abi::WithAbiType for #ident #ty_generics #where_clause { 35 | fn abi_type() -> ::everscale_types::abi::AbiType { 36 | ::everscale_types::abi::AbiType::Tuple(::std::sync::Arc::from(#abi_values)) 37 | } 38 | } 39 | }; 40 | 41 | Ok(token_stream) 42 | } 43 | 44 | fn construct_with_abi_type(fields: &Fields, ctx: &Ctxt) -> Option> { 45 | let mut tuple = Vec::new(); 46 | 47 | for (index, field) in fields.iter().enumerate() { 48 | let struct_field = match &field.ident { 49 | Some(named) => StructField::named(named), 50 | None => StructField::unnamed(index), 51 | }; 52 | 53 | let attributes = extract_field_attributes(ctx, field.attrs.as_slice()); 54 | if !attributes.extracted { 55 | return None; 56 | } 57 | 58 | let token = construct_with_abi_type_inner(&struct_field, &field.ty, attributes); 59 | tuple.push(token); 60 | } 61 | 62 | Some(tuple) 63 | } 64 | 65 | fn construct_with_abi_type_inner( 66 | struct_field: &StructField, 67 | ty: &syn::Type, 68 | attrs: FieldAttributes, 69 | ) -> TokenStream { 70 | let name = match &attrs.custom_name { 71 | Some(custom) => custom, 72 | None => &struct_field.name_ident, 73 | } 74 | .to_string(); 75 | 76 | let extractor = if let Some(path) = &attrs.with_handlers.abi_type_handler { 77 | quote! { #path } 78 | } else if let Some(path) = &attrs.mod_handler { 79 | quote! { #path::abi_type } 80 | } else { 81 | quote! { <#ty as ::everscale_types::abi::WithAbiType>::abi_type } 82 | }; 83 | 84 | quote! { #extractor().named(#name) } 85 | } 86 | -------------------------------------------------------------------------------- /abi-proc/src/internals/container.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use syn::punctuated::Pair; 4 | use syn::visit::Visit; 5 | use syn::{visit, DataStruct, Fields}; 6 | 7 | use crate::internals::context::Ctxt; 8 | pub struct Container<'a> { 9 | pub name_ident: &'a syn::Ident, 10 | pub generics: syn::Generics, 11 | pub data: DataStruct, 12 | } 13 | 14 | impl<'a> Container<'a> { 15 | pub fn from_ast(ctx: &'a Ctxt, input: &'a syn::DeriveInput) -> Option { 16 | let data = match &input.data { 17 | syn::Data::Struct(data_struct) => data_struct, 18 | syn::Data::Enum(_) => { 19 | ctx.error_spanned_by(input, "FromAbi doesn't support derive for enums"); 20 | return None; 21 | } 22 | syn::Data::Union(_) => { 23 | ctx.error_spanned_by(input, "FromAbi doesn't support derive for unions"); 24 | return None; 25 | } 26 | }; 27 | 28 | let generics = without_default_generic(&input.generics); 29 | 30 | let name_ident = &input.ident; 31 | 32 | Some(Self { 33 | name_ident, 34 | generics, 35 | data: data.clone(), 36 | }) 37 | } 38 | } 39 | 40 | pub fn with_bound(fields: &Fields, generics: &syn::Generics, bound: &syn::Path) -> syn::Generics { 41 | struct FindTyParams<'ast> { 42 | all_type_params: HashSet, 43 | relevant_type_params: HashSet, 44 | associated_type_usage: Vec<&'ast syn::TypePath>, 45 | } 46 | 47 | impl<'ast> Visit<'ast> for FindTyParams<'ast> { 48 | fn visit_field(&mut self, field: &'ast syn::Field) { 49 | if let syn::Type::Path(ty) = ungroup(&field.ty) { 50 | if let Some(Pair::Punctuated(t, _)) = ty.path.segments.pairs().next() { 51 | if self.all_type_params.contains(&t.ident) { 52 | self.associated_type_usage.push(ty); 53 | } 54 | } 55 | } 56 | self.visit_type(&field.ty); 57 | } 58 | 59 | fn visit_macro(&mut self, _mac: &'ast syn::Macro) {} 60 | 61 | fn visit_path(&mut self, path: &'ast syn::Path) { 62 | if let Some(seg) = path.segments.last() { 63 | if seg.ident == "PhantomData" { 64 | return; 65 | } 66 | } 67 | if path.leading_colon.is_none() && path.segments.len() == 1 { 68 | let id = &path.segments[0].ident; 69 | if self.all_type_params.contains(id) { 70 | self.relevant_type_params.insert(id.clone()); 71 | } 72 | } 73 | visit::visit_path(self, path); 74 | } 75 | } 76 | 77 | let all_type_params = generics 78 | .type_params() 79 | .map(|param| param.ident.clone()) 80 | .collect(); 81 | 82 | let mut visitor = FindTyParams { 83 | all_type_params, 84 | relevant_type_params: HashSet::new(), 85 | associated_type_usage: Vec::new(), 86 | }; 87 | 88 | for field in fields.iter() { 89 | visitor.visit_field(field); 90 | } 91 | 92 | let relevant_type_params = visitor.relevant_type_params; 93 | let associated_type_params = visitor.associated_type_usage; 94 | 95 | let new_predicates = generics 96 | .type_params() 97 | .map(|param| param.ident.clone()) 98 | .filter(|ident| relevant_type_params.contains(ident)) 99 | .map(|ident| syn::TypePath { 100 | qself: None, 101 | path: ident.into(), 102 | }) 103 | .chain(associated_type_params.into_iter().cloned()) 104 | .map(|bounded_ty| { 105 | syn::WherePredicate::Type(syn::PredicateType { 106 | lifetimes: None, 107 | bounded_ty: syn::Type::Path(bounded_ty), 108 | colon_token: ::default(), 109 | bounds: vec![syn::TypeParamBound::Trait(syn::TraitBound { 110 | paren_token: None, 111 | modifier: syn::TraitBoundModifier::None, 112 | lifetimes: None, 113 | path: bound.clone(), 114 | })] 115 | .into_iter() 116 | .collect(), 117 | }) 118 | }); 119 | 120 | let mut generics = generics.clone(); 121 | generics 122 | .make_where_clause() 123 | .predicates 124 | .extend(new_predicates); 125 | generics 126 | } 127 | 128 | fn ungroup(mut ty: &syn::Type) -> &syn::Type { 129 | while let syn::Type::Group(group) = ty { 130 | ty = &group.elem; 131 | } 132 | ty 133 | } 134 | 135 | fn without_default_generic(generics: &syn::Generics) -> syn::Generics { 136 | syn::Generics { 137 | params: generics 138 | .params 139 | .iter() 140 | .map(|param| match param { 141 | syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam { 142 | eq_token: None, 143 | default: None, 144 | ..param.clone() 145 | }), 146 | _ => param.clone(), 147 | }) 148 | .collect(), 149 | ..generics.clone() 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /abi-proc/src/internals/context.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | use quote::ToTokens; 4 | 5 | pub struct Ctxt { 6 | errors: RefCell>, 7 | } 8 | 9 | impl Ctxt { 10 | pub fn new() -> Self { 11 | Self { 12 | errors: RefCell::new(Vec::new()), 13 | } 14 | } 15 | 16 | pub fn error_spanned_by(&self, object: T, message: M) 17 | where 18 | T: ToTokens, 19 | M: std::fmt::Display, 20 | { 21 | self.errors 22 | .borrow_mut() 23 | .push(syn::Error::new_spanned(object.into_token_stream(), message)); 24 | } 25 | 26 | pub fn syn_error(&self, error: syn::Error) { 27 | self.errors.borrow_mut().push(error) 28 | } 29 | 30 | pub fn check(self) -> Result<(), Vec> { 31 | let errors = std::mem::take(&mut *self.errors.borrow_mut()); 32 | match errors.len() { 33 | 0 => Ok(()), 34 | _ => Err(errors), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /abi-proc/src/internals/field.rs: -------------------------------------------------------------------------------- 1 | use quote::{format_ident, ToTokens}; 2 | use syn::{Attribute, Error, Ident, LitStr, Path}; 3 | 4 | use crate::internals::context::Ctxt; 5 | 6 | pub struct StructField { 7 | pub field_name: syn::Member, 8 | pub name_ident: Ident, 9 | } 10 | 11 | impl StructField { 12 | pub fn named(name: &syn::Ident) -> Self { 13 | Self { 14 | field_name: syn::Member::Named(name.clone()), 15 | name_ident: name.clone(), 16 | } 17 | } 18 | 19 | pub fn unnamed(field_index: usize) -> Self { 20 | Self { 21 | field_name: syn::Member::Unnamed(syn::Index::from(field_index)), 22 | name_ident: format_ident!("value{field_index}"), 23 | } 24 | } 25 | } 26 | 27 | #[derive(Default)] 28 | pub struct WithHandlers { 29 | pub abi_type_handler: Option, 30 | pub into_abi_handler: Option, 31 | pub as_abi_handler: Option, 32 | pub from_abi_handler: Option, 33 | } 34 | 35 | pub struct FieldAttributes { 36 | pub extracted: bool, 37 | pub custom_name: Option, 38 | pub mod_handler: Option, 39 | pub with_handlers: WithHandlers, 40 | } 41 | 42 | impl FieldAttributes { 43 | pub fn check(&self) -> Result<(), Error> { 44 | if let Some(path) = &self.mod_handler { 45 | if self.with_handlers.into_abi_handler.is_some() 46 | && self.with_handlers.from_abi_handler.is_some() 47 | && self.with_handlers.as_abi_handler.is_some() 48 | && self.with_handlers.abi_type_handler.is_some() 49 | { 50 | return Err(Error::new_spanned( 51 | path, 52 | "`with` parameter should not be used simultaneously with other handling parameters", 53 | )); 54 | } 55 | } 56 | 57 | if let Some(path) = &self.with_handlers.into_abi_handler { 58 | if self.with_handlers.as_abi_handler.is_none() { 59 | return Err(Error::new_spanned( 60 | path, 61 | "`into_abi_with` also requires `as_abi_with`", 62 | )); 63 | } 64 | } 65 | 66 | Ok(()) 67 | } 68 | } 69 | 70 | const ABI: &str = "abi"; 71 | const NAME: &str = "name"; 72 | const WITH: &str = "with"; 73 | const ABI_TYPE_WITH: &str = "abi_type_with"; 74 | const INTO_ABI_WITH: &str = "into_abi_with"; 75 | const AS_ABI_WITH: &str = "as_abi_with"; 76 | const FROM_ABI_WITH: &str = "from_abi_with"; 77 | 78 | pub fn extract_field_attributes(ctx: &Ctxt, attrs: &[Attribute]) -> FieldAttributes { 79 | let mut attributes = FieldAttributes { 80 | extracted: true, 81 | custom_name: None, 82 | mod_handler: None, 83 | with_handlers: Default::default(), 84 | }; 85 | 86 | fn parse_path(value: syn::parse::ParseStream) -> Result { 87 | let path_str: syn::LitStr = value.parse()?; 88 | let path = syn::parse_str::(&path_str.value())?; 89 | Ok(path) 90 | } 91 | 92 | for attr in attrs { 93 | if !attr.path().is_ident(ABI) { 94 | continue; 95 | } 96 | 97 | let result = attr.parse_nested_meta(|meta| { 98 | let value = meta.value()?; 99 | 100 | match &meta.path { 101 | path if path.is_ident(NAME) => { 102 | if attributes.custom_name.is_some() { 103 | attributes.extracted = false; 104 | ctx.error_spanned_by(path, format!("`{NAME}` already defined")); 105 | } 106 | match value.parse::() { 107 | Ok(lit) => { 108 | attributes.custom_name = 109 | Some(format_ident!("{}", lit.value(), span = lit.span())); 110 | } 111 | Err(e) => { 112 | attributes.extracted = false; 113 | ctx.syn_error(e); 114 | } 115 | } 116 | } 117 | path if path.is_ident(WITH) => { 118 | if attributes.mod_handler.is_some() { 119 | attributes.extracted = false; 120 | ctx.error_spanned_by(path, format!("`{WITH}` already defined")); 121 | } 122 | 123 | match parse_path(value) { 124 | Ok(path) => attributes.mod_handler = Some(path), 125 | Err(e) => { 126 | attributes.extracted = false; 127 | ctx.syn_error(e); 128 | } 129 | } 130 | } 131 | path if path.is_ident(ABI_TYPE_WITH) => { 132 | if attributes.with_handlers.abi_type_handler.is_some() { 133 | attributes.extracted = false; 134 | ctx.error_spanned_by(path, format!("`{ABI_TYPE_WITH}` already defined")); 135 | } 136 | 137 | match parse_path(value) { 138 | Ok(path) => attributes.with_handlers.abi_type_handler = Some(path), 139 | Err(e) => { 140 | attributes.extracted = false; 141 | ctx.syn_error(e); 142 | } 143 | } 144 | } 145 | path if path.is_ident(INTO_ABI_WITH) => { 146 | if attributes.with_handlers.into_abi_handler.is_some() { 147 | attributes.extracted = false; 148 | ctx.error_spanned_by(path, format!("`{INTO_ABI_WITH}` already defined")); 149 | } 150 | match parse_path(value) { 151 | Ok(path) => attributes.with_handlers.into_abi_handler = Some(path), 152 | Err(e) => { 153 | attributes.extracted = false; 154 | ctx.syn_error(e); 155 | } 156 | } 157 | } 158 | path if path.is_ident(AS_ABI_WITH) => { 159 | if attributes.with_handlers.as_abi_handler.is_some() { 160 | attributes.extracted = false; 161 | ctx.error_spanned_by(path, format!("`{AS_ABI_WITH}` already defined")); 162 | } 163 | match parse_path(value) { 164 | Ok(path) => attributes.with_handlers.as_abi_handler = Some(path), 165 | Err(e) => { 166 | attributes.extracted = false; 167 | ctx.syn_error(e); 168 | } 169 | } 170 | } 171 | path if path.is_ident(FROM_ABI_WITH) => { 172 | if attributes.with_handlers.from_abi_handler.is_some() { 173 | attributes.extracted = false; 174 | ctx.error_spanned_by(path, format!("`{FROM_ABI_WITH}` already defined")); 175 | } 176 | 177 | match parse_path(value) { 178 | Ok(path) => attributes.with_handlers.from_abi_handler = Some(path), 179 | Err(e) => { 180 | attributes.extracted = false; 181 | ctx.syn_error(e); 182 | } 183 | } 184 | } 185 | 186 | path => { 187 | let str = meta.path.to_token_stream().to_string().replace(' ', ""); 188 | ctx.error_spanned_by(path, format!("unknown abi container attribute `{str}`")) 189 | } 190 | } 191 | 192 | Ok(()) 193 | }); 194 | 195 | if let Err(e) = result { 196 | attributes.extracted = false; 197 | ctx.syn_error(e); 198 | } 199 | 200 | if let Err(e) = attributes.check() { 201 | attributes.extracted = false; 202 | ctx.syn_error(e); 203 | } 204 | } 205 | 206 | attributes 207 | } 208 | -------------------------------------------------------------------------------- /abi-proc/src/internals/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod container; 2 | pub mod context; 3 | pub mod field; 4 | -------------------------------------------------------------------------------- /abi-proc/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod derive_from_abi; 2 | mod derive_into_abi; 3 | mod derive_with_abi_type; 4 | mod internals; 5 | 6 | use proc_macro::TokenStream; 7 | use quote::quote; 8 | 9 | #[proc_macro_derive(IntoAbi, attributes(abi))] 10 | pub fn derive_into_abi(input: TokenStream) -> TokenStream { 11 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 12 | derive_into_abi::impl_derive(input) 13 | .unwrap_or_else(to_compile_errors) 14 | .into() 15 | } 16 | 17 | #[proc_macro_derive(WithAbiType, attributes(abi))] 18 | pub fn derive_with_abi_type(input: TokenStream) -> TokenStream { 19 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 20 | derive_with_abi_type::impl_derive(input) 21 | .unwrap_or_else(to_compile_errors) 22 | .into() 23 | } 24 | 25 | #[proc_macro_derive(FromAbi, attributes(abi))] 26 | pub fn derive_from_abi(input: TokenStream) -> TokenStream { 27 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 28 | derive_from_abi::impl_derive(input) 29 | .unwrap_or_else(to_compile_errors) 30 | .into() 31 | } 32 | 33 | fn to_compile_errors(errors: Vec) -> proc_macro2::TokenStream { 34 | let compile_errors = errors.iter().map(syn::Error::to_compile_error); 35 | quote!(#(#compile_errors)*) 36 | } 37 | -------------------------------------------------------------------------------- /benches/big_cell.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::hint::black_box; 3 | 4 | use criterion::{criterion_group, criterion_main, Criterion}; 5 | use everscale_types::boc::ser::BocHeaderCache; 6 | use everscale_types::boc::Boc; 7 | use everscale_types::cell::{Cell, CellBuilder}; 8 | 9 | thread_local! { 10 | static CACHE: RefCell> = { 11 | RefCell::new(BocHeaderCache::with_capacity(300000)) 12 | }; 13 | } 14 | 15 | fn make_big_tree(depth: u8, count: &mut u32, target: u32) -> Cell { 16 | *count += 1; 17 | 18 | if depth == 0 { 19 | CellBuilder::build_from(*count).unwrap() 20 | } else { 21 | let mut b = CellBuilder::new(); 22 | for _ in 0..4 { 23 | if *count < target { 24 | b.store_reference(make_big_tree(depth - 1, count, target)) 25 | .unwrap(); 26 | } 27 | } 28 | b.build().unwrap() 29 | } 30 | } 31 | 32 | fn encode(c: &mut Criterion) { 33 | let cell = make_big_tree(11, &mut 0, 300000); 34 | c.bench_function("encode", |b| { 35 | b.iter(|| { 36 | CACHE.with_borrow_mut(|c| { 37 | let bytes = Boc::encode_with_cache(cell.as_ref(), c); 38 | black_box(bytes); 39 | }); 40 | }) 41 | }); 42 | } 43 | 44 | criterion_group!(benches, encode); 45 | criterion_main!(benches); 46 | -------------------------------------------------------------------------------- /benches/boc.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use everscale_types::boc::Boc; 3 | 4 | fn deserialize_boc(id: BenchmarkId, boc: &'static [u8], c: &mut Criterion) { 5 | c.bench_with_input(id, &boc, |b, boc| { 6 | b.iter(|| { 7 | let result = Boc::decode(boc); 8 | _ = black_box(result); 9 | }); 10 | }); 11 | } 12 | 13 | fn serialize_boc(id: BenchmarkId, boc: &'static [u8], c: &mut Criterion) { 14 | let cell = Boc::decode(boc).unwrap(); 15 | 16 | c.bench_with_input(id, &cell, |b, cell| { 17 | b.iter(|| { 18 | let result = Boc::encode(cell.as_ref()); 19 | _ = black_box(result); 20 | }); 21 | }); 22 | } 23 | 24 | fn boc_group(c: &mut Criterion) { 25 | macro_rules! decl_boc_benches { 26 | ($($name:literal),*$(,)?) => { 27 | $({ 28 | let id = BenchmarkId::new( 29 | "deserialize_boc", 30 | format!("name={}", $name) 31 | ); 32 | let boc = include_bytes!(concat!("data/", $name)); 33 | deserialize_boc(id, boc, c); 34 | });* 35 | 36 | $({ 37 | let id = BenchmarkId::new( 38 | "serialize_boc", 39 | format!("name={}", $name) 40 | ); 41 | let boc = include_bytes!(concat!("data/", $name)); 42 | serialize_boc(id, boc, c); 43 | });* 44 | }; 45 | } 46 | 47 | decl_boc_benches![ 48 | "external_message", 49 | "internal_message_empty", 50 | "internal_message_with_body", 51 | "internal_message_with_deploy", 52 | "masterchain_block", 53 | "masterchain_key_block", 54 | "shard_block_empty", 55 | "shard_block_with_messages", 56 | "masterchain_block_proof", 57 | "shard_block_proof" 58 | ]; 59 | } 60 | 61 | criterion_group!(boc, boc_group); 62 | criterion_main!(boc); 63 | -------------------------------------------------------------------------------- /benches/callgrind_boc.rs: -------------------------------------------------------------------------------- 1 | use std::hint::black_box; 2 | 3 | use everscale_types::boc::Boc; 4 | use everscale_types::cell::Cell; 5 | use iai_callgrind::{library_benchmark, library_benchmark_group, main}; 6 | 7 | #[macro_export] 8 | macro_rules! decl_boc_benches { 9 | ($( 10 | $name:literal 11 | ),* $(,)?) => { 12 | // generate setup functions for raw bytes 13 | $( 14 | paste::paste! { 15 | fn [<$name _setup>]() -> &'static [u8] { 16 | include_bytes!(concat!("data/", $name)) 17 | } 18 | } 19 | )* 20 | 21 | // generate setup functions for cells 22 | $( 23 | paste::paste! { 24 | fn [<$name _setup_de>]() -> Cell { 25 | let bytes = include_bytes!(concat!("data/", $name)); 26 | Boc::decode(&bytes).unwrap() 27 | } 28 | } 29 | )* 30 | 31 | // generate benchmark functions attributes for decode / encode 32 | paste::paste! { 33 | #[library_benchmark] 34 | $( 35 | #[bench::[<$name>](setup = [<$name _setup>])] 36 | )* 37 | fn deserialize_boc(input: &[u8]) { 38 | let result = Boc::decode(input); 39 | _ = black_box(result); 40 | } 41 | 42 | #[library_benchmark] 43 | $( 44 | #[bench::[<$name>](setup = [<$name _setup_de>])] 45 | )* 46 | fn serialize_boc(input: Cell) { 47 | let result = Boc::encode(&input); 48 | _ = black_box(result); 49 | std::mem::forget(input); 50 | } 51 | } 52 | }; 53 | } 54 | 55 | decl_boc_benches![ 56 | "external_message", 57 | "internal_message_empty", 58 | "internal_message_with_body", 59 | "internal_message_with_deploy", 60 | "masterchain_block", 61 | "masterchain_key_block", 62 | "shard_block_empty", 63 | "shard_block_with_messages", 64 | "masterchain_block_proof", 65 | "shard_block_proof" 66 | ]; 67 | 68 | library_benchmark_group!( 69 | name = benches; 70 | benchmarks = 71 | deserialize_boc, 72 | serialize_boc 73 | ); 74 | 75 | main!(library_benchmark_groups = benches); 76 | -------------------------------------------------------------------------------- /benches/callgrind_dict.rs: -------------------------------------------------------------------------------- 1 | use std::hint::black_box; 2 | 3 | use everscale_types::cell::*; 4 | use everscale_types::dict::*; 5 | use iai_callgrind::{library_benchmark, library_benchmark_group, main}; 6 | use rand::distributions::{Distribution, Standard}; 7 | use rand::{Rng, SeedableRng}; 8 | 9 | fn build_dict(num_elements: usize) -> Dict 10 | where 11 | Standard: Distribution + Distribution, 12 | K: StoreDictKey, 13 | V: Store, 14 | { 15 | let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); 16 | 17 | let mut result = Dict::::new(); 18 | for _ in 0..num_elements { 19 | let key = rng.gen::(); 20 | let value = rng.gen::(); 21 | result.set(key, value).unwrap(); 22 | } 23 | result 24 | } 25 | 26 | #[library_benchmark] 27 | #[bench::small(10)] 28 | #[bench::medium(100)] 29 | #[bench::large(1000)] 30 | #[bench::xlarge(10000)] 31 | fn bench_build_dict_u64_u64(num_elements: usize) -> Dict { 32 | black_box(build_dict(num_elements)) 33 | } 34 | 35 | library_benchmark_group!(name = build_dict; benchmarks = bench_build_dict_u64_u64); 36 | 37 | main!(library_benchmark_groups = build_dict); 38 | -------------------------------------------------------------------------------- /benches/callgrind_dict_from_slice.rs: -------------------------------------------------------------------------------- 1 | use std::hint::black_box; 2 | 3 | use everscale_types::cell::*; 4 | use everscale_types::dict::*; 5 | use iai_callgrind::{library_benchmark, library_benchmark_group, main}; 6 | use rand::distributions::{Distribution, Standard}; 7 | use rand::{Rng, SeedableRng}; 8 | 9 | fn build_dict_inserts(num_elements: usize) -> Dict 10 | where 11 | Standard: Distribution + Distribution, 12 | K: StoreDictKey, 13 | V: Store, 14 | { 15 | let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); 16 | 17 | let mut result = Dict::::new(); 18 | for _ in 0..num_elements { 19 | let key = rng.gen::(); 20 | let value = rng.gen::(); 21 | result.add(key, value).unwrap(); 22 | } 23 | result 24 | } 25 | 26 | fn build_dict_leaves(num_elements: usize) -> Dict 27 | where 28 | Standard: Distribution + Distribution, 29 | K: StoreDictKey + Ord, 30 | V: Store, 31 | { 32 | let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); 33 | 34 | let mut values = (0..num_elements) 35 | .map(|_| (rng.gen::(), rng.gen::())) 36 | .collect::>(); 37 | values.sort_by(|(l, _), (r, _)| l.cmp(r)); 38 | 39 | Dict::::try_from_sorted_slice(&values).unwrap() 40 | } 41 | 42 | #[library_benchmark] 43 | #[bench::small(10)] 44 | #[bench::medium(100)] 45 | #[bench::large(1000)] 46 | #[bench::xlarge(10000)] 47 | fn bench_build_dict_u64_u64_inserts(num_elements: usize) -> Dict { 48 | black_box(build_dict_inserts(num_elements)) 49 | } 50 | 51 | #[library_benchmark] 52 | #[bench::small(10)] 53 | #[bench::medium(100)] 54 | #[bench::large(1000)] 55 | #[bench::xlarge(10000)] 56 | fn bench_build_dict_u64_u64_leaves(num_elements: usize) -> Dict { 57 | black_box(build_dict_leaves(num_elements)) 58 | } 59 | 60 | library_benchmark_group!( 61 | name = build_dict; 62 | benchmarks = bench_build_dict_u64_u64_inserts, bench_build_dict_u64_u64_leaves 63 | ); 64 | 65 | main!(library_benchmark_groups = build_dict); 66 | -------------------------------------------------------------------------------- /benches/callgrind_slice_uniform.rs: -------------------------------------------------------------------------------- 1 | use std::hint::black_box; 2 | 3 | use everscale_types::prelude::*; 4 | use iai_callgrind::{library_benchmark, library_benchmark_group, main}; 5 | 6 | #[library_benchmark] 7 | #[bench::small(2)] 8 | #[bench::medium(4)] 9 | #[bench::large(8)] 10 | #[bench::xlarge(10)] 11 | fn test(bits: u32) { 12 | let mut builder = CellBuilder::new(); 13 | builder.store_zeros(2u16.pow(bits) - 1u16).unwrap(); 14 | let cell = builder.build().unwrap(); 15 | 16 | let slice = cell.as_slice().unwrap(); 17 | black_box(slice.test_uniform()); 18 | } 19 | 20 | library_benchmark_group!( 21 | name = test_uniform; 22 | benchmarks = test 23 | ); 24 | 25 | main!(library_benchmark_groups = test_uniform); 26 | -------------------------------------------------------------------------------- /benches/callgrind_usage_cell.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::hint::black_box; 3 | 4 | use everscale_types::cell::RefsIter; 5 | use everscale_types::prelude::*; 6 | use iai_callgrind::{library_benchmark, library_benchmark_group, main}; 7 | 8 | const BOC: &str = "te6ccgECCAEAAWQAAnPP9noJKCEBL3oZerOiIcNghuL96V3wIcuYOWQdvNC+2fqCEIJDQAAAAAAAAAAAAAAAAZa8xB6QABNAAgEAUO3QlUyMI4dEepUMw3Ou6oSqq8+1lyHkjOGFK6DAn6TXAAAAAAAAAAABFP8A9KQT9LzyyAsDAgEgBwQC5vJx1wEBwADyeoMI1xjtRNCDB9cB1ws/yPgozxYjzxbJ+QADcdcBAcMAmoMH1wFRE7ry4GTegEDXAYAg1wGAINcBVBZ1+RDyqPgju/J5Zr74I4EHCKCBA+ioUiC8sfJ0AiCCEEzuZGy64w8ByMv/yz/J7VQGBQA+ghAWnj4Ruo4R+AACkyDXSpd41wHUAvsA6NGTMvI84gCYMALXTND6QIMG1wFx1wF41wHXTPgAcIAQBKoCFLHIywVQBc8WUAP6AstpItAhzzEh10mghAm5mDNwAcsAWM8WlzBxAcsAEsziyQH7AAAE0jA="; 9 | 10 | #[library_benchmark] 11 | fn traverse_cell_ordinary() { 12 | let cell = Boc::decode_base64(BOC).unwrap(); 13 | 14 | let mut visitor = Visitor::new(); 15 | black_box(visitor.add_cell(cell.as_ref())); 16 | } 17 | 18 | #[library_benchmark] 19 | fn traverse_cell_storage_cell() { 20 | let cell = Boc::decode_base64(BOC).unwrap(); 21 | let usage_tree = UsageTree::new(UsageTreeMode::OnDataAccess); 22 | let cell = usage_tree.track(&cell); 23 | 24 | let mut visitor = Visitor::new(); 25 | black_box(visitor.add_cell(cell.as_ref())); 26 | } 27 | 28 | #[library_benchmark] 29 | fn traverse_cell_storage_cell_with_capacity() { 30 | let cell = Boc::decode_base64(BOC).unwrap(); 31 | let usage_tree = UsageTree::with_mode_and_capacity(UsageTreeMode::OnDataAccess, 100); 32 | let cell = usage_tree.track(&cell); 33 | 34 | let mut visitor = Visitor::new(); 35 | black_box(visitor.add_cell(cell.as_ref())); 36 | } 37 | 38 | struct Visitor<'a> { 39 | visited: ahash::HashSet<&'a HashBytes>, 40 | stack: Vec>, 41 | } 42 | 43 | impl<'a> Visitor<'a> { 44 | fn new() -> Self { 45 | Self { 46 | visited: HashSet::with_hasher(ahash::RandomState::with_seed(0)), 47 | stack: Vec::new(), 48 | } 49 | } 50 | 51 | fn add_cell(&mut self, cell: &'a DynCell) -> bool { 52 | if !self.visited.insert(cell.repr_hash()) { 53 | return true; 54 | } 55 | 56 | self.stack.clear(); 57 | self.stack.push(cell.references()); 58 | self.reduce_stack() 59 | } 60 | 61 | fn reduce_stack(&mut self) -> bool { 62 | 'outer: while let Some(item) = self.stack.last_mut() { 63 | for cell in item.by_ref() { 64 | if !self.visited.insert(cell.repr_hash()) { 65 | continue; 66 | } 67 | 68 | let mut slice = cell.as_slice().unwrap(); 69 | slice.load_bit().ok(); 70 | slice.load_u32().ok(); 71 | slice.load_small_uint(5).ok(); 72 | slice.load_reference().ok(); 73 | 74 | let next = cell.references(); 75 | if next.peek().is_some() { 76 | self.stack.push(next); 77 | continue 'outer; 78 | } 79 | } 80 | 81 | self.stack.pop(); 82 | } 83 | 84 | true 85 | } 86 | } 87 | 88 | library_benchmark_group!( 89 | name = traverse_cell; 90 | benchmarks = traverse_cell_ordinary, traverse_cell_storage_cell, traverse_cell_storage_cell_with_capacity 91 | ); 92 | 93 | main!(library_benchmark_groups = traverse_cell); 94 | -------------------------------------------------------------------------------- /benches/data/external_message: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/external_message -------------------------------------------------------------------------------- /benches/data/internal_message_empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/internal_message_empty -------------------------------------------------------------------------------- /benches/data/internal_message_with_body: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/internal_message_with_body -------------------------------------------------------------------------------- /benches/data/internal_message_with_deploy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/internal_message_with_deploy -------------------------------------------------------------------------------- /benches/data/masterchain_block: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/masterchain_block -------------------------------------------------------------------------------- /benches/data/masterchain_block_proof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/masterchain_block_proof -------------------------------------------------------------------------------- /benches/data/masterchain_key_block: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/masterchain_key_block -------------------------------------------------------------------------------- /benches/data/shard_block_empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/shard_block_empty -------------------------------------------------------------------------------- /benches/data/shard_block_proof: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/shard_block_proof -------------------------------------------------------------------------------- /benches/data/shard_block_with_messages: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/benches/data/shard_block_with_messages -------------------------------------------------------------------------------- /benches/dict.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use everscale_types::cell::*; 3 | use everscale_types::dict::*; 4 | use rand::distributions::{Distribution, Standard}; 5 | use rand::{Rng, SeedableRng}; 6 | 7 | fn build_dict_impl(id: BenchmarkId, num_elements: usize, c: &mut Criterion) 8 | where 9 | Standard: Distribution + Distribution, 10 | K: StoreDictKey, 11 | V: Store, 12 | { 13 | let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); 14 | 15 | let values = (0..num_elements) 16 | .map(|_| (rng.gen::(), rng.gen::())) 17 | .collect::>(); 18 | 19 | c.bench_with_input(id, &values, |b, values| { 20 | b.iter(|| { 21 | let mut result = Dict::::new(); 22 | for (key, value) in values { 23 | result.set(key, value).unwrap(); 24 | } 25 | black_box(result); 26 | }); 27 | }); 28 | } 29 | 30 | fn build_dict_group(c: &mut Criterion) { 31 | macro_rules! decl_dict_benches { 32 | ($({ $n:literal, $k:ty, $v:ident }),*$(,)?) => { 33 | $({ 34 | let id = BenchmarkId::new( 35 | "build_dict", 36 | format!( 37 | "size={}; key={}; value={}", 38 | $n, stringify!($k), stringify!($v) 39 | ) 40 | ); 41 | build_dict_impl::<$k, $v>(id, $n, c); 42 | });* 43 | }; 44 | } 45 | 46 | decl_dict_benches![ 47 | { 10, u8, u64 }, 48 | { 256, u8, u64 }, 49 | 50 | { 10, u16, u64 }, 51 | { 100, u16, u64 }, 52 | { 256, u16, u64 }, 53 | { 10000, u16, u64 }, 54 | 55 | { 10, u32, u64 }, 56 | { 100, u32, u64 }, 57 | { 1000, u32, u64 }, 58 | { 100000, u32, u64 }, 59 | 60 | { 10, u64, u64 }, 61 | { 100, u64, u64 }, 62 | { 1000, u64, u64 }, 63 | { 100000, u64, u64 }, 64 | ]; 65 | } 66 | 67 | criterion_group!(build_dict, build_dict_group); 68 | criterion_main!(build_dict); 69 | -------------------------------------------------------------------------------- /benches/dict_from_slice.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use everscale_types::cell::*; 3 | use everscale_types::dict::*; 4 | use rand::distributions::{Distribution, Standard}; 5 | use rand::{Rng, SeedableRng}; 6 | 7 | fn build_dict_impl(id: impl Into, sizes: &[usize], c: &mut Criterion) 8 | where 9 | Standard: Distribution + Distribution, 10 | K: StoreDictKey + Ord, 11 | V: Store, 12 | { 13 | let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); 14 | 15 | let mut group = c.benchmark_group(id); 16 | 17 | for size in sizes { 18 | let mut values = (0..*size) 19 | .map(|_| (rng.gen::(), rng.gen::())) 20 | .collect::>(); 21 | 22 | group.bench_with_input(BenchmarkId::new("inserts", size), &values, |b, values| { 23 | b.iter(|| { 24 | let mut result = Dict::::new(); 25 | for (key, value) in values { 26 | result.add(key, value).unwrap(); 27 | } 28 | black_box(result); 29 | }); 30 | }); 31 | 32 | values.sort_by(|(l, _), (r, _)| l.cmp(r)); 33 | group.bench_with_input(BenchmarkId::new("leaves", size), &values, |b, values| { 34 | b.iter(|| { 35 | let result = Dict::::try_from_sorted_slice(values).unwrap(); 36 | black_box(result); 37 | }); 38 | }); 39 | } 40 | } 41 | 42 | fn build_dict_group(c: &mut Criterion) { 43 | macro_rules! decl_dict_benches { 44 | ($(($k:ty, $v:ident): [$($n:literal),+]),*$(,)?) => { 45 | $({ 46 | let id = format!( 47 | "build_dict({},{})", 48 | stringify!($k), stringify!($v) 49 | ); 50 | build_dict_impl::<$k, $v>(id, &[$($n),+], c); 51 | });* 52 | }; 53 | } 54 | 55 | decl_dict_benches![ 56 | (u8, u64): [10, 256], 57 | (u16, u64): [10, 100, 256, 1000, 10000], 58 | (u32, u64): [10, 100, 1000, 10000, 100000], 59 | (u64, u64): [10, 100, 1000, 5000, 10000, 20000, 25000, 50000, 75000, 100000], 60 | ]; 61 | } 62 | 63 | criterion_group!(build_dict, build_dict_group); 64 | criterion_main!(build_dict); 65 | -------------------------------------------------------------------------------- /benches/dict_modify.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use everscale_types::cell::*; 3 | use everscale_types::dict::*; 4 | use rand::distributions::{Distribution, Standard}; 5 | use rand::{Rng, SeedableRng}; 6 | 7 | fn build_dict_impl(id: impl Into, sizes: &[usize], c: &mut Criterion) 8 | where 9 | Standard: Distribution + Distribution, 10 | K: Copy + StoreDictKey + Ord, 11 | V: Copy + Store, 12 | { 13 | let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); 14 | 15 | let mut group = c.benchmark_group(id); 16 | 17 | for size in sizes { 18 | let mut values = (0..*size) 19 | .map(|_| (rng.gen::(), rng.gen::())) 20 | .collect::>(); 21 | values.sort_by(|(l, _), (r, _)| l.cmp(r)); 22 | let initial = Dict::try_from_sorted_slice(&values).unwrap(); 23 | 24 | let mut operations = (0..*size) 25 | .map(|_| (rng.gen::(), rng.gen::>())) 26 | .collect::>(); 27 | operations.sort_by(|(l, _), (r, _)| l.cmp(r)); 28 | operations.dedup_by(|(l, _), (r, _)| (*l).eq(r)); 29 | 30 | group.bench_with_input( 31 | BenchmarkId::new("manual", size), 32 | &operations, 33 | |b, operations| { 34 | b.iter(|| { 35 | let mut result = initial.clone(); 36 | for (key, value) in operations { 37 | match value { 38 | Some(value) => { 39 | result.add(key, value).unwrap(); 40 | } 41 | None => { 42 | result.remove_raw(key).unwrap(); 43 | } 44 | } 45 | } 46 | black_box(result); 47 | }); 48 | }, 49 | ); 50 | 51 | group.bench_with_input( 52 | BenchmarkId::new("batched", size), 53 | &operations, 54 | |b, operations| { 55 | b.iter(|| { 56 | let mut result = initial.clone(); 57 | result 58 | .modify_with_sorted_iter(operations.iter().copied()) 59 | .unwrap(); 60 | black_box(result); 61 | }); 62 | }, 63 | ); 64 | } 65 | } 66 | 67 | fn modify_dict_group(c: &mut Criterion) { 68 | macro_rules! decl_dict_benches { 69 | ($(($k:ty, $v:ident): [$($n:literal),+]),*$(,)?) => { 70 | $({ 71 | let id = format!( 72 | "modify_dict({},{})", 73 | stringify!($k), stringify!($v) 74 | ); 75 | build_dict_impl::<$k, $v>(id, &[$($n),+], c); 76 | });* 77 | }; 78 | } 79 | 80 | decl_dict_benches![ 81 | (u8, u64): [10, 256], 82 | (u16, u64): [10, 100, 256, 1000, 10000], 83 | (u32, u64): [10, 100, 1000, 10000, 100000], 84 | (u64, u64): [10, 100, 1000, 5000, 10000, 20000, 25000, 50000, 75000, 100000], 85 | ]; 86 | } 87 | 88 | criterion_group!(modify_dict, modify_dict_group); 89 | criterion_main!(modify_dict); 90 | -------------------------------------------------------------------------------- /benches/dict_modify_aug.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use everscale_types::cell::*; 3 | use everscale_types::dict::*; 4 | use everscale_types::error::Error; 5 | use rand::distributions::{Distribution, Standard}; 6 | use rand::{Rng, SeedableRng}; 7 | 8 | #[derive(Default, Debug, Store, Load, Clone, Copy)] 9 | struct SimpleAug(u32); 10 | 11 | impl rand::distributions::Distribution for rand::distributions::Standard { 12 | #[inline] 13 | fn sample(&self, rng: &mut R) -> SimpleAug { 14 | SimpleAug(rand::distributions::Standard.sample(rng)) 15 | } 16 | } 17 | 18 | impl AugDictExtra for SimpleAug { 19 | fn comp_add( 20 | left: &mut CellSlice, 21 | right: &mut CellSlice, 22 | b: &mut CellBuilder, 23 | _: &dyn CellContext, 24 | ) -> Result<(), Error> { 25 | let left = left.load_u32()?; 26 | let right = right.load_u32()?; 27 | b.store_u32(left.saturating_add(right)) 28 | } 29 | } 30 | 31 | fn build_dict_impl(id: impl Into, sizes: &[usize], c: &mut Criterion) 32 | where 33 | Standard: Distribution + Distribution + Distribution, 34 | K: Copy + StoreDictKey + Ord, 35 | for<'a> A: Copy + AugDictExtra + Store + Load<'a>, 36 | V: Copy + Store, 37 | { 38 | let mut rng = rand_xorshift::XorShiftRng::from_seed([0u8; 16]); 39 | 40 | let mut group = c.benchmark_group(id); 41 | 42 | for size in sizes { 43 | let mut values = (0..*size) 44 | .map(|_| (rng.gen::(), rng.gen::(), rng.gen::())) 45 | .collect::>(); 46 | values.sort_by(|(l, ..), (r, ..)| l.cmp(r)); 47 | let initial = AugDict::try_from_sorted_slice(&values).unwrap(); 48 | 49 | let mut operations = (0..*size) 50 | .map(|_| (rng.gen::(), rng.gen::>())) 51 | .collect::>(); 52 | operations.sort_by(|(l, _), (r, _)| l.cmp(r)); 53 | operations.dedup_by(|(l, _), (r, _)| (*l).eq(r)); 54 | 55 | group.bench_with_input( 56 | BenchmarkId::new("manual", size), 57 | &operations, 58 | |b, operations| { 59 | b.iter(|| { 60 | let mut result = initial.clone(); 61 | for (key, value) in operations { 62 | match value { 63 | Some((extra, value)) => { 64 | result.add(key, extra, value).unwrap(); 65 | } 66 | None => { 67 | result.remove_raw(key).unwrap(); 68 | } 69 | } 70 | } 71 | black_box(result); 72 | }); 73 | }, 74 | ); 75 | 76 | group.bench_with_input( 77 | BenchmarkId::new("batched", size), 78 | &operations, 79 | |b, operations| { 80 | b.iter(|| { 81 | let mut result = initial.clone(); 82 | result 83 | .modify_with_sorted_iter(operations.iter().copied()) 84 | .unwrap(); 85 | black_box(result); 86 | }); 87 | }, 88 | ); 89 | } 90 | } 91 | 92 | fn modify_dict_aug_group(c: &mut Criterion) { 93 | macro_rules! decl_dict_benches { 94 | ($(($k:ty, $a:ident, $v:ident): [$($n:literal),+]),*$(,)?) => { 95 | $({ 96 | let id = format!( 97 | "modify_dict_aug({},{},{})", 98 | stringify!($k), 99 | stringify!($a), 100 | stringify!($v), 101 | ); 102 | build_dict_impl::<$k, $a, $v>(id, &[$($n),+], c); 103 | });* 104 | }; 105 | } 106 | 107 | decl_dict_benches![ 108 | (u8, SimpleAug, u64): [10, 256], 109 | (u16, SimpleAug, u64): [10, 100, 256, 1000, 10000], 110 | (u32, SimpleAug, u64): [10, 100, 1000, 10000, 100000], 111 | (u64, SimpleAug, u64): [10, 100, 1000, 5000, 10000, 20000, 25000, 50000, 75000, 100000], 112 | ]; 113 | } 114 | 115 | criterion_group!(modify_dict_aug, modify_dict_aug_group); 116 | criterion_main!(modify_dict_aug); 117 | -------------------------------------------------------------------------------- /benches/mine.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use everscale_types::boc::Boc; 3 | use everscale_types::cell::{Cell, CellBuilder, CellFamily, DynCell, HashBytes, StaticCell, Store}; 4 | use everscale_types::dict::{Dict, RawDict}; 5 | use everscale_types::models::{StateInit, StdAddr}; 6 | 7 | pub const MAX_NONCE: u64 = 300; 8 | 9 | #[derive(Debug)] 10 | pub struct MinedData { 11 | pub nonce: u64, 12 | pub hash: HashBytes, 13 | pub data: Vec, 14 | } 15 | 16 | fn do_mine(code: &Cell, factory_addr: &StdAddr, recipient: &StdAddr, reward: u128) -> MinedData { 17 | const KEY_ZERO: &DynCell = 18 | &unsafe { StaticCell::new(&[0, 0, 0, 0, 0, 0, 0, 0, 0x80], 64, &[0u8; 32]) }; 19 | const KEY_ONE: &DynCell = 20 | &unsafe { StaticCell::new(&[0, 0, 0, 0, 0, 0, 0, 1, 0x80], 64, &[0u8; 32]) }; 21 | const KEY_TWO: &DynCell = 22 | &unsafe { StaticCell::new(&[0, 0, 0, 0, 0, 0, 0, 2, 0x80], 64, &[0u8; 32]) }; 23 | 24 | let cx = Cell::empty_context(); 25 | 26 | let target_bits = factory_addr.address.0[0] >> 4; 27 | 28 | let mut data = RawDict::<64>::new(); 29 | 30 | data.set_ext(KEY_ZERO.as_slice_allow_exotic(), &HashBytes::ZERO, cx) 31 | .unwrap(); 32 | 33 | let airdrop_data_child = CellBuilder::build_from_ext((recipient, reward), cx).unwrap(); 34 | let airdrop_data = CellBuilder::build_from_ext((factory_addr, airdrop_data_child), cx).unwrap(); 35 | 36 | data.set_ext(KEY_TWO.as_slice_allow_exotic(), &airdrop_data, cx) 37 | .unwrap(); 38 | 39 | let nonce_key = KEY_ONE.as_slice_allow_exotic(); 40 | 41 | let mut nonce = 0; 42 | loop { 43 | let mut builder = CellBuilder::new(); 44 | builder.store_zeros(192).unwrap(); 45 | builder.store_u64(nonce).unwrap(); 46 | data.set_ext(nonce_key, &builder.as_data_slice(), cx) 47 | .unwrap(); 48 | 49 | let partial_state_init = CellBuilder::build_from_ext( 50 | StateInit { 51 | split_depth: None, 52 | special: None, 53 | code: Some(code.clone()), 54 | data: Some(CellBuilder::build_from_ext(&data, cx).unwrap()), 55 | libraries: Dict::new(), 56 | }, 57 | cx, 58 | ) 59 | .unwrap(); 60 | 61 | let address = partial_state_init.repr_hash(); 62 | if address.0[0] >> 4 == target_bits || nonce >= MAX_NONCE { 63 | let mut builder = CellBuilder::new(); 64 | 65 | let child = CellBuilder::build_from_ext((recipient, reward), cx).unwrap(); 66 | 67 | // uint256 nonce 68 | builder.store_zeros(192).unwrap(); 69 | builder.store_u64(nonce).unwrap(); 70 | 71 | // addr 72 | factory_addr.store_into(&mut builder, cx).unwrap(); 73 | builder.store_reference(child).unwrap(); 74 | 75 | let full_airdrop_data = builder.build_ext(cx).unwrap(); 76 | 77 | let hash = *full_airdrop_data.repr_hash(); 78 | let data = Boc::encode(&full_airdrop_data); 79 | 80 | return MinedData { nonce, hash, data }; 81 | } 82 | 83 | nonce += 1; 84 | } 85 | } 86 | 87 | fn mine_address(c: &mut Criterion) { 88 | let inputs = [ 89 | ( 90 | "0:cf9b339921e0b981f4c0bc3053c4e386f14cf97127ffa297fd93d7de6a561ffa", 91 | "0:cccc221b25626349429bc14669c6c072c1e238c6fe5d3ea02de707860a8c6d21", 92 | 120000000, 93 | 0, 94 | ), 95 | ( 96 | "0:b84ee317b5ddb05002a52a0ffe6a524ccb4c87f9cf35b9a98a1ac623de70f113", 97 | "0:bbbb83ba6af3bca1a78bf09271b163bbd54736272768c9544faf1efd1049dba6", 98 | 110000000, 99 | 3, 100 | ), 101 | ( 102 | "0:da612c822390fdc8b62b14a432d401a73b3247dec38e3747fb9cd665ca4df5bf", 103 | "0:ddddfdd246302e87bad55712acc50d4d72e55f5636a52741f6a188939e2fe518", 104 | 210000000, 105 | 4, 106 | ), 107 | ( 108 | "0:7973aac2a0f1f1048aa4364ab545a1d1c85839f7dac7f16bec6cd4d9a252f92b", 109 | "0:77775d17834917c7a5d66a59d3dedd2f993d0059d1824c7272854fc808acc574", 110 | 170000000, 111 | 5, 112 | ), 113 | ( 114 | "0:b27e8a967935bcb17c1cf8afbaf09d317c1553926c9c772fd53240c0933c1043", 115 | "0:bbbb83ba6af3bca1a78bf09271b163bbd54736272768c9544faf1efd1049dba6", 116 | 150000000, 117 | 7, 118 | ), 119 | ( 120 | "0:b3de1d08c26fe6e35bddc3ac4efc573c33d639fa69195cad243e54e68f2dbdcd", 121 | "0:bbbb83ba6af3bca1a78bf09271b163bbd54736272768c9544faf1efd1049dba6", 122 | 220000000, 123 | 9, 124 | ), 125 | ( 126 | "0:12c9c61c113f292d19d2e7b29c578c0a6e0efaf52fb64d2582aa962b4c4fa908", 127 | "0:1111160d461226ad45bacafbdf42bd76e33643547c41e2e42bcae19b42b90122", 128 | 100000000, 129 | 10, 130 | ), 131 | ( 132 | "0:3d7c3041562f788f00381ddf5a61631b37a7ec13127f89b428ca713f547bb4ad", 133 | "0:3334dd1d2f96f3907f7332d6974c383f51500130ce1bc00480020bbb52b8dfb3", 134 | 160000000, 135 | 14, 136 | ), 137 | ( 138 | "0:387d7b2854430a64560179fcbdf9d3ba0950d6aaf481bd3664630d6211a7a23e", 139 | "0:3334dd1d2f96f3907f7332d6974c383f51500130ce1bc00480020bbb52b8dfb3", 140 | 180000000, 141 | 15, 142 | ), 143 | ( 144 | "0:962a78c7dfa5605327730fe3110eb3caed1310d6715de59ae6d4e6907c0ef2be", 145 | "0:9999be620c23631cf75b16c53201e3bad9528c17118e9d124b644fb649bfdbad", 146 | 130000000, 147 | 18, 148 | ), 149 | ( 150 | "0:8e63172ad2163783e195909a5e81aff8004dbf4357ad98400a4f84d9576d3701", 151 | "0:8888b8c017f3afa3c16b394959fed181153edb7253c827c6ffd68ddb88560c8e", 152 | 20000000, 153 | 24, 154 | ), 155 | ( 156 | "0:21e6c0a5a7a9f287d974601e594262a963f2df755de0714afbed3412698a03f6", 157 | "0:2222d76864be64b9b872df63a311b1217d1cba3fe687cd107fd29b9b7fe01570", 158 | 190000000, 159 | 28, 160 | ), 161 | ( 162 | "0:81de3632ee719ff174a0fe1318c28cbb0c80a8903a112fe4b864a193fbfa2584", 163 | "0:8888b8c017f3afa3c16b394959fed181153edb7253c827c6ffd68ddb88560c8e", 164 | 250000000, 165 | 30, 166 | ), 167 | ( 168 | "0:2f8737396a897945a082390fbb476a3151f2881dbd1cf6180b05fd000bfb205a", 169 | "0:2222d76864be64b9b872df63a311b1217d1cba3fe687cd107fd29b9b7fe01570", 170 | 140000000, 171 | 37, 172 | ), 173 | ( 174 | "0:49d5682a789d0d7820a9a9657df0280c87597e312344cdda08c51dd46996c00a", 175 | "0:44442bf5dd7b2bd3653537e116a255baff32cfbc7af9f10128f2897eba3efd0b", 176 | 240000000, 177 | 46, 178 | ), 179 | ]; 180 | 181 | for (wallet_addr, factory_addr, tokens, nonce) in inputs { 182 | let wallet_addr = wallet_addr.parse::().unwrap(); 183 | let factory_addr = factory_addr.parse::().unwrap(); 184 | 185 | let mut group = c.benchmark_group("mine"); 186 | group.bench_with_input(BenchmarkId::from_parameter(nonce), &nonce, move |b, _| { 187 | b.iter(|| { 188 | thread_local! { 189 | static CODE_CELL: std::cell::UnsafeCell> = const { 190 | std::cell::UnsafeCell::new(None) 191 | }; 192 | } 193 | 194 | CODE_CELL.with(|code_cell| { 195 | let code_cell = { 196 | let slot = unsafe { &mut *code_cell.get() }; 197 | &*slot.get_or_insert_with(|| { 198 | Boc::decode_base64("te6ccgECDgEAAd0ABCSK7VMg4wMgwP/jAiDA/uMC8gsBAgMMAhD0pCD0vfLATgwEA1LtRNDXScMB+GYi0NMD+kAw+GmpOADcIccA4wIh1w0f8rwh4wMB2zzyPAUFBwLA7UTQ10nDAfhmjQhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE+Gkh2zzTAAGOH4MI1xgg+CjIzs7J+QAB0wABlNP/AwGTAvhC4vkQ8qiV0wAB8nri0z8BCQYAFHNvbCAwLjcxLjAACvhG8uBMAU74QyG58rQg+COBA+iogggbd0CgufK0+GPTHwH4I7zyudMfAds88jwHARQgghBCHlYVuuMCCAPEMPhCbuMA+EbycyGT1NHQ3vpA03/RW/hL0PpA1NHQ+kDTf9FbIPpCbxPXC//DAPhJWMcFsPLgZNs8cPsC+Ev4SvhJcMjPhYDKAM+EQM6CECeY5YTPC47L/8zJgwb7ANs88gAJCgsCdO1E0NdJwgGOr3DtRND0BXEhgED0DpPXC/+RcOJyIoBA9A+OgYjf+Gv4aoBA9A7yvdcL//hicPhj4w0MDQAKggnJw4AAKvhL+Er4Q/hCyMv/yz/Pg8v/zMntVAAAACztRNDT/9M/0wAx0//U0fhr+Gr4Y/hi").unwrap() 199 | }) 200 | }; 201 | 202 | let res = do_mine(code_cell, &factory_addr, &wallet_addr, tokens); 203 | assert_eq!(res.nonce, nonce); 204 | criterion::black_box( res); 205 | }) 206 | }); 207 | }); 208 | group.finish(); 209 | } 210 | } 211 | 212 | criterion_group!(mine, mine_address); 213 | criterion_main!(mine); 214 | -------------------------------------------------------------------------------- /benches/slice_uniform.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use everscale_types::prelude::*; 3 | 4 | fn test_uniform(c: &mut Criterion) { 5 | let cells = (0..=32) 6 | .chain([ 7 | 40, 60, 64, 80, 96, 127, 128, 160, 196, 200, 255, 256, 300, 400, 500, 600, 700, 800, 8 | 900, 1000, 1023, 9 | ]) 10 | .map(|bits| { 11 | let mut builder = CellBuilder::new(); 12 | builder.store_zeros(bits).unwrap(); 13 | builder.build().unwrap() 14 | }) 15 | .collect::>(); 16 | 17 | for cell in cells { 18 | let slice = cell.as_slice().unwrap(); 19 | c.bench_with_input( 20 | BenchmarkId::new("test slice uniform", slice.size_bits()), 21 | &slice, 22 | |b, slice| b.iter(|| black_box(slice.test_uniform())), 23 | ); 24 | } 25 | } 26 | 27 | criterion_group!(benches, test_uniform); 28 | criterion_main!(benches); 29 | -------------------------------------------------------------------------------- /benches/usage_cell.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use everscale_types::cell::RefsIter; 3 | use everscale_types::prelude::*; 4 | 5 | const BOC: &str = "te6ccgECCAEAAWQAAnPP9noJKCEBL3oZerOiIcNghuL96V3wIcuYOWQdvNC+2fqCEIJDQAAAAAAAAAAAAAAAAZa8xB6QABNAAgEAUO3QlUyMI4dEepUMw3Ou6oSqq8+1lyHkjOGFK6DAn6TXAAAAAAAAAAABFP8A9KQT9LzyyAsDAgEgBwQC5vJx1wEBwADyeoMI1xjtRNCDB9cB1ws/yPgozxYjzxbJ+QADcdcBAcMAmoMH1wFRE7ry4GTegEDXAYAg1wGAINcBVBZ1+RDyqPgju/J5Zr74I4EHCKCBA+ioUiC8sfJ0AiCCEEzuZGy64w8ByMv/yz/J7VQGBQA+ghAWnj4Ruo4R+AACkyDXSpd41wHUAvsA6NGTMvI84gCYMALXTND6QIMG1wFx1wF41wHXTPgAcIAQBKoCFLHIywVQBc8WUAP6AstpItAhzzEh10mghAm5mDNwAcsAWM8WlzBxAcsAEsziyQH7AAAE0jA="; 6 | 7 | fn traverse_cell_ordinary(c: &mut Criterion) { 8 | let cell = Boc::decode_base64(BOC).unwrap(); 9 | 10 | c.bench_function("traverse cell ordinary", |b| { 11 | b.iter(|| { 12 | let mut visitor = Visitor::default(); 13 | black_box(visitor.add_cell(cell.as_ref())); 14 | }) 15 | }); 16 | } 17 | 18 | fn traverse_cell_storage_cell(c: &mut Criterion) { 19 | let cell = Boc::decode_base64(BOC).unwrap(); 20 | let usage_tree = UsageTree::new(UsageTreeMode::OnDataAccess); 21 | let cell = usage_tree.track(&cell); 22 | 23 | c.bench_function("traverse cell usage tree", |b| { 24 | b.iter(|| { 25 | let mut visitor = Visitor::default(); 26 | black_box(visitor.add_cell(cell.as_ref())); 27 | }) 28 | }); 29 | } 30 | 31 | fn traverse_cell_storage_cell_with_capacity(c: &mut Criterion) { 32 | let cell = Boc::decode_base64(BOC).unwrap(); 33 | let usage_tree = UsageTree::with_mode_and_capacity(UsageTreeMode::OnDataAccess, 100); 34 | let cell = usage_tree.track(&cell); 35 | 36 | c.bench_function("traverse cell usage tree with capacity", |b| { 37 | b.iter(|| { 38 | let mut visitor = Visitor::default(); 39 | black_box(visitor.add_cell(cell.as_ref())); 40 | }) 41 | }); 42 | } 43 | 44 | #[derive(Default)] 45 | struct Visitor<'a> { 46 | visited: ahash::HashSet<&'a HashBytes>, 47 | stack: Vec>, 48 | } 49 | 50 | impl<'a> Visitor<'a> { 51 | fn add_cell(&mut self, cell: &'a DynCell) -> bool { 52 | if !self.visited.insert(cell.repr_hash()) { 53 | return true; 54 | } 55 | 56 | self.stack.clear(); 57 | self.stack.push(cell.references()); 58 | self.reduce_stack() 59 | } 60 | 61 | fn reduce_stack(&mut self) -> bool { 62 | 'outer: while let Some(item) = self.stack.last_mut() { 63 | for cell in item.by_ref() { 64 | if !self.visited.insert(cell.repr_hash()) { 65 | continue; 66 | } 67 | 68 | let mut slice = cell.as_slice().unwrap(); 69 | slice.load_bit().ok(); 70 | slice.load_u32().ok(); 71 | slice.load_small_uint(5).ok(); 72 | slice.load_reference().ok(); 73 | 74 | let next = cell.references(); 75 | if next.peek().is_some() { 76 | self.stack.push(next); 77 | continue 'outer; 78 | } 79 | } 80 | 81 | self.stack.pop(); 82 | } 83 | 84 | true 85 | } 86 | } 87 | 88 | criterion_group!( 89 | benches, 90 | traverse_cell_ordinary, 91 | traverse_cell_storage_cell, 92 | traverse_cell_storage_cell_with_capacity 93 | ); 94 | criterion_main!(benches); 95 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "everscale-types-fuzz" 3 | version = "0.0.0" 4 | authors = ["Automatically generated"] 5 | publish = false 6 | edition = "2021" 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies] 12 | arbitrary = { version = "1.4", features = ["derive"] } 13 | libfuzzer-sys = "0.4" 14 | 15 | [dependencies.everscale-types] 16 | path = ".." 17 | features = ["base64", "arbitrary"] 18 | 19 | [[bin]] 20 | name = "base64_addr" 21 | path = "fuzz_targets/base64_addr.rs" 22 | test = false 23 | doc = false 24 | 25 | [[bin]] 26 | name = "boc_decode" 27 | path = "fuzz_targets/boc_decode.rs" 28 | test = false 29 | doc = false 30 | 31 | [[bin]] 32 | name = "boc_encode" 33 | path = "fuzz_targets/boc_encode.rs" 34 | test = false 35 | doc = false 36 | 37 | [[bin]] 38 | name = "boc_decode_pair" 39 | path = "fuzz_targets/boc_decode_pair.rs" 40 | test = false 41 | doc = false 42 | 43 | [[bin]] 44 | name = "boc_decode_encode" 45 | path = "fuzz_targets/boc_decode_encode.rs" 46 | test = false 47 | doc = false 48 | 49 | [[bin]] 50 | name = "boc_dict" 51 | path = "fuzz_targets/boc_dict.rs" 52 | test = false 53 | doc = false 54 | 55 | [[bin]] 56 | name = "boc_message" 57 | path = "fuzz_targets/boc_message.rs" 58 | test = false 59 | doc = false 60 | 61 | [[bin]] 62 | name = "dict_modify" 63 | path = "fuzz_targets/dict_modify.rs" 64 | test = false 65 | doc = false 66 | 67 | [[bin]] 68 | name = "dict_modify_aug" 69 | path = "fuzz_targets/dict_modify_aug.rs" 70 | test = false 71 | doc = false 72 | 73 | [[bin]] 74 | name = "dict_modify_aug_with_cells" 75 | path = "fuzz_targets/dict_modify_aug_with_cells.rs" 76 | test = false 77 | doc = false 78 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/base64_addr.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use everscale_types::models::{StdAddr, StdAddrFormat}; 3 | use libfuzzer_sys::{fuzz_target, Corpus}; 4 | 5 | fuzz_target!(|data: &[u8]| -> Corpus { 6 | if let Ok(s) = std::str::from_utf8(data) { 7 | if StdAddr::from_str_ext(s, StdAddrFormat::any()).is_ok() { 8 | return Corpus::Keep; 9 | } 10 | } 11 | Corpus::Reject 12 | }); 13 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/boc_decode.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use everscale_types::prelude::Boc; 3 | use libfuzzer_sys::fuzz_target; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | if let Ok(cell) = Boc::decode(data) { 7 | if let Ok(mut slice) = cell.as_slice() { 8 | _ = slice.get_u8(0); 9 | _ = slice.get_u16(0); 10 | _ = slice.get_u32(0); 11 | _ = slice.get_u64(0); 12 | _ = slice.get_u128(0); 13 | _ = slice.get_u256(0); 14 | if slice.skip_first(3, 0).is_ok() { 15 | _ = slice.get_u8(0); 16 | _ = slice.get_u16(0); 17 | _ = slice.get_u32(0); 18 | _ = slice.get_u64(0); 19 | _ = slice.get_u128(0); 20 | _ = slice.get_u256(0); 21 | } 22 | } 23 | } 24 | }); 25 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/boc_decode_encode.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use everscale_types::prelude::Boc; 3 | use libfuzzer_sys::fuzz_target; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | if let Ok(cell) = Boc::decode(data) { 7 | _ = Boc::encode(cell.as_ref()); 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/boc_decode_pair.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use everscale_types::prelude::Boc; 3 | use libfuzzer_sys::fuzz_target; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | _ = Boc::decode_pair(data); 7 | }); 8 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/boc_dict.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use everscale_types::prelude::{Boc, RawDict}; 3 | use libfuzzer_sys::{fuzz_target, Corpus}; 4 | 5 | fuzz_target!(|data: &[u8]| -> Corpus { 6 | if let Ok(cell) = Boc::decode(data) { 7 | if let Ok(map) = cell.parse::>() { 8 | _ = map.iter().count(); 9 | return Corpus::Keep; 10 | } 11 | } 12 | Corpus::Reject 13 | }); 14 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/boc_encode.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use everscale_types::prelude::{Boc, Cell}; 3 | use libfuzzer_sys::fuzz_target; 4 | 5 | fuzz_target!(|cell: Cell| { 6 | _ = Boc::encode(cell); 7 | }); 8 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/boc_message.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use everscale_types::models::Message; 3 | use everscale_types::prelude::Boc; 4 | use libfuzzer_sys::{fuzz_target, Corpus}; 5 | 6 | fuzz_target!(|data: &[u8]| -> Corpus { 7 | if let Ok(cell) = Boc::decode(data) { 8 | if cell.parse::().is_ok() { 9 | return Corpus::Keep; 10 | } 11 | } 12 | Corpus::Reject 13 | }); 14 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/common.rs: -------------------------------------------------------------------------------- 1 | use arbitrary::Arbitrary; 2 | use everscale_types::dict::{self, AugDictExtra, StoreDictKey}; 3 | use everscale_types::prelude::*; 4 | 5 | #[derive(Debug)] 6 | pub struct AugInput { 7 | pub initial_items: Vec<(K, A, V)>, 8 | pub operations: Vec>, 9 | } 10 | 11 | impl AugInput 12 | where 13 | K: StoreDictKey + Ord + Copy, 14 | for<'a> A: AugDictExtra + Store + Load<'a>, 15 | V: Store, 16 | { 17 | pub fn compare_manual_vs_batched(self) { 18 | let ctx = Cell::empty_context(); 19 | 20 | let initial = 21 | dict::build_aug_dict_from_sorted_iter(self.initial_items.into_iter(), A::comp_add, ctx) 22 | .unwrap(); 23 | 24 | // Build manually 25 | let mut target = initial.clone(); 26 | for op in &self.operations { 27 | let mut b = CellDataBuilder::new(); 28 | match op { 29 | AugOperation::Remove { key } => { 30 | key.store_into_data(&mut b).unwrap(); 31 | dict::aug_dict_remove_owned( 32 | &mut target, 33 | &mut b.as_data_slice(), 34 | K::BITS, 35 | false, 36 | A::comp_add, 37 | ctx, 38 | ) 39 | .unwrap(); 40 | } 41 | AugOperation::Insert { key, extra, value } => { 42 | key.store_into_data(&mut b).unwrap(); 43 | dict::aug_dict_insert( 44 | &mut target, 45 | &mut b.as_data_slice(), 46 | K::BITS, 47 | extra, 48 | value, 49 | dict::SetMode::Set, 50 | A::comp_add, 51 | ctx, 52 | ) 53 | .unwrap(); 54 | } 55 | } 56 | } 57 | 58 | // Build batched 59 | let mut result = initial.clone(); 60 | dict::aug_dict_modify_from_sorted_iter( 61 | &mut result, 62 | self.operations, 63 | |op| *op.key(), 64 | |op| { 65 | Ok(match op { 66 | AugOperation::Remove { .. } => None, 67 | AugOperation::Insert { extra, value, .. } => Some((extra, value)), 68 | }) 69 | }, 70 | A::comp_add, 71 | ctx, 72 | ) 73 | .unwrap(); 74 | 75 | // 76 | assert_eq!(result, target); 77 | } 78 | } 79 | 80 | impl<'a, const MAX_N: usize, K, A, V> Arbitrary<'a> for AugInput 81 | where 82 | K: Arbitrary<'a> + PartialEq + Ord, 83 | A: Arbitrary<'a>, 84 | V: Arbitrary<'a>, 85 | { 86 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { 87 | let item_count = u.int_in_range(0..=MAX_N)?; 88 | let mut initial_items = Vec::<(K, A, V)>::with_capacity(item_count); 89 | for _ in 0..item_count { 90 | initial_items.push(u.arbitrary()?); 91 | } 92 | initial_items.sort_unstable_by(|(a, ..), (b, ..)| a.cmp(b)); 93 | initial_items.dedup_by(|(a, ..), (b, ..)| (*a).eq(b)); 94 | 95 | let operation_count = u.int_in_range(0..=MAX_N)?; 96 | let mut operations = Vec::>::with_capacity(operation_count); 97 | for _ in 0..operation_count { 98 | operations.push(u.arbitrary()?); 99 | } 100 | operations.sort_unstable_by(|a, b| a.key().cmp(b.key())); 101 | operations.dedup_by(|a, b| a.key().eq(b.key())); 102 | 103 | Ok(Self { 104 | initial_items, 105 | operations, 106 | }) 107 | } 108 | } 109 | 110 | #[derive(Debug, Arbitrary)] 111 | pub enum AugOperation { 112 | Remove { key: K }, 113 | Insert { key: K, extra: A, value: V }, 114 | } 115 | 116 | impl AugOperation { 117 | pub fn key(&self) -> &K { 118 | match self { 119 | Self::Remove { key } => key, 120 | Self::Insert { key, .. } => key, 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/dict_modify.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use arbitrary::Arbitrary; 3 | use everscale_types::dict; 4 | use everscale_types::prelude::*; 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | fuzz_target!(|input: Input<1000, u32, u64>| compare_manual_vs_batched(input)); 8 | 9 | fn compare_manual_vs_batched(input: Input) 10 | where 11 | K: StoreDictKey + Ord + Copy, 12 | V: Store, 13 | { 14 | let ctx = Cell::empty_context(); 15 | 16 | let initial = dict::build_dict_from_sorted_iter(input.initial_items.into_iter(), ctx).unwrap(); 17 | 18 | // Build manually 19 | let mut target = initial.clone(); 20 | for op in &input.operations { 21 | let mut b = CellDataBuilder::new(); 22 | match op { 23 | Operation::Remove { key } => { 24 | key.store_into_data(&mut b).unwrap(); 25 | dict::dict_remove_owned(&mut target, &mut b.as_data_slice(), K::BITS, false, ctx) 26 | .unwrap(); 27 | } 28 | Operation::Insert { key, value } => { 29 | key.store_into_data(&mut b).unwrap(); 30 | dict::dict_insert( 31 | &mut target, 32 | &mut b.as_data_slice(), 33 | K::BITS, 34 | value, 35 | dict::SetMode::Set, 36 | ctx, 37 | ) 38 | .unwrap(); 39 | } 40 | } 41 | } 42 | 43 | // Build batched 44 | let mut result = initial.clone(); 45 | dict::dict_modify_from_sorted_iter( 46 | &mut result, 47 | input.operations, 48 | |op| *op.key(), 49 | |op| { 50 | Ok(match op { 51 | Operation::Remove { .. } => None, 52 | Operation::Insert { value, .. } => Some(value), 53 | }) 54 | }, 55 | ctx, 56 | ) 57 | .unwrap(); 58 | 59 | // 60 | assert_eq!(result, target); 61 | } 62 | 63 | #[derive(Debug)] 64 | struct Input { 65 | initial_items: Vec<(K, V)>, 66 | operations: Vec>, 67 | } 68 | 69 | impl<'a, const MAX_N: usize, K, V> Arbitrary<'a> for Input 70 | where 71 | K: Arbitrary<'a> + PartialEq + Ord, 72 | V: Arbitrary<'a>, 73 | { 74 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { 75 | let item_count = u.int_in_range(0..=MAX_N)?; 76 | let mut initial_items = Vec::<(K, V)>::with_capacity(item_count); 77 | for _ in 0..item_count { 78 | initial_items.push(u.arbitrary()?); 79 | } 80 | initial_items.sort_unstable_by(|(a, _), (b, _)| a.cmp(b)); 81 | initial_items.dedup_by(|(a, _), (b, _)| (*a).eq(b)); 82 | 83 | let operation_count = u.int_in_range(0..=MAX_N)?; 84 | let mut operations = Vec::>::with_capacity(operation_count); 85 | for _ in 0..operation_count { 86 | operations.push(u.arbitrary()?); 87 | } 88 | operations.sort_unstable_by(|a, b| a.key().cmp(b.key())); 89 | operations.dedup_by(|a, b| a.key().eq(b.key())); 90 | 91 | Ok(Self { 92 | initial_items, 93 | operations, 94 | }) 95 | } 96 | } 97 | 98 | #[derive(Debug, Arbitrary)] 99 | enum Operation { 100 | Remove { key: K }, 101 | Insert { key: K, value: V }, 102 | } 103 | 104 | impl Operation { 105 | fn key(&self) -> &K { 106 | match self { 107 | Self::Remove { key } => key, 108 | Self::Insert { key, .. } => key, 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/dict_modify_aug.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use arbitrary::Arbitrary; 3 | use everscale_types::dict::AugDictExtra; 4 | use everscale_types::error::Error; 5 | use everscale_types::prelude::*; 6 | use libfuzzer_sys::fuzz_target; 7 | 8 | use self::common::AugInput; 9 | 10 | mod common; 11 | 12 | fuzz_target!(|input: AugInput<1000, u32, SimpleAug, u64>| input.compare_manual_vs_batched()); 13 | 14 | #[derive(Default, Debug, Store, Load, Clone, Copy, Arbitrary)] 15 | struct SimpleAug(u32); 16 | 17 | impl AugDictExtra for SimpleAug { 18 | fn comp_add( 19 | left: &mut CellSlice, 20 | right: &mut CellSlice, 21 | b: &mut CellBuilder, 22 | _: &dyn CellContext, 23 | ) -> Result<(), Error> { 24 | let left = left.load_u32()?; 25 | let right = right.load_u32()?; 26 | b.store_u32(left.saturating_add(right)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/dict_modify_aug_with_cells.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use arbitrary::Arbitrary; 3 | use everscale_types::cell::Lazy; 4 | use everscale_types::dict::AugDictExtra; 5 | use everscale_types::error::Error; 6 | use everscale_types::prelude::*; 7 | use libfuzzer_sys::fuzz_target; 8 | 9 | use self::common::AugInput; 10 | 11 | mod common; 12 | 13 | fuzz_target!(|input: AugInput<1000, u32, AugWithCell, u64>| input.compare_manual_vs_batched()); 14 | 15 | #[derive(Default, Debug, Store, Load, Clone, Arbitrary)] 16 | struct AugWithCell(u32, Option>); 17 | 18 | impl AugDictExtra for AugWithCell { 19 | fn comp_add( 20 | left: &mut CellSlice, 21 | right: &mut CellSlice, 22 | b: &mut CellBuilder, 23 | ctx: &dyn CellContext, 24 | ) -> Result<(), Error> { 25 | let left = Self::load_from(left)?; 26 | let right = Self::load_from(right)?; 27 | 28 | let sum = left.0.saturating_add(right.0); 29 | let cell = match (left.1, right.1) { 30 | (None, None) => Some(Lazy::new(&!sum)?), 31 | (Some(cell), None) | (None, Some(cell)) => { 32 | let cell = cell.load()?; 33 | Some(Lazy::new(&(cell & sum))?) 34 | } 35 | (Some(left), Some(right)) => { 36 | let left = left.load()?; 37 | let right = right.load()?; 38 | let cell_sum = left.saturating_add(right); 39 | (sum & 1 != cell_sum & 1) 40 | .then(|| Lazy::new(&cell_sum)) 41 | .transpose()? 42 | } 43 | }; 44 | 45 | AugWithCell(sum, cell).store_into(b, ctx) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /proc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "everscale-types-proc" 3 | description = "Proc-macro helpers for everscale-types" 4 | authors = ["Ivan Kalinin "] 5 | repository = "https://github.com/broxus/everscale-types" 6 | version = "0.1.5" 7 | edition = "2021" 8 | include = ["src/**/*.rs", "../LICENSE-*", "../README.md"] 9 | license = "MIT OR Apache-2.0" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | proc-macro2 = "1.0" 16 | quote = "1.0" 17 | syn = { version = "2.0.87", features = ["visit", "full"] } 18 | -------------------------------------------------------------------------------- /proc/src/bound.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use syn::punctuated::Pair; 4 | use syn::visit::{self, Visit}; 5 | 6 | use crate::internals::{ast, attr}; 7 | 8 | pub fn without_default(generics: &syn::Generics) -> syn::Generics { 9 | syn::Generics { 10 | params: generics 11 | .params 12 | .iter() 13 | .map(|param| match param { 14 | syn::GenericParam::Type(param) => syn::GenericParam::Type(syn::TypeParam { 15 | eq_token: None, 16 | default: None, 17 | ..param.clone() 18 | }), 19 | _ => param.clone(), 20 | }) 21 | .collect(), 22 | ..generics.clone() 23 | } 24 | } 25 | 26 | #[allow(unused)] 27 | pub fn with_bound( 28 | container: &ast::Container, 29 | generics: &syn::Generics, 30 | filter: fn(&attr::Field, Option<&attr::Variant>) -> bool, 31 | bound: &syn::Path, 32 | ) -> syn::Generics { 33 | struct FindTyParams<'ast> { 34 | all_type_params: HashSet, 35 | relevant_type_params: HashSet, 36 | associated_type_usage: Vec<&'ast syn::TypePath>, 37 | } 38 | 39 | impl<'ast> Visit<'ast> for FindTyParams<'ast> { 40 | fn visit_field(&mut self, field: &'ast syn::Field) { 41 | if let syn::Type::Path(ty) = ungroup(&field.ty) { 42 | if let Some(Pair::Punctuated(t, _)) = ty.path.segments.pairs().next() { 43 | if self.all_type_params.contains(&t.ident) { 44 | self.associated_type_usage.push(ty); 45 | } 46 | } 47 | } 48 | self.visit_type(&field.ty); 49 | } 50 | 51 | fn visit_macro(&mut self, _mac: &'ast syn::Macro) {} 52 | 53 | fn visit_path(&mut self, path: &'ast syn::Path) { 54 | if let Some(seg) = path.segments.last() { 55 | if seg.ident == "PhantomData" { 56 | return; 57 | } 58 | } 59 | if path.leading_colon.is_none() && path.segments.len() == 1 { 60 | let id = &path.segments[0].ident; 61 | if self.all_type_params.contains(id) { 62 | self.relevant_type_params.insert(id.clone()); 63 | } 64 | } 65 | visit::visit_path(self, path); 66 | } 67 | } 68 | 69 | let all_type_params = generics 70 | .type_params() 71 | .map(|param| param.ident.clone()) 72 | .collect(); 73 | 74 | let mut visitor = FindTyParams { 75 | all_type_params, 76 | relevant_type_params: HashSet::new(), 77 | associated_type_usage: Vec::new(), 78 | }; 79 | 80 | match &container.data { 81 | ast::Data::Enum(variants) => { 82 | for variant in variants.iter() { 83 | let relevant_fields = variant 84 | .fields 85 | .iter() 86 | .filter(|field| filter(&field.attrs, Some(&variant.attrs))); 87 | 88 | for field in relevant_fields { 89 | visitor.visit_field(field.original); 90 | } 91 | } 92 | } 93 | ast::Data::Struct(_, fields) => { 94 | for field in fields.iter().filter(|field| filter(&field.attrs, None)) { 95 | visitor.visit_field(field.original); 96 | } 97 | } 98 | } 99 | 100 | let relevant_type_params = visitor.relevant_type_params; 101 | let associated_type_params = visitor.associated_type_usage; 102 | 103 | let new_predicates = generics 104 | .type_params() 105 | .map(|param| param.ident.clone()) 106 | .filter(|ident| relevant_type_params.contains(ident)) 107 | .map(|ident| syn::TypePath { 108 | qself: None, 109 | path: ident.into(), 110 | }) 111 | .chain(associated_type_params.into_iter().cloned()) 112 | .map(|bounded_ty| { 113 | syn::WherePredicate::Type(syn::PredicateType { 114 | lifetimes: None, 115 | bounded_ty: syn::Type::Path(bounded_ty), 116 | colon_token: ::default(), 117 | bounds: vec![syn::TypeParamBound::Trait(syn::TraitBound { 118 | paren_token: None, 119 | modifier: syn::TraitBoundModifier::None, 120 | lifetimes: None, 121 | path: bound.clone(), 122 | })] 123 | .into_iter() 124 | .collect(), 125 | }) 126 | }); 127 | 128 | let mut generics = generics.clone(); 129 | generics 130 | .make_where_clause() 131 | .predicates 132 | .extend(new_predicates); 133 | generics 134 | } 135 | 136 | fn ungroup(mut ty: &syn::Type) -> &syn::Type { 137 | while let syn::Type::Group(group) = ty { 138 | ty = &group.elem; 139 | } 140 | ty 141 | } 142 | -------------------------------------------------------------------------------- /proc/src/derive_store.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeSet; 2 | 3 | use proc_macro2::TokenStream; 4 | use quote::quote; 5 | 6 | use crate::internals::{ast, attr, ctxt}; 7 | use crate::{bound, Derive}; 8 | 9 | pub fn impl_derive(input: syn::DeriveInput) -> Result> { 10 | let cx = ctxt::Ctxt::new(); 11 | let container = match ast::Container::from_ast(&cx, &input, Derive::Debug) { 12 | Some(container) => container, 13 | None => return Err(cx.check().unwrap_err()), 14 | }; 15 | cx.check()?; 16 | 17 | let ident = &container.ident; 18 | let generics = bound::without_default(container.generics); 19 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 20 | 21 | let (inline, body) = match &container.data { 22 | ast::Data::Enum(variants) => (variants.len() < 2, build_enum(variants)), 23 | ast::Data::Struct(style, fields) => { 24 | (fields.len() < 2, build_struct(&container, *style, fields)) 25 | } 26 | }; 27 | 28 | let inline = if inline { quote!(#[inline]) } else { quote!() }; 29 | 30 | let result = quote! { 31 | #[automatically_derived] 32 | impl #impl_generics ::everscale_types::cell::Store for #ident #ty_generics #where_clause { 33 | #inline 34 | fn store_into( 35 | &self, 36 | __builder: &mut ::everscale_types::cell::CellBuilder, 37 | __context: &dyn ::everscale_types::cell::CellContext, 38 | ) -> ::core::result::Result<(), ::everscale_types::error::Error> { 39 | #body 40 | } 41 | } 42 | }; 43 | 44 | Ok(result) 45 | } 46 | 47 | fn build_enum(_: &[ast::Variant<'_>]) -> TokenStream { 48 | panic!("Unsupported") 49 | } 50 | 51 | fn build_struct( 52 | container: &ast::Container<'_>, 53 | style: ast::Style, 54 | fields: &[ast::Field<'_>], 55 | ) -> TokenStream { 56 | let mut tag_version = None; 57 | let (compute_tag_version, store_tag) = match &container.attrs.tlb_tag { 58 | attr::ContainerTag::None => (None, None), 59 | attr::ContainerTag::Single(tag) => (None, store_tag(*tag)), 60 | attr::ContainerTag::Multiple(tags) => store_tags_versioned(tags, fields, &mut tag_version), 61 | }; 62 | 63 | let fields_len = fields.len(); 64 | let members = fields.iter().enumerate().map(|(i, field)| { 65 | let ident = &field.member; 66 | let field_ident = quote!(self.#ident); 67 | let op = store_op(&field_ident, field.ty); 68 | 69 | let is_last = i + 1 == fields_len; 70 | match (field.attrs.since_tag, &tag_version) { 71 | (Some(since), Some(tag_version)) if is_last => { 72 | quote! { 73 | if #tag_version >= #since { 74 | #op 75 | } else { 76 | Ok(()) 77 | } 78 | } 79 | } 80 | (Some(since), Some(tag_version)) => { 81 | let op = into_ok(op); 82 | quote! { 83 | if #tag_version >= #since { 84 | #op 85 | } 86 | } 87 | } 88 | _ if is_last => op, 89 | _ => into_ok(op), 90 | } 91 | }); 92 | 93 | match style { 94 | ast::Style::Unit => match store_tag { 95 | Some(store_tag) => store_tag, 96 | None => quote!(::core::result::Result::Ok(())), 97 | }, 98 | _ => { 99 | let validate_with = container.attrs.tlb_validate_with.as_ref().map(|expr| { 100 | quote!(if !#expr(self) { 101 | return ::core::result::Result::Err(::everscale_types::error::Error::InvalidData); 102 | }) 103 | }); 104 | let store_tag = store_tag.map(into_ok); 105 | quote! { 106 | #validate_with 107 | #compute_tag_version 108 | #store_tag 109 | #(#members)* 110 | } 111 | } 112 | } 113 | } 114 | 115 | fn store_tag(tag: attr::TlbTag) -> Option { 116 | let bits = tag.bits as u16; 117 | 118 | let op = match bits { 119 | 0 => return None, 120 | 1 if tag.value == 0 => quote!(store_bit_zero()), 121 | 1 => quote!(store_bit_one()), 122 | 2..=7 => { 123 | let value = tag.value as u8; 124 | quote!(store_small_uint(#value, #bits)) 125 | } 126 | 8 => { 127 | let value = tag.value as u8; 128 | quote!(store_u8(#value)) 129 | } 130 | 16 => { 131 | let value = tag.value as u16; 132 | quote!(store_u16(#value)) 133 | } 134 | 32 => { 135 | let value = tag.value; 136 | quote!(store_u32(#value)) 137 | } 138 | _ => { 139 | let value = tag.value as u64; 140 | quote!(store_uint(#value, #bits)) 141 | } 142 | }; 143 | Some(quote!(__builder.#op)) 144 | } 145 | 146 | // Returns (tag_version, stop_tag) 147 | fn store_tags_versioned( 148 | tags: &[attr::TlbTag], 149 | fields: &[ast::Field<'_>], 150 | tag_version: &mut Option, 151 | ) -> (Option, Option) { 152 | let Some(first_tag) = tags.first() else { 153 | return (None, None); 154 | }; 155 | 156 | let mut used_tags = BTreeSet::new(); 157 | let mut version_guards = Vec::new(); 158 | for field in fields { 159 | if let Some(since) = field.attrs.since_tag { 160 | used_tags.insert(since); 161 | 162 | let tag_version = 163 | tag_version.get_or_insert_with(|| quote::format_ident!("__tag_version")); 164 | 165 | let ident = &field.member; 166 | let ty = field.ty; 167 | 168 | // TODO: Optimize codegen 169 | version_guards.push(quote! { 170 | if self.#ident != <#ty as Default>::default() { 171 | #tag_version = std::cmp::max(#tag_version, #since); 172 | } 173 | }); 174 | } 175 | } 176 | 177 | let Some(tag_version) = &tag_version else { 178 | return (None, store_tag(*first_tag)); 179 | }; 180 | 181 | used_tags.remove(&0); 182 | let match_arms = used_tags.into_iter().filter_map(|tag_index| { 183 | let store_op = store_tag(tags[tag_index])?; 184 | Some(quote! { #tag_index => #store_op }) 185 | }); 186 | let Some(store_first) = store_tag(*first_tag) else { 187 | return (None, None); 188 | }; 189 | 190 | let store_tag = quote! { 191 | match #tag_version { 192 | #(#match_arms),*, 193 | _ => #store_first, 194 | } 195 | }; 196 | 197 | let compute_tag_version = quote! { 198 | let mut #tag_version = 0usize; 199 | #(#version_guards)* 200 | }; 201 | (Some(compute_tag_version), Some(store_tag)) 202 | } 203 | 204 | fn store_op(field_ident: &TokenStream, ty: &syn::Type) -> TokenStream { 205 | #[allow(clippy::unnecessary_operation)] 206 | 'fallback: { 207 | match ty { 208 | syn::Type::Path(syn::TypePath { path, .. }) => { 209 | if let Some(syn::PathSegment { ident, .. }) = path.segments.last() { 210 | let op = match ident.to_string().as_str() { 211 | "bool" => quote!(store_bit(#field_ident)), 212 | "i8" => quote!(store_u8(#field_ident as u8)), 213 | "u8" => quote!(store_u8(#field_ident)), 214 | "i16" => quote!(store_u16(#field_ident as u16)), 215 | "u16" => quote!(store_u16(#field_ident)), 216 | "i32" => quote!(store_u32(#field_ident as u32)), 217 | "u32" => quote!(store_u32(#field_ident)), 218 | "i64" => quote!(store_u64(#field_ident as u64)), 219 | "u64" => quote!(store_u64(#field_ident)), 220 | "NonZeroU8" => quote!(store_u8(#field_ident.get())), 221 | "NonZeroU16" => quote!(store_u16(#field_ident.get())), 222 | "NonZeroU32" => quote!(store_u32(#field_ident.get())), 223 | "HashBytes" => quote!(store_u256(&#field_ident)), 224 | "CellContainer" => quote!(store_reference(#field_ident.clone())), 225 | _ => break 'fallback, 226 | }; 227 | 228 | return quote!(__builder.#op); 229 | } 230 | } 231 | syn::Type::Reference(syn::TypeReference { elem, .. }) => { 232 | return store_op(field_ident, elem); 233 | } 234 | _ => break 'fallback, 235 | } 236 | }; 237 | 238 | quote! { <#ty as ::everscale_types::cell::Store>::store_into(&#field_ident, __builder, __context) } 239 | } 240 | 241 | fn into_ok(tokens: TokenStream) -> TokenStream { 242 | quote!(match #tokens { 243 | ::core::result::Result::Ok(_) => {}, 244 | ::core::result::Result::Err(err) => return ::core::result::Result::Err(err), 245 | }) 246 | } 247 | -------------------------------------------------------------------------------- /proc/src/internals/ast.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use syn::punctuated::Punctuated; 4 | use syn::Token; 5 | 6 | use super::attr; 7 | use super::ctxt::*; 8 | use crate::Derive; 9 | 10 | pub struct Container<'a> { 11 | pub ident: syn::Ident, 12 | pub attrs: attr::Container, 13 | pub data: Data<'a>, 14 | pub generics: &'a syn::Generics, 15 | #[allow(unused)] 16 | pub original: &'a syn::DeriveInput, 17 | } 18 | 19 | impl<'a> Container<'a> { 20 | pub(crate) fn from_ast(cx: &Ctxt, item: &'a syn::DeriveInput, _derive: Derive) -> Option { 21 | let attrs = attr::Container::from_ast(cx, item); 22 | 23 | let data = match &item.data { 24 | syn::Data::Enum(data) => Data::Enum(enum_from_ast(cx, &data.variants)), 25 | syn::Data::Struct(data) => { 26 | let (style, fields) = struct_from_ast(cx, &data.fields); 27 | Data::Struct(style, fields) 28 | } 29 | syn::Data::Union(_) => { 30 | cx.error_spanned_by(item, "tl doesn't support derive for unions"); 31 | return None; 32 | } 33 | }; 34 | 35 | let container = Self { 36 | ident: item.ident.clone(), 37 | attrs, 38 | data, 39 | generics: &item.generics, 40 | original: item, 41 | }; 42 | 43 | container.validate(cx); 44 | 45 | Some(container) 46 | } 47 | 48 | fn validate(&self, cx: &Ctxt) { 49 | match &self.data { 50 | Data::Enum(variants) => { 51 | if matches!(self.attrs.tlb_tag, attr::ContainerTag::Multiple(_)) { 52 | cx.error_spanned_by(self.original, "multi-tags for enum are not supported"); 53 | } 54 | 55 | for var in variants { 56 | for field in &var.fields { 57 | if field.attrs.since_tag.is_some() { 58 | cx.error_spanned_by( 59 | field.original, 60 | "since_tag is not supported for enum fields", 61 | ); 62 | } 63 | } 64 | } 65 | } 66 | Data::Struct(_, fields) => { 67 | let mut unused_tags = HashSet::new(); 68 | let tag_count = match &self.attrs.tlb_tag { 69 | attr::ContainerTag::None => 0, 70 | attr::ContainerTag::Single(_) => 1, 71 | attr::ContainerTag::Multiple(tags) => { 72 | let mut tag_len = None; 73 | for (i, tag) in tags.iter().enumerate() { 74 | if i > 0 { 75 | unused_tags.insert(i); 76 | } 77 | 78 | match tag_len { 79 | None => tag_len = Some(tag.bits), 80 | Some(bits) if tag.bits != bits => { 81 | cx.error_spanned_by( 82 | self.original, 83 | "all tags must have the same bit length", 84 | ); 85 | } 86 | Some(_) => {} 87 | } 88 | } 89 | 90 | tags.len() 91 | } 92 | }; 93 | 94 | for field in fields { 95 | if let Some(since) = field.attrs.since_tag { 96 | unused_tags.remove(&since); 97 | 98 | if tag_count == 0 { 99 | cx.error_spanned_by( 100 | field.original, 101 | "since_tag is specified but there are no tags for this struct", 102 | ); 103 | } else if since >= tag_count { 104 | cx.error_spanned_by( 105 | field.original, 106 | format!( 107 | "since_tag is out of bounds (max tag index is {})", 108 | tag_count - 1 109 | ), 110 | ); 111 | } 112 | } 113 | } 114 | 115 | for i in unused_tags { 116 | cx.error_spanned_by(self.original, format!("tag {i} is not used")); 117 | } 118 | } 119 | } 120 | } 121 | } 122 | 123 | fn enum_from_ast<'a>( 124 | cx: &Ctxt, 125 | variants: &'a Punctuated, 126 | ) -> Vec> { 127 | variants 128 | .iter() 129 | .map(|variant| { 130 | let attrs = attr::Variant::from_ast(cx, variant); 131 | let (style, fields) = struct_from_ast(cx, &variant.fields); 132 | Variant { 133 | ident: variant.ident.clone(), 134 | attrs, 135 | style, 136 | fields, 137 | original: variant, 138 | } 139 | }) 140 | .collect() 141 | } 142 | 143 | fn struct_from_ast<'a>(cx: &Ctxt, fields: &'a syn::Fields) -> (Style, Vec>) { 144 | match fields { 145 | syn::Fields::Named(fields) => (Style::Struct, fields_from_ast(cx, &fields.named)), 146 | syn::Fields::Unnamed(fields) => (Style::Tuple, fields_from_ast(cx, &fields.unnamed)), 147 | syn::Fields::Unit => (Style::Unit, Vec::new()), 148 | } 149 | } 150 | 151 | fn fields_from_ast<'a>(cx: &Ctxt, fields: &'a Punctuated) -> Vec> { 152 | fields 153 | .iter() 154 | .enumerate() 155 | .map(|(i, field)| Field { 156 | member: match &field.ident { 157 | Some(ident) => syn::Member::Named(ident.clone()), 158 | None => syn::Member::Unnamed(i.into()), 159 | }, 160 | attrs: attr::Field::from_ast(cx, field), 161 | ty: &field.ty, 162 | original: field, 163 | }) 164 | .collect() 165 | } 166 | 167 | pub enum Data<'a> { 168 | Enum(Vec>), 169 | Struct(Style, Vec>), 170 | } 171 | 172 | pub struct Variant<'a> { 173 | #[allow(unused)] 174 | pub ident: syn::Ident, 175 | pub attrs: attr::Variant, 176 | #[allow(unused)] 177 | pub style: Style, 178 | pub fields: Vec>, 179 | #[allow(unused)] 180 | pub original: &'a syn::Variant, 181 | } 182 | 183 | pub struct Field<'a> { 184 | pub member: syn::Member, 185 | pub attrs: attr::Field, 186 | pub ty: &'a syn::Type, 187 | pub original: &'a syn::Field, 188 | } 189 | 190 | #[derive(Debug, Copy, Clone, Eq, PartialEq)] 191 | pub enum Style { 192 | Struct, 193 | Tuple, 194 | Unit, 195 | } 196 | -------------------------------------------------------------------------------- /proc/src/internals/ctxt.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | 3 | use quote::ToTokens; 4 | 5 | pub struct Ctxt { 6 | errors: RefCell>, 7 | } 8 | 9 | impl Ctxt { 10 | pub fn new() -> Self { 11 | Self { 12 | errors: RefCell::new(Vec::new()), 13 | } 14 | } 15 | 16 | pub fn error_spanned_by(&self, object: T, message: M) 17 | where 18 | T: ToTokens, 19 | M: std::fmt::Display, 20 | { 21 | self.errors 22 | .borrow_mut() 23 | .push(syn::Error::new_spanned(object.into_token_stream(), message)); 24 | } 25 | 26 | pub fn syn_error(&self, error: syn::Error) { 27 | self.errors.borrow_mut().push(error) 28 | } 29 | 30 | pub fn check(self) -> Result<(), Vec> { 31 | let errors = std::mem::take(&mut *self.errors.borrow_mut()); 32 | match errors.len() { 33 | 0 => Ok(()), 34 | _ => Err(errors), 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /proc/src/internals/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ast; 2 | pub mod attr; 3 | pub mod ctxt; 4 | pub mod symbol; 5 | -------------------------------------------------------------------------------- /proc/src/internals/symbol.rs: -------------------------------------------------------------------------------- 1 | pub const TLB: Symbol = Symbol("tlb"); 2 | 3 | pub const VALIDATE_WITH: Symbol = Symbol("validate_with"); 4 | pub const TAG: Symbol = Symbol("tag"); 5 | pub const SINCE_TAG: Symbol = Symbol("since_tag"); 6 | 7 | #[derive(Copy, Clone)] 8 | pub struct Symbol(&'static str); 9 | 10 | impl PartialEq for syn::Ident { 11 | fn eq(&self, other: &Symbol) -> bool { 12 | self == other.0 13 | } 14 | } 15 | 16 | impl PartialEq for &syn::Ident { 17 | fn eq(&self, other: &Symbol) -> bool { 18 | *self == other.0 19 | } 20 | } 21 | 22 | impl PartialEq for syn::Path { 23 | fn eq(&self, other: &Symbol) -> bool { 24 | self.is_ident(other.0) 25 | } 26 | } 27 | 28 | impl PartialEq for &syn::Path { 29 | fn eq(&self, other: &Symbol) -> bool { 30 | self.is_ident(other.0) 31 | } 32 | } 33 | 34 | impl std::fmt::Display for Symbol { 35 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 36 | f.write_str(self.0) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /proc/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | 4 | mod bound; 5 | mod derive_load; 6 | mod derive_store; 7 | mod internals; 8 | 9 | #[derive(Copy, Clone, Eq, PartialEq)] 10 | enum Derive { 11 | Debug, 12 | } 13 | 14 | /// Implements `Load` for the type. 15 | #[proc_macro_derive(Load, attributes(bounds, tlb))] 16 | pub fn derive_load(input: TokenStream) -> TokenStream { 17 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 18 | derive_load::impl_derive(input) 19 | .unwrap_or_else(to_compile_errors) 20 | .into() 21 | } 22 | 23 | /// Implements `Store` for the type. 24 | #[proc_macro_derive(Store, attributes(bounds, tlb))] 25 | pub fn derive_store(input: TokenStream) -> TokenStream { 26 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 27 | derive_store::impl_derive(input) 28 | .unwrap_or_else(to_compile_errors) 29 | .into() 30 | } 31 | 32 | fn to_compile_errors(errors: Vec) -> proc_macro2::TokenStream { 33 | let compile_errors = errors.iter().map(syn::Error::to_compile_error); 34 | quote!(#(#compile_errors)*) 35 | } 36 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | format_code_in_doc_comments = true 2 | imports_granularity = "Module" 3 | normalize_comments = true 4 | overflow_delimited_expr = true 5 | group_imports = "StdExternalCrate" 6 | -------------------------------------------------------------------------------- /src/abi/error.rs: -------------------------------------------------------------------------------- 1 | //! ABI related error types. 2 | 3 | use std::sync::Arc; 4 | 5 | /// Error type for ABI version parsing related errors. 6 | #[derive(Debug, Clone, thiserror::Error)] 7 | pub enum ParseAbiVersionError { 8 | /// Expected a dot separated major and minor components. 9 | #[error("invalid format")] 10 | InvalidFormat, 11 | /// Failed to parse version component as number. 12 | #[error("invalid component")] 13 | InvalidComponent(#[source] std::num::ParseIntError), 14 | } 15 | 16 | /// Error type for ABI type parsing related errors. 17 | #[derive(Debug, Clone, thiserror::Error)] 18 | pub enum ParseAbiTypeError { 19 | /// Error while parsing array type. 20 | #[error("invalid array type")] 21 | InvalidArrayType, 22 | /// Error while parsing array length. 23 | #[error("invalid array length")] 24 | InvalidArrayLength(#[source] std::num::ParseIntError), 25 | /// Error while parsing integer bit length. 26 | #[error("invalid integer bit length")] 27 | InvalidBitLen(#[source] std::num::ParseIntError), 28 | /// Error while parsing varint or fixedbytes byte length. 29 | #[error("invalid byte length")] 30 | InvalidByteLen(#[source] std::num::ParseIntError), 31 | /// Expected type to be terminated with ')'. 32 | #[error("unterminated inner type")] 33 | UnterminatedInnerType, 34 | /// Expected value type for map. 35 | #[error("map value type not found")] 36 | ValueTypeNotFound, 37 | /// Invalid ABI type. 38 | #[error("unknown type")] 39 | UnknownType, 40 | } 41 | 42 | /// Error type for named ABI type parsing related errors. 43 | #[derive(Debug, Clone, thiserror::Error)] 44 | pub enum ParseNamedAbiTypeError { 45 | /// Error while parsing ABI type. 46 | #[error("invalid type `{ty}`: {error}")] 47 | InvalidType { 48 | /// Parsed ABI type. 49 | ty: Box, 50 | /// ABI type parsing error. 51 | #[source] 52 | error: ParseAbiTypeError, 53 | }, 54 | /// Components array was not expected. 55 | #[error("unexpected components for `{ty}`")] 56 | UnexpectedComponents { 57 | /// Parsed ABI type. 58 | ty: Box, 59 | }, 60 | /// Expected components array for tuple. 61 | #[error("expected components for `{ty}`")] 62 | ExpectedComponents { 63 | /// Parsed ABI type. 64 | ty: Box, 65 | }, 66 | } 67 | 68 | /// Error type for ABI values unpacking related errors. 69 | #[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)] 70 | pub enum AbiError { 71 | /// Expected a different value type. 72 | #[error("expected ABI type `{expected}`, got `{ty}`")] 73 | TypeMismatch { 74 | /// A full signature of the expected type. 75 | expected: Box, 76 | /// A full signature of the received type. 77 | ty: Box, 78 | }, 79 | /// More parameters were passed into state init than needed. 80 | #[error("unexpected init data parameter `{0}`")] 81 | UnexpectedInitDataParam(Arc), 82 | /// Field is absent in the init data dictionary. 83 | #[error("missing init data field `{0}`")] 84 | InitDataFieldNotFound(Arc), 85 | /// There are still some unconsumed bits or refs and we did not expect this. 86 | #[error("slice was not fully consumed during parsing")] 87 | IncompleteDeserialization, 88 | /// All cells for `bytes` or `fixedbytes` must have data multiple of 8. 89 | #[error("number of bits in a cell is not a multiple of 8")] 90 | ExpectedCellWithBytes, 91 | /// Expected a valid utf8-encoded string. 92 | #[error("invalid string")] 93 | InvalidString(#[from] std::str::Utf8Error), 94 | /// Invalid length for a fixedarrayN. 95 | #[error("expected array of len {expected}, got {len}")] 96 | ArraySizeMismatch { 97 | /// `N` in `fixedarrayN`. 98 | expected: usize, 99 | /// Length of the parsed array. 100 | len: usize, 101 | }, 102 | /// Invalid length for a fixedbytes{n}. 103 | #[error("expected bytes of len {expected}, got {len}")] 104 | BytesSizeMismatch { 105 | /// `N` in `fixedbytesN`. 106 | expected: usize, 107 | /// Length of the parsed bytes. 108 | len: usize, 109 | }, 110 | /// Address is required for signature for some ABI versions and it was not provided. 111 | #[error("an address was expected for signing but was not provided")] 112 | AddressNotProvided, 113 | /// Expected a different function id while decoding function input. 114 | #[error("expected input id 0x{expected:08x}, got 0x{id:08x}")] 115 | InputIdMismatch { 116 | /// Function input id. 117 | expected: u32, 118 | /// Id from parsed data. 119 | id: u32, 120 | }, 121 | /// Expected a different function id while decoding function output. 122 | #[error("expected output id 0x{expected:08x}, got 0x{id:08x}")] 123 | OutputIdMismatch { 124 | /// Function output id. 125 | expected: u32, 126 | /// Id from parsed data. 127 | id: u32, 128 | }, 129 | } 130 | -------------------------------------------------------------------------------- /src/abi/mod.rs: -------------------------------------------------------------------------------- 1 | //! Common ABI implementation. 2 | 3 | use std::hash::{BuildHasher, Hash}; 4 | use std::str::FromStr; 5 | 6 | pub use everscale_types_abi_proc::{FromAbi, IntoAbi, WithAbiType}; 7 | 8 | pub use self::contract::{ 9 | Contract, Event, EventBuilder, ExternalInput, Function, FunctionBuilder, UnsignedBody, 10 | UnsignedExternalMessage, 11 | }; 12 | pub use self::signature::{extend_signature_with_id, sign_with_signature_id}; 13 | pub use self::traits::{ 14 | FromAbi, FromAbiIter, FromPlainAbi, IgnoreName, IntoAbi, IntoPlainAbi, WithAbiType, 15 | WithPlainAbiType, 16 | }; 17 | pub use self::ty::{ 18 | AbiHeaderType, AbiType, AbiTypeFlatten, NamedAbiType, NamedAbiTypeFlatten, PlainAbiType, 19 | }; 20 | pub use self::value::{AbiHeader, AbiValue, NamedAbiValue, PlainAbiValue}; 21 | 22 | pub mod error; 23 | 24 | mod contract; 25 | mod signature; 26 | mod traits; 27 | mod ty; 28 | mod value; 29 | 30 | #[cfg(test)] 31 | mod tests; 32 | 33 | #[doc(hidden)] 34 | pub mod __export { 35 | pub use anyhow; 36 | } 37 | 38 | /// ABI version. 39 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] 40 | pub struct AbiVersion { 41 | /// Major version component. 42 | pub major: u8, 43 | /// Minor version component. 44 | pub minor: u8, 45 | } 46 | 47 | impl AbiVersion { 48 | /// A legacy ABI version. 49 | pub const V1_0: Self = Self::new(1, 0); 50 | /// A base version of an ABI 2. 51 | pub const V2_0: Self = Self::new(2, 0); 52 | /// A base version with strings and refs. 53 | pub const V2_1: Self = Self::new(2, 1); 54 | /// Same as 2.1 but with a more compact address serialization. 55 | pub const V2_2: Self = Self::new(2, 2); 56 | /// Same as 2.2 but uses an address during signing. 57 | pub const V2_3: Self = Self::new(2, 3); 58 | 59 | /// Creates an ABI version from components. 60 | pub const fn new(major: u8, minor: u8) -> Self { 61 | Self { major, minor } 62 | } 63 | } 64 | 65 | impl FromStr for AbiVersion { 66 | type Err = error::ParseAbiVersionError; 67 | 68 | fn from_str(s: &str) -> Result { 69 | let (major, minor) = ok!(s 70 | .split_once('.') 71 | .ok_or(error::ParseAbiVersionError::InvalidFormat)); 72 | 73 | Ok(Self { 74 | major: ok!(major 75 | .parse() 76 | .map_err(error::ParseAbiVersionError::InvalidComponent)), 77 | minor: ok!(minor 78 | .parse() 79 | .map_err(error::ParseAbiVersionError::InvalidComponent)), 80 | }) 81 | } 82 | } 83 | 84 | impl std::fmt::Display for AbiVersion { 85 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 86 | write!(f, "{}.{}", self.major, self.minor) 87 | } 88 | } 89 | 90 | /// A wrapper around [`AbiType`], [`NamedAbiType`], [`AbiValue`] and [`NamedAbiValue`] 91 | /// that implements hash/comparison traits without name. 92 | #[repr(transparent)] 93 | pub struct WithoutName(pub T); 94 | 95 | impl WithoutName { 96 | /// Wraps a reference of the inner type. 97 | pub fn wrap(value: &T) -> &Self { 98 | // SAFETY: HashWithoutName is #[repr(transparent)] 99 | unsafe { &*(value as *const T as *const Self) } 100 | } 101 | 102 | /// Wraps a slice of the inner type. 103 | pub fn wrap_slice(value: &[T]) -> &[Self] { 104 | // SAFETY: HashWithoutName is #[repr(transparent)] 105 | unsafe { &*(value as *const [T] as *const [Self]) } 106 | } 107 | } 108 | 109 | impl std::fmt::Debug for WithoutName { 110 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 111 | f.debug_tuple("WithoutName").field(&self.0).finish() 112 | } 113 | } 114 | 115 | impl Clone for WithoutName { 116 | #[inline] 117 | fn clone(&self) -> Self { 118 | WithoutName(self.0.clone()) 119 | } 120 | } 121 | 122 | impl Eq for WithoutName where WithoutName: PartialEq {} 123 | 124 | impl PartialOrd for WithoutName 125 | where 126 | WithoutName: Ord, 127 | { 128 | #[inline] 129 | fn partial_cmp(&self, other: &Self) -> Option { 130 | Some(self.cmp(other)) 131 | } 132 | } 133 | 134 | impl PartialEq for WithoutName> 135 | where 136 | WithoutName: PartialEq, 137 | { 138 | fn eq(&self, WithoutName(other): &Self) -> bool { 139 | WithoutName::wrap_slice(self.0.as_slice()) == WithoutName::wrap_slice(other.as_slice()) 140 | } 141 | } 142 | 143 | impl PartialEq for WithoutName> 144 | where 145 | K: PartialEq, 146 | WithoutName: PartialEq, 147 | { 148 | fn eq(&self, WithoutName(other): &Self) -> bool { 149 | self.0.len() == other.len() 150 | && self.0.iter().zip(other).all(|((ak, av), (bk, bv))| { 151 | (ak, WithoutName::wrap(av)) == (bk, WithoutName::wrap(bv)) 152 | }) 153 | } 154 | } 155 | 156 | impl PartialEq for WithoutName> 157 | where 158 | K: Eq + Hash, 159 | WithoutName: PartialEq, 160 | S: BuildHasher, 161 | { 162 | fn eq(&self, WithoutName(other): &Self) -> bool { 163 | if self.0.len() != other.len() { 164 | return false; 165 | } 166 | 167 | self.0.iter().all(|(key, value)| { 168 | other 169 | .get(key) 170 | .is_some_and(|v| WithoutName::wrap(value) == WithoutName::wrap(v)) 171 | }) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/abi/signature.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use ed25519_dalek::Signer; 4 | 5 | /// Signs arbitrary data using the key and optional signature id. 6 | pub fn sign_with_signature_id( 7 | key: &ed25519_dalek::SigningKey, 8 | data: &[u8], 9 | signature_id: Option, 10 | ) -> ed25519_dalek::Signature { 11 | let data = extend_signature_with_id(data, signature_id); 12 | key.sign(&data) 13 | } 14 | 15 | /// Prepares arbitrary data for signing. 16 | pub fn extend_signature_with_id(data: &[u8], signature_id: Option) -> Cow<'_, [u8]> { 17 | match signature_id { 18 | Some(signature_id) => { 19 | let mut result = Vec::with_capacity(4 + data.len()); 20 | result.extend_from_slice(&signature_id.to_be_bytes()); 21 | result.extend_from_slice(data); 22 | Cow::Owned(result) 23 | } 24 | None => Cow::Borrowed(data), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/arbitrary.rs: -------------------------------------------------------------------------------- 1 | //! More specific logic for generating arbitrary data. 2 | 3 | use arbitrary::{Arbitrary, Result, Unstructured}; 4 | 5 | use crate::boc::Boc; 6 | use crate::cell::{Cell, CellBuilder, DynCell, MAX_BIT_LEN}; 7 | use crate::util::ArrayVec; 8 | 9 | /// [`Arbitrary`] helper for generating trees of only ordinary cells. 10 | #[repr(transparent)] 11 | pub struct OrdinaryCell(pub Cell); 12 | 13 | impl std::fmt::Debug for OrdinaryCell { 14 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | std::fmt::Debug::fmt(&Boc::encode(self), f) 16 | } 17 | } 18 | 19 | impl std::ops::Deref for OrdinaryCell { 20 | type Target = Cell; 21 | 22 | #[inline] 23 | fn deref(&self) -> &Self::Target { 24 | &self.0 25 | } 26 | } 27 | 28 | impl AsRef for OrdinaryCell { 29 | #[inline] 30 | fn as_ref(&self) -> &DynCell { 31 | self.0.as_ref() 32 | } 33 | } 34 | 35 | impl From for Cell { 36 | #[inline] 37 | fn from(value: OrdinaryCell) -> Self { 38 | value.0 39 | } 40 | } 41 | 42 | impl<'a> Arbitrary<'a> for OrdinaryCell { 43 | fn arbitrary(u: &mut Unstructured<'a>) -> Result { 44 | let OrdinaryCellBuilder(b) = u.arbitrary()?; 45 | Ok(Self(b.build().unwrap())) 46 | } 47 | 48 | #[inline] 49 | fn size_hint(depth: usize) -> (usize, Option) { 50 | ::size_hint(depth) 51 | } 52 | } 53 | 54 | /// [`Arbitrary`] helper for generating trees of only ordinary cells. 55 | #[repr(transparent)] 56 | #[derive(Debug)] 57 | pub struct OrdinaryCellBuilder(pub CellBuilder); 58 | 59 | impl From for CellBuilder { 60 | #[inline] 61 | fn from(value: OrdinaryCellBuilder) -> Self { 62 | value.0 63 | } 64 | } 65 | 66 | impl<'a> Arbitrary<'a> for OrdinaryCellBuilder { 67 | fn arbitrary(u: &mut Unstructured<'a>) -> Result { 68 | let bit_len = u.int_in_range(0..=MAX_BIT_LEN)?; 69 | let refs = u.int_in_range(0..=4)?; 70 | 71 | let mut b = CellBuilder::new(); 72 | b.store_raw(u.bytes(bit_len.div_ceil(8) as _)?, bit_len) 73 | .unwrap(); 74 | 75 | let mut children = ArrayVec::::new(); 76 | for i in 0..refs as u8 { 77 | let cell = 'cell: { 78 | if i > 0 { 79 | // Allow to reuse cells. 80 | if let Some(i) = u.int_in_range(0..=i)?.checked_sub(1) { 81 | break 'cell children.get(i).cloned().unwrap(); 82 | } 83 | } 84 | 85 | u.arbitrary::().and_then(check_max_depth)?.0 86 | }; 87 | 88 | b.store_reference(cell.clone()).unwrap(); 89 | 90 | // SAFETY: `refs` is at most 4. 91 | unsafe { children.push(cell) }; 92 | } 93 | 94 | Ok(Self(b)) 95 | } 96 | 97 | #[inline] 98 | fn size_hint(_: usize) -> (usize, Option) { 99 | (3, None) 100 | } 101 | } 102 | 103 | /// [`Arbitrary`] helper for generating a "real-life" balance. 104 | #[cfg(feature = "models")] 105 | #[derive(Debug)] 106 | #[repr(transparent)] 107 | pub struct SimpleBalance(pub crate::models::CurrencyCollection); 108 | 109 | #[cfg(feature = "models")] 110 | impl From for crate::models::CurrencyCollection { 111 | #[inline] 112 | fn from(value: SimpleBalance) -> Self { 113 | value.0 114 | } 115 | } 116 | 117 | #[cfg(feature = "models")] 118 | impl<'a> Arbitrary<'a> for SimpleBalance { 119 | #[inline] 120 | fn arbitrary(u: &mut Unstructured<'a>) -> Result { 121 | const BIG_BALANCE: u128 = 100_000_000_000_000_000_000; 122 | 123 | Ok(Self(crate::models::CurrencyCollection { 124 | tokens: crate::num::Tokens::new(u.int_in_range(0..=BIG_BALANCE)?), 125 | other: crate::models::ExtraCurrencyCollection::new(), 126 | })) 127 | } 128 | 129 | #[inline] 130 | fn size_hint(depth: usize) -> (usize, Option) { 131 | ::size_hint(depth) 132 | } 133 | } 134 | 135 | /// Returns the argument if it doesn't have levels with max depth. 136 | pub fn check_max_depth>(cell: T) -> Result { 137 | if cell.as_ref().has_max_depth() { 138 | return Err(arbitrary::Error::IncorrectFormat); 139 | } 140 | Ok(cell) 141 | } 142 | -------------------------------------------------------------------------------- /src/boc/tests/block.boc.2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/boc/tests/block.boc.2 -------------------------------------------------------------------------------- /src/cell/cell_context.rs: -------------------------------------------------------------------------------- 1 | use sha2::digest::Digest; 2 | 3 | #[cfg(feature = "stats")] 4 | use crate::cell::CellTreeStats; 5 | use crate::cell::{Cell, CellDescriptor, CellType, DynCell, HashBytes, LevelMask, MAX_REF_COUNT}; 6 | use crate::error::Error; 7 | use crate::util::{unlikely, ArrayVec}; 8 | 9 | /// Gas accounting and resolcing exotic cells. 10 | pub trait CellContext { 11 | /// Builds a new cell from cell parts. 12 | fn finalize_cell(&self, cell: CellParts<'_>) -> Result; 13 | 14 | /// Resolve an owned cell. 15 | fn load_cell(&self, cell: Cell, mode: LoadMode) -> Result; 16 | 17 | /// Resolve a cell reference. 18 | fn load_dyn_cell<'s: 'a, 'a>( 19 | &'s self, 20 | cell: &'a DynCell, 21 | mode: LoadMode, 22 | ) -> Result<&'a DynCell, Error>; 23 | } 24 | 25 | /// Dictionary insertion mode. 26 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 27 | #[repr(u8)] 28 | pub enum LoadMode { 29 | /// Do not modify the default behavior. 30 | Noop = 0b00, 31 | /// Count the cost of loading the cell. 32 | UseGas = 0b01, 33 | /// Resolve exotic cells such as libraries or merkle stuff. 34 | Resolve = 0b10, 35 | /// Both `UseGas` and `Resolve`. 36 | Full = 0b11, 37 | } 38 | 39 | impl LoadMode { 40 | /// Returns `true` if this mode requires gas accounting. 41 | #[inline] 42 | pub const fn use_gas(self) -> bool { 43 | self as u8 & 0b01 != 0 44 | } 45 | 46 | /// Returns `true` if exotic cells are resolved in this mode. 47 | #[inline] 48 | pub const fn resolve(self) -> bool { 49 | self as u8 & 0b10 != 0 50 | } 51 | } 52 | 53 | /// Partially assembled cell. 54 | pub struct CellParts<'a> { 55 | /// Cell tree storage stats. 56 | #[cfg(feature = "stats")] 57 | pub stats: CellTreeStats, 58 | 59 | /// Length of this cell's data in bits. 60 | pub bit_len: u16, 61 | 62 | /// Well-formed cell descriptor. 63 | pub descriptor: CellDescriptor, 64 | 65 | /// Bitwise OR of child level masks. 66 | pub children_mask: LevelMask, 67 | 68 | /// Array of child cells. 69 | /// 70 | /// NOTE: it is guaranteed that the length of the array is consistent 71 | /// with the descriptor. 72 | pub references: ArrayVec, 73 | 74 | /// Cell data slice. 75 | pub data: &'a [u8], 76 | } 77 | 78 | impl CellParts<'_> { 79 | /// Validates cell and computes all hashes. 80 | pub fn compute_hashes(&self) -> Result, Error> { 81 | const HASH_BITS: usize = 256; 82 | const DEPTH_BITS: usize = 16; 83 | 84 | let mut descriptor = self.descriptor; 85 | let bit_len = self.bit_len as usize; 86 | let level_mask = descriptor.level_mask(); 87 | let level = level_mask.level() as usize; 88 | 89 | let references = self.references.as_ref(); 90 | 91 | // `hashes_len` is guaranteed to be in range 1..4 92 | let mut hashes_len = level + 1; 93 | 94 | let (cell_type, computed_level_mask) = if unlikely(descriptor.is_exotic()) { 95 | let Some(&first_byte) = self.data.first() else { 96 | return Err(Error::InvalidCell); 97 | }; 98 | 99 | match CellType::from_byte_exotic(first_byte) { 100 | // 8 bits type, 8 bits level mask, level x (hash, depth) 101 | Some(CellType::PrunedBranch) => { 102 | if unlikely(level == 0) { 103 | return Err(Error::InvalidCell); 104 | } 105 | 106 | let expected_bit_len = 8 + 8 + level * (HASH_BITS + DEPTH_BITS); 107 | if unlikely(bit_len != expected_bit_len || !references.is_empty()) { 108 | return Err(Error::InvalidCell); 109 | } 110 | 111 | let stored_mask = self.data.get(1).copied().unwrap_or_default(); 112 | if unlikely(level_mask != stored_mask) { 113 | return Err(Error::InvalidCell); 114 | } 115 | 116 | hashes_len = 1; 117 | (CellType::PrunedBranch, level_mask) 118 | } 119 | // 8 bits type, hash, depth 120 | Some(CellType::MerkleProof) => { 121 | const EXPECTED_BIT_LEN: usize = 8 + HASH_BITS + DEPTH_BITS; 122 | if unlikely(bit_len != EXPECTED_BIT_LEN || references.len() != 1) { 123 | return Err(Error::InvalidCell); 124 | } 125 | 126 | (CellType::MerkleProof, self.children_mask.virtualize(1)) 127 | } 128 | // 8 bits type, 2 x (hash, depth) 129 | Some(CellType::MerkleUpdate) => { 130 | const EXPECTED_BIT_LEN: usize = 8 + 2 * (HASH_BITS + DEPTH_BITS); 131 | if unlikely(bit_len != EXPECTED_BIT_LEN || references.len() != 2) { 132 | return Err(Error::InvalidCell); 133 | } 134 | 135 | (CellType::MerkleUpdate, self.children_mask.virtualize(1)) 136 | } 137 | // 8 bits type, hash 138 | Some(CellType::LibraryReference) => { 139 | const EXPECTED_BIT_LEN: usize = 8 + HASH_BITS; 140 | if unlikely(bit_len != EXPECTED_BIT_LEN || !references.is_empty()) { 141 | return Err(Error::InvalidCell); 142 | } 143 | 144 | (CellType::LibraryReference, LevelMask::EMPTY) 145 | } 146 | _ => return Err(Error::InvalidCell), 147 | } 148 | } else { 149 | (CellType::Ordinary, self.children_mask) 150 | }; 151 | 152 | if unlikely(computed_level_mask != level_mask) { 153 | return Err(Error::InvalidCell); 154 | } 155 | 156 | let level_offset = cell_type.is_merkle() as u8; 157 | let is_pruned = cell_type.is_pruned_branch(); 158 | 159 | let mut hashes = Vec::<(HashBytes, u16)>::with_capacity(hashes_len); 160 | for level in 0..4 { 161 | // Skip non-zero levels for pruned branches and insignificant hashes for other cells 162 | if level != 0 && (is_pruned || !level_mask.contains(level)) { 163 | continue; 164 | } 165 | 166 | let mut hasher = sha2::Sha256::new(); 167 | 168 | let level_mask = if is_pruned { 169 | level_mask 170 | } else { 171 | LevelMask::from_level(level) 172 | }; 173 | 174 | descriptor.d1 &= !(CellDescriptor::LEVEL_MASK | CellDescriptor::STORE_HASHES_MASK); 175 | descriptor.d1 |= u8::from(level_mask) << 5; 176 | hasher.update([descriptor.d1, descriptor.d2]); 177 | 178 | if level == 0 { 179 | hasher.update(self.data); 180 | } else { 181 | // SAFETY: new hash is added on each iteration, so there will 182 | // definitely be a hash, when level>0 183 | let prev_hash = unsafe { hashes.last().unwrap_unchecked() }; 184 | hasher.update(prev_hash.0.as_slice()); 185 | } 186 | 187 | let mut depth = 0; 188 | for child in references { 189 | let child_depth = child.as_ref().depth(level + level_offset); 190 | let next_depth = match child_depth.checked_add(1) { 191 | Some(next_depth) => next_depth, 192 | None => return Err(Error::DepthOverflow), 193 | }; 194 | depth = std::cmp::max(depth, next_depth); 195 | 196 | hasher.update(child_depth.to_be_bytes()); 197 | } 198 | 199 | for child in references { 200 | let child_hash = child.as_ref().hash(level + level_offset); 201 | hasher.update(child_hash.as_slice()); 202 | } 203 | 204 | let hash = hasher.finalize().into(); 205 | hashes.push((hash, depth)); 206 | } 207 | 208 | Ok(hashes) 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/cell/lazy.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::cell::{ 4 | Cell, CellBuilder, CellContext, CellSlice, DynCell, EquivalentRepr, Load, LoadCell, Size, Store, 5 | }; 6 | use crate::error::Error; 7 | use crate::util::{debug_tuple_field1_finish, unlikely}; 8 | 9 | /// Lazy-loaded model. 10 | #[repr(transparent)] 11 | pub struct Lazy { 12 | cell: Cell, 13 | _marker: PhantomData, 14 | } 15 | 16 | /// Lazy-loaded exotic cell. 17 | pub type LazyExotic = Lazy; 18 | 19 | impl crate::cell::ExactSize for Lazy { 20 | #[inline] 21 | fn exact_size(&self) -> Size { 22 | Size { bits: 0, refs: 1 } 23 | } 24 | } 25 | 26 | impl std::fmt::Debug for Lazy { 27 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 28 | debug_tuple_field1_finish(f, "Lazy", &self.cell) 29 | } 30 | } 31 | 32 | impl std::fmt::Debug for Lazy { 33 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 34 | debug_tuple_field1_finish(f, "LazyExotic", &self.cell) 35 | } 36 | } 37 | 38 | impl Eq for Lazy {} 39 | impl PartialEq for Lazy { 40 | #[inline] 41 | fn eq(&self, other: &Self) -> bool { 42 | self.cell.as_ref().eq(other.cell.as_ref()) 43 | } 44 | } 45 | 46 | impl PartialEq for Lazy { 47 | #[inline] 48 | fn eq(&self, other: &Cell) -> bool { 49 | PartialEq::eq(self.inner(), other) 50 | } 51 | } 52 | 53 | impl PartialEq<&Cell> for Lazy { 54 | #[inline] 55 | fn eq(&self, other: &&Cell) -> bool { 56 | PartialEq::eq(self.inner(), *other) 57 | } 58 | } 59 | 60 | impl PartialEq> for Cell { 61 | #[inline] 62 | fn eq(&self, other: &Lazy) -> bool { 63 | PartialEq::eq(self, other.inner()) 64 | } 65 | } 66 | 67 | impl PartialEq<&Lazy> for Cell { 68 | #[inline] 69 | fn eq(&self, other: &&Lazy) -> bool { 70 | PartialEq::eq(self, other.inner()) 71 | } 72 | } 73 | 74 | impl Clone for Lazy { 75 | #[inline] 76 | fn clone(&self) -> Self { 77 | Self { 78 | cell: self.cell.clone(), 79 | _marker: PhantomData, 80 | } 81 | } 82 | } 83 | 84 | impl Lazy { 85 | /// Wraps the cell in a typed wrapper. 86 | #[inline] 87 | pub fn from_raw(cell: Cell) -> Result { 88 | if unlikely(cell.is_exotic() != EXOTIC) { 89 | return Err(if EXOTIC { 90 | Error::UnexpectedOrdinaryCell 91 | } else { 92 | Error::UnexpectedExoticCell 93 | }); 94 | } 95 | 96 | Ok(Self { 97 | cell, 98 | _marker: PhantomData, 99 | }) 100 | } 101 | 102 | /// Wraps the cell in a typed wrapper. 103 | /// 104 | /// # Safety 105 | /// 106 | /// The cell `is_exotic` flag must be in sync with `EXOTIC` type param. 107 | #[inline] 108 | pub unsafe fn from_raw_unchecked(cell: Cell) -> Self { 109 | Self { 110 | cell, 111 | _marker: PhantomData, 112 | } 113 | } 114 | 115 | /// Converts into the underlying cell. 116 | #[inline] 117 | pub fn into_inner(self) -> Cell { 118 | self.cell 119 | } 120 | 121 | /// Returns the underlying cell. 122 | #[inline] 123 | pub fn inner(&self) -> &Cell { 124 | &self.cell 125 | } 126 | 127 | /// Converts into a lazy loader for an equivalent type. 128 | pub fn cast_into(self) -> Lazy 129 | where 130 | Q: EquivalentRepr, 131 | { 132 | Lazy { 133 | cell: self.cell, 134 | _marker: PhantomData, 135 | } 136 | } 137 | 138 | /// Casts itself into a lazy loaded for an equivalent type. 139 | pub fn cast_ref(&self) -> &Lazy 140 | where 141 | Q: EquivalentRepr, 142 | { 143 | // SAFETY: Lazy is #[repr(transparent)] 144 | unsafe { &*(self as *const Self as *const Lazy) } 145 | } 146 | 147 | /// Serializes only the root hash of the cell. 148 | #[cfg(feature = "serde")] 149 | pub fn serialize_repr_hash(&self, serializer: S) -> Result 150 | where 151 | S: serde::Serializer, 152 | { 153 | serde::Serialize::serialize(self.cell.repr_hash(), serializer) 154 | } 155 | } 156 | 157 | impl AsRef for Lazy { 158 | #[inline] 159 | fn as_ref(&self) -> &DynCell { 160 | self.cell.as_ref() 161 | } 162 | } 163 | 164 | impl std::ops::Deref for Lazy { 165 | type Target = Cell; 166 | 167 | #[inline] 168 | fn deref(&self) -> &Self::Target { 169 | &self.cell 170 | } 171 | } 172 | 173 | impl Lazy { 174 | /// Serializes the provided data and returns the typed wrapper around it. 175 | pub fn new(data: &T) -> Result { 176 | Self::from_raw(ok!(CellBuilder::build_from(data))) 177 | } 178 | 179 | /// Updates the content with the provided data. 180 | pub fn set(&mut self, data: &T) -> Result<(), Error> { 181 | *self = ok!(Self::new(data)); 182 | Ok(()) 183 | } 184 | } 185 | 186 | impl<'a, T: Load<'a> + 'a> Lazy { 187 | /// Loads inner data from cell expecting an ordinary cell. 188 | pub fn load(&'a self) -> Result { 189 | self.cell.as_ref().parse::() 190 | } 191 | } 192 | 193 | impl<'a, T: LoadCell<'a> + 'a> Lazy { 194 | /// Loads inner data from cell expecting an exotic cell. 195 | pub fn load(&'a self) -> Result { 196 | self.cell.as_ref().parse_exotic::() 197 | } 198 | } 199 | 200 | impl Store for Lazy { 201 | fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> { 202 | builder.store_reference(self.cell.clone()) 203 | } 204 | } 205 | 206 | impl<'a, T, const EXOTIC: bool> Load<'a> for Lazy { 207 | fn load_from(slice: &mut CellSlice<'a>) -> Result { 208 | match slice.load_reference_cloned() { 209 | Ok(cell) => Self::from_raw(cell), 210 | Err(e) => Err(e), 211 | } 212 | } 213 | } 214 | 215 | #[cfg(feature = "serde")] 216 | impl serde::Serialize for Lazy 217 | where 218 | for<'a> T: serde::Serialize + Load<'a>, 219 | { 220 | fn serialize(&self, serializer: S) -> Result 221 | where 222 | S: serde::Serializer, 223 | { 224 | if serializer.is_human_readable() { 225 | let value = ok!(self.load().map_err(serde::ser::Error::custom)); 226 | value.serialize(serializer) 227 | } else { 228 | crate::boc::Boc::serialize(&self.cell, serializer) 229 | } 230 | } 231 | } 232 | 233 | #[cfg(feature = "serde")] 234 | impl serde::Serialize for Lazy 235 | where 236 | for<'a> T: serde::Serialize + LoadCell<'a>, 237 | { 238 | fn serialize(&self, serializer: S) -> Result 239 | where 240 | S: serde::Serializer, 241 | { 242 | if serializer.is_human_readable() { 243 | let value = ok!(self.load().map_err(serde::ser::Error::custom)); 244 | value.serialize(serializer) 245 | } else { 246 | crate::boc::Boc::serialize(&self.cell, serializer) 247 | } 248 | } 249 | } 250 | 251 | #[cfg(feature = "serde")] 252 | impl<'de, T, const EXOTIC: bool> serde::Deserialize<'de> for Lazy 253 | where 254 | T: serde::Deserialize<'de> + Store, 255 | { 256 | fn deserialize(deserializer: D) -> Result, D::Error> 257 | where 258 | D: serde::Deserializer<'de>, 259 | { 260 | if deserializer.is_human_readable() { 261 | let value = T::deserialize(deserializer)?; 262 | Lazy::new(&value) 263 | } else { 264 | let cell = crate::boc::Boc::deserialize(deserializer)?; 265 | Lazy::from_raw(cell) 266 | } 267 | .map_err(serde::de::Error::custom) 268 | } 269 | } 270 | 271 | #[cfg(feature = "arbitrary")] 272 | impl<'a, T: Store + arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'a> for Lazy { 273 | fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { 274 | let inner = u.arbitrary::()?; 275 | Lazy::new(&inner).map_err(|_| arbitrary::Error::IncorrectFormat) 276 | } 277 | 278 | #[inline] 279 | fn size_hint(depth: usize) -> (usize, Option) { 280 | Self::try_size_hint(depth).unwrap_or_default() 281 | } 282 | 283 | #[inline] 284 | fn try_size_hint( 285 | depth: usize, 286 | ) -> arbitrary::Result<(usize, Option), arbitrary::MaxRecursionReached> { 287 | ::try_size_hint(depth) 288 | } 289 | } 290 | -------------------------------------------------------------------------------- /src/crc.rs: -------------------------------------------------------------------------------- 1 | //! CRC stuff. 2 | 3 | /// CRC16 bit CCITT 4 | #[inline] 5 | pub fn crc_16(data: &[u8]) -> u16 { 6 | let mut crc: u32 = 0; 7 | for c in data { 8 | let t = c ^ ((crc >> 8) as u8); 9 | crc = (CRC16_TABLE[t as usize] ^ ((crc << 8) as u16)) as u32; 10 | } 11 | crc as u16 12 | } 13 | 14 | static CRC16_TABLE: [u16; 256] = [ 15 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 16 | 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 17 | 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 18 | 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 19 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 20 | 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 21 | 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 22 | 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 23 | 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 24 | 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 25 | 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 26 | 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 27 | 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 28 | 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 29 | 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 30 | 0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 31 | 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 32 | 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 33 | 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 34 | 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 35 | 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 36 | 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0, 37 | ]; 38 | -------------------------------------------------------------------------------- /src/dict/ops/get.rs: -------------------------------------------------------------------------------- 1 | use crate::cell::*; 2 | use crate::dict::{make_leaf, read_label, split_edge, Branch}; 3 | use crate::error::Error; 4 | 5 | /// Returns a `CellSlice` of the value corresponding to the key. 6 | pub fn dict_get<'a: 'b, 'b, 'c: 'a>( 7 | dict: Option<&'a Cell>, 8 | key_bit_len: u16, 9 | mut key: CellSlice<'b>, 10 | context: &'c dyn CellContext, 11 | ) -> Result>, Error> { 12 | if key.size_bits() != key_bit_len { 13 | return Err(Error::CellUnderflow); 14 | } 15 | 16 | let mut data = match dict { 17 | Some(data) => ok!(context 18 | .load_dyn_cell(data.as_ref(), LoadMode::Full) 19 | .and_then(CellSlice::new)), 20 | None => return Ok(None), 21 | }; 22 | 23 | // Try to find the required leaf 24 | let is_key_empty = loop { 25 | // Read the key part written in the current edge 26 | let prefix = ok!(read_label(&mut data, key.size_bits())); 27 | 28 | // Remove this prefix from the key 29 | match key.strip_data_prefix(&prefix) { 30 | Some(stripped_key) => { 31 | if stripped_key.is_data_empty() { 32 | // All key parts were collected <=> value found 33 | break true; 34 | } else if data.size_refs() < 2 { 35 | // Reached leaf while key was not fully constructed 36 | return Ok(None); 37 | } else { 38 | key = stripped_key; 39 | } 40 | } 41 | None => break key.is_data_empty(), 42 | } 43 | 44 | // Load next child based on the next bit 45 | let child_index = ok!(key.load_bit()) as u8; 46 | data = match data.cell().reference(child_index) { 47 | Some(cell) => ok!(context 48 | .load_dyn_cell(cell, LoadMode::Full) 49 | .and_then(CellSlice::new)), 50 | None => return Err(Error::CellUnderflow), 51 | }; 52 | }; 53 | 54 | // Return the last slice as data 55 | Ok(if is_key_empty { Some(data) } else { None }) 56 | } 57 | 58 | /// Returns cell slice parts of the value corresponding to the key. 59 | pub fn dict_get_owned( 60 | dict: Option<&Cell>, 61 | key_bit_len: u16, 62 | mut key: CellSlice<'_>, 63 | context: &dyn CellContext, 64 | ) -> Result, Error> { 65 | if key.size_bits() != key_bit_len { 66 | return Err(Error::CellUnderflow); 67 | } 68 | 69 | let root = match dict { 70 | Some(cell) => ok!(context.load_cell(cell.clone(), LoadMode::Full)), 71 | None => return Ok(None), 72 | }; 73 | let mut data = ok!(root.as_slice()); 74 | let mut prev = None; 75 | 76 | // Try to find the required leaf 77 | let is_key_empty = loop { 78 | // Read the key part written in the current edge 79 | let prefix = ok!(read_label(&mut data, key.size_bits())); 80 | 81 | // Remove this prefix from the key 82 | match key.strip_data_prefix(&prefix) { 83 | Some(stripped_key) => { 84 | if stripped_key.is_data_empty() { 85 | // All key parts were collected <=> value found 86 | break true; 87 | } else if data.size_refs() < 2 { 88 | // Reached leaf while key was not fully constructed 89 | return Ok(None); 90 | } else { 91 | key = stripped_key; 92 | } 93 | } 94 | None => break key.is_data_empty(), 95 | } 96 | 97 | // Load next child based on the next bit 98 | let child_index = ok!(key.load_bit()) as u8; 99 | prev = Some((data.cell(), child_index)); 100 | data = match data.cell().reference(child_index) { 101 | Some(cell) => ok!(context 102 | .load_dyn_cell(cell, LoadMode::Full) 103 | .and_then(CellSlice::new)), 104 | None => return Err(Error::CellUnderflow), 105 | }; 106 | }; 107 | 108 | // Return the last slice as data 109 | Ok(if is_key_empty { 110 | Some(match prev { 111 | Some((prev, child_index)) => { 112 | let cell = match prev.reference_cloned(child_index) { 113 | Some(cell) => ok!(context.load_cell(cell, LoadMode::Resolve)), 114 | None => return Err(Error::CellUnderflow), 115 | }; 116 | (data.range(), cell) 117 | } 118 | None => { 119 | let range = data.range(); 120 | (range, root) 121 | } 122 | }) 123 | } else { 124 | None 125 | }) 126 | } 127 | 128 | /// Gets subdictionary by specified prefiex 129 | /// Returns optional dictionary as Cell representation if specified prefix is present in dictionary 130 | pub fn dict_get_subdict<'a: 'b, 'b, 'c: 'a>( 131 | dict: Option<&'a Cell>, 132 | key_bit_len: u16, 133 | prefix: &mut CellSlice<'b>, 134 | context: &'c dyn CellContext, 135 | ) -> Result, Error> { 136 | match dict { 137 | None => Ok(None), 138 | Some(cell) => { 139 | let prefix_len = prefix.size_bits(); 140 | if prefix_len == 0 || key_bit_len < prefix_len { 141 | return Ok(Some(cell.clone())); 142 | } 143 | 144 | let mut data = match dict { 145 | Some(data) => ok!(context 146 | .load_dyn_cell(data.as_ref(), LoadMode::Full) 147 | .and_then(CellSlice::new)), 148 | None => return Ok(None), 149 | }; 150 | 151 | // Try to find the required root 152 | let subtree = loop { 153 | // Read the key part written in the current edge 154 | let label = &mut ok!(read_label(&mut data, prefix.size_bits())); 155 | let lcp = prefix.longest_common_data_prefix(label); 156 | match lcp.size_bits().cmp(&prefix.size_bits()) { 157 | std::cmp::Ordering::Equal => { 158 | // Found exact key 159 | let new_leaf = ok!(make_leaf(label, lcp.size_bits(), &data, context)); 160 | break new_leaf; 161 | } 162 | std::cmp::Ordering::Less if lcp.size_bits() < label.size_bits() => { 163 | // Have to split edge 164 | let value = ok!(CellBuilder::new().build_ext(context)); 165 | let split_edge = 166 | ok!(split_edge(&data, label, &lcp, prefix, &value, context)); 167 | break split_edge; 168 | } 169 | std::cmp::Ordering::Less => { 170 | if data.cell().reference_count() < 2 { 171 | return Err(Error::CellUnderflow); 172 | } 173 | prefix.skip_first(lcp.size_bits(), 0).ok(); 174 | let next_branch = match prefix.load_bit() { 175 | Ok(bit) => Branch::from(bit), 176 | Err(e) => return Err(e), 177 | }; 178 | 179 | let child = match data.cell().reference(next_branch as u8) { 180 | // TODO: change mode to `LoadMode::UseGas` if copy-on-write for libraries is not ok 181 | Some(cell) => ok!(context 182 | .load_dyn_cell(cell, LoadMode::Full) 183 | .and_then(CellSlice::new)), 184 | None => return Err(Error::CellUnderflow), 185 | }; 186 | data = child; 187 | } 188 | std::cmp::Ordering::Greater => { 189 | debug_assert!(false, "LCP of prefix and key can't be greater than key"); 190 | unsafe { std::hint::unreachable_unchecked() }; 191 | } 192 | } 193 | }; 194 | 195 | Ok(Some(subtree)) 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/dict/tests/account_blocks_aug_dict.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/dict/tests/account_blocks_aug_dict.boc -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Common error types. 2 | 3 | /// Error type for cell related errors. 4 | #[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)] 5 | pub enum Error { 6 | /// There were not enough bits or refs in the cell slice. 7 | #[error("cell underflow")] 8 | CellUnderflow, 9 | /// There were not enough bits or refs capacity in the cell builder. 10 | #[error("cell overflow")] 11 | CellOverflow, 12 | /// Something tried to load an exotic cell but an ordinary was required. 13 | #[error("unexpected exotic cell")] 14 | UnexpectedExoticCell, 15 | /// Something tried to load an ordinary cell but an exotic was required. 16 | #[error("unexpected exotic cell")] 17 | UnexpectedOrdinaryCell, 18 | /// Cell contains invalid descriptor or data. 19 | #[error("invalid cell")] 20 | InvalidCell, 21 | /// Data does not satisfy some constraints. 22 | #[error("invalid data")] 23 | InvalidData, 24 | /// Unknown TLB tag. 25 | #[error("invalid tag")] 26 | InvalidTag, 27 | /// Merkle proof does not contain the root cell. 28 | #[error("empty proof")] 29 | EmptyProof, 30 | /// Tree of cells is too deep. 31 | #[error("cell depth overflow")] 32 | DepthOverflow, 33 | /// Signature check failed. 34 | #[error("invalid signature")] 35 | InvalidSignature, 36 | /// Public key is not in a ed25519 valid range. 37 | #[error("invalid public key")] 38 | InvalidPublicKey, 39 | /// Underlying integer type does not fit into the target type. 40 | #[error("underlying integer is too large to fit in target type")] 41 | IntOverflow, 42 | /// Helper error variant for cancelled operations. 43 | #[error("operation cancelled")] 44 | Cancelled, 45 | /// Presented structure is unbalanced. 46 | #[error("unbalanced structure")] 47 | Unbalanced, 48 | } 49 | 50 | /// Error type for integer parsing related errors. 51 | #[derive(Debug, Clone, thiserror::Error)] 52 | pub enum ParseIntError { 53 | /// Error while parsing underlying type. 54 | #[error("cannot parse underlying integer")] 55 | InvalidString(#[source] std::num::ParseIntError), 56 | /// Underlying integer type does not fit into the target type. 57 | #[error("underlying integer is too large to fit in target type")] 58 | Overflow, 59 | } 60 | 61 | /// Error type for hash bytes parsing related errors. 62 | #[derive(Debug, Clone, thiserror::Error)] 63 | pub enum ParseHashBytesError { 64 | /// Failed to parse base64 encoded bytes. 65 | #[cfg(feature = "base64")] 66 | #[error("invalid base64 string")] 67 | InvalidBase64(#[from] base64::DecodeSliceError), 68 | /// Failed to parse hex encoded bytes. 69 | #[error("invalid hex string")] 70 | InvalidHex(#[from] hex::FromHexError), 71 | /// Error for an unexpected string length. 72 | #[error("expected string of 44, 64 or 66 bytes")] 73 | UnexpectedStringLength, 74 | } 75 | 76 | /// Error type for address parsing related errors. 77 | #[derive(Debug, Clone, thiserror::Error)] 78 | pub enum ParseAddrError { 79 | /// Tried to parse an empty string. 80 | #[error("cannot parse address from an empty string")] 81 | Empty, 82 | /// Workchain id is too large. 83 | #[error("workchain id is too large to fit in target type")] 84 | InvalidWorkchain, 85 | /// Invalid account id hex. 86 | #[error("cannot parse account id")] 87 | InvalidAccountId, 88 | /// Too many address parts. 89 | #[error("unexpected address part")] 90 | UnexpectedPart, 91 | /// Unexpected or invalid address format. 92 | #[error("invalid address format")] 93 | BadFormat, 94 | } 95 | 96 | /// Error type for block id parsing related errors. 97 | #[derive(Debug, Clone, thiserror::Error)] 98 | pub enum ParseBlockIdError { 99 | /// Tried to parse an empty string. 100 | #[error("cannot parse block id from an empty string")] 101 | Empty, 102 | /// Workchain id is too large. 103 | #[error("cannot parse workchain id")] 104 | InvalidWorkchain, 105 | /// Invalid workchain or shard prefix. 106 | #[error("invalid shard id")] 107 | InvalidShardIdent, 108 | /// Invalid block seqno. 109 | #[error("cannot parse block seqno")] 110 | InvalidSeqno, 111 | /// Invalid root hash hex. 112 | #[error("cannot parse root hash")] 113 | InvalidRootHash, 114 | /// Invalid file hash hex. 115 | #[error("cannot parse file hash")] 116 | InvalidFileHash, 117 | /// Too many block id parts. 118 | #[error("unexpected block id part")] 119 | UnexpectedPart, 120 | } 121 | 122 | /// Error type for global capability parsing related errors. 123 | #[derive(Debug, Clone, thiserror::Error)] 124 | pub enum ParseGlobalCapabilityError { 125 | /// Tried to parse an unknown string. 126 | #[error("unknown capability")] 127 | UnknownCapability, 128 | } 129 | -------------------------------------------------------------------------------- /src/merkle/mod.rs: -------------------------------------------------------------------------------- 1 | //! Merkle stuff. 2 | 3 | use std::collections::HashSet; 4 | use std::hash::BuildHasher; 5 | 6 | pub use self::proof::{MerkleProof, MerkleProofBuilder, MerkleProofExtBuilder, MerkleProofRef}; 7 | pub use self::pruned_branch::make_pruned_branch; 8 | pub use self::update::{MerkleUpdate, MerkleUpdateBuilder}; 9 | use crate::cell::{HashBytes, UsageTree, UsageTreeWithSubtrees}; 10 | 11 | mod proof; 12 | mod pruned_branch; 13 | mod update; 14 | 15 | #[cfg(test)] 16 | mod tests; 17 | 18 | #[cfg(feature = "sync")] 19 | #[doc(hidden)] 20 | mod __checks { 21 | use super::*; 22 | 23 | assert_impl_all!(MerkleProof: Send); 24 | assert_impl_all!(MerkleUpdate: Send); 25 | } 26 | 27 | /// A cell tree filter that controls which cells will be included 28 | /// in the Merkle proof or update. 29 | pub trait MerkleFilter { 30 | /// Returns how the cell should be included in the Merkle proof or update. 31 | fn check(&self, cell: &HashBytes) -> FilterAction; 32 | } 33 | 34 | /// Merkle filter action. 35 | #[derive(Debug, Clone, Copy, Eq, PartialEq)] 36 | pub enum FilterAction { 37 | /// Skip this cell and its subtree. 38 | Skip, 39 | /// Include this cell and continue checking its subtree. 40 | Include, 41 | /// Include this cell and its subtree. 42 | IncludeSubtree, 43 | } 44 | 45 | impl MerkleFilter for &T { 46 | #[inline] 47 | fn check(&self, cell: &HashBytes) -> FilterAction { 48 | ::check(self, cell) 49 | } 50 | } 51 | 52 | impl MerkleFilter for UsageTree { 53 | fn check(&self, cell: &HashBytes) -> FilterAction { 54 | if UsageTree::contains(self, cell) { 55 | FilterAction::Include 56 | } else { 57 | FilterAction::Skip 58 | } 59 | } 60 | } 61 | 62 | impl MerkleFilter for UsageTreeWithSubtrees { 63 | fn check(&self, cell: &HashBytes) -> FilterAction { 64 | if UsageTreeWithSubtrees::contains_direct(self, cell) { 65 | FilterAction::Include 66 | } else if UsageTreeWithSubtrees::contains_subtree(self, cell) { 67 | FilterAction::IncludeSubtree 68 | } else { 69 | FilterAction::Skip 70 | } 71 | } 72 | } 73 | 74 | impl MerkleFilter for HashSet { 75 | fn check(&self, cell: &HashBytes) -> FilterAction { 76 | if HashSet::contains(self, cell) { 77 | FilterAction::Include 78 | } else { 79 | FilterAction::Skip 80 | } 81 | } 82 | } 83 | 84 | impl MerkleFilter for HashSet<&HashBytes, S> { 85 | fn check(&self, cell: &HashBytes) -> FilterAction { 86 | if HashSet::contains(self, cell) { 87 | FilterAction::Include 88 | } else { 89 | FilterAction::Skip 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/merkle/pruned_branch.rs: -------------------------------------------------------------------------------- 1 | use crate::cell::*; 2 | use crate::error::Error; 3 | 4 | /// Creates a pruned branch cell with the specified merkle depth. 5 | pub fn make_pruned_branch( 6 | cell: &DynCell, 7 | merkle_depth: u8, 8 | context: &dyn CellContext, 9 | ) -> Result { 10 | let descriptor = cell.descriptor(); 11 | let mut cell_level_mask = descriptor.level_mask(); 12 | 13 | let mut builder = CellBuilder::new(); 14 | 15 | let new_level = 1 << merkle_depth; 16 | let level_mask = LevelMask::new(cell_level_mask.to_byte() | new_level); 17 | 18 | builder.set_exotic(true); 19 | 20 | _ = builder.store_u16(u16::from_be_bytes([ 21 | CellType::PrunedBranch.to_byte(), 22 | level_mask.to_byte(), 23 | ])); 24 | 25 | // Only write levels lower than the new level. 26 | cell_level_mask = LevelMask::new(cell_level_mask.to_byte() & (new_level - 1)); 27 | 28 | for level in cell_level_mask { 29 | _ = builder.store_u256(cell.hash(level)); 30 | } 31 | 32 | for level in cell_level_mask { 33 | _ = builder.store_u16(cell.depth(level)); 34 | } 35 | 36 | builder.build_ext(context) 37 | } 38 | 39 | #[cfg(test)] 40 | mod test { 41 | use super::*; 42 | use crate::boc::Boc; 43 | use crate::merkle::MerkleProof; 44 | 45 | #[test] 46 | fn correct_pruned_branch() { 47 | let cell = { 48 | let mut builder = CellBuilder::new(); 49 | builder.store_u128(0xdeafbeaf123123).unwrap(); 50 | builder.store_reference(Cell::empty_cell()).unwrap(); 51 | builder.build().unwrap() 52 | }; 53 | 54 | let pruned_branch = make_pruned_branch(cell.as_ref(), 0, Cell::empty_context()).unwrap(); 55 | assert_eq!(cell.repr_hash(), pruned_branch.hash(0)); 56 | assert_eq!(cell.depth(0), pruned_branch.depth(0)); 57 | 58 | let virtual_cell = cell.virtualize(); 59 | assert_eq!(cell.repr_hash(), virtual_cell.repr_hash()); 60 | assert_eq!(cell.depth(3), virtual_cell.depth(3)); 61 | 62 | let virtual_pruned_branch = 63 | make_pruned_branch(virtual_cell, 0, Cell::empty_context()).unwrap(); 64 | assert_eq!(pruned_branch.as_ref(), virtual_pruned_branch.as_ref()); 65 | } 66 | 67 | #[test] 68 | fn partial_pruned() -> anyhow::Result<()> { 69 | let cell = CellBuilder::build_from(( 70 | CellBuilder::build_from((0xaa_u8, Cell::empty_cell()))?, 71 | CellBuilder::build_from((0xbb_u8, Cell::empty_cell()))?, 72 | ))?; 73 | println!("Original: {}", Boc::encode_base64(&cell)); 74 | 75 | // Prune left cell. 76 | let with_left_pruned = { 77 | let usage_tree = UsageTree::new(UsageTreeMode::OnLoad); 78 | let tracked = usage_tree.track(&cell); 79 | tracked.reference(1).unwrap().touch_recursive(); 80 | 81 | MerkleProof::create(cell.as_ref(), usage_tree).build_raw_ext(Cell::empty_context())? 82 | }; 83 | println!("Left pruned: {}", Boc::encode_base64(&with_left_pruned)); 84 | 85 | // Full pruned. 86 | let pruned = make_pruned_branch(with_left_pruned.as_ref(), 0, Cell::empty_context())?; 87 | println!("Full pruned: {}", Boc::encode_base64(&pruned)); 88 | 89 | Ok(()) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/merkle/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::cell::{CellTreeStats, EMPTY_CELL_HASH}; 3 | use crate::error::Error; 4 | use crate::prelude::*; 5 | 6 | #[test] 7 | fn correct_proof_store_load() { 8 | let proof = MerkleProof::default(); 9 | let cell = CellBuilder::build_from(&proof).unwrap(); 10 | 11 | let parsed = cell.as_ref().parse_exotic::().unwrap(); 12 | assert_eq!(parsed, proof); 13 | 14 | let parsed = cell.as_ref().parse_exotic::>().unwrap(); 15 | assert_eq!(parsed, proof); 16 | } 17 | 18 | #[test] 19 | fn test_proof() { 20 | let root = Boc::decode(include_bytes!("simple_proof.boc")).unwrap(); 21 | let target_hash = root.as_ref().reference(1).unwrap().repr_hash(); 22 | 23 | let merkle_proof = MerkleProof::create_for_cell(root.as_ref(), target_hash) 24 | .build() 25 | .unwrap(); 26 | 27 | let virtual_root = merkle_proof.cell.as_ref().virtualize(); 28 | println!("{}", virtual_root.display_tree()); 29 | 30 | assert_eq!(root.as_ref().repr_hash(), virtual_root.repr_hash()); 31 | assert_eq!(root.as_ref().repr_depth(), virtual_root.repr_depth()); 32 | } 33 | 34 | #[test] 35 | #[cfg_attr(miri, ignore)] // takes too long to execute on miri 36 | fn create_proof_for_deep_cell() { 37 | let mut cell = Cell::empty_cell(); 38 | for i in 0..65000 { 39 | let mut builder = CellBuilder::new(); 40 | builder.store_u32(i).unwrap(); 41 | builder.store_reference(cell).unwrap(); 42 | cell = builder.build().unwrap(); 43 | } 44 | 45 | let stats = cell.compute_unique_stats(1 << 22).unwrap(); 46 | assert_eq!(stats, CellTreeStats { 47 | bit_count: 65000 * 32, 48 | cell_count: 65001 49 | }); 50 | 51 | { 52 | let encoded = Boc::encode_base64(cell.as_ref()); 53 | let decoded = Boc::decode_base64(encoded).unwrap(); 54 | assert_eq!(decoded.as_ref(), cell.as_ref()); 55 | } 56 | 57 | let cell = MerkleProof::create_for_cell(cell.as_ref(), EMPTY_CELL_HASH) 58 | .build() 59 | .unwrap(); 60 | 61 | let encoded = BocRepr::encode_base64(&cell).unwrap(); 62 | let decoded = Boc::decode_base64(encoded) 63 | .unwrap() 64 | .as_ref() 65 | .parse_exotic::() 66 | .unwrap(); 67 | 68 | assert_eq!(cell, decoded); 69 | } 70 | 71 | #[test] 72 | fn create_proof_for_dict() { 73 | // Create dict with keys 0..10 74 | let mut dict = Dict::::new(); 75 | 76 | for i in 0..10 { 77 | dict.add(i, i * 10).unwrap(); 78 | } 79 | 80 | // Create a usage tree for accessing an element with keys 0 and 9 81 | let usage_tree = UsageTree::new(UsageTreeMode::OnDataAccess); 82 | let tracked_cell = usage_tree.track(&CellBuilder::build_from(dict).unwrap()); 83 | let tracked_dict = tracked_cell.as_ref().parse::>().unwrap(); 84 | tracked_dict.get(0).unwrap().unwrap(); 85 | tracked_dict.get(9).unwrap().unwrap(); 86 | 87 | // Create proof from the usage tree 88 | let merkle_proof = MerkleProof::create(tracked_cell.as_ref(), usage_tree) 89 | .build() 90 | .unwrap(); 91 | 92 | // Try to read some keys 93 | let dict = merkle_proof.cell.as_ref().virtualize(); 94 | let dict = dict.parse::>().unwrap(); 95 | dict.get(0).unwrap().unwrap(); 96 | dict.get(9).unwrap().unwrap(); 97 | 98 | assert!(matches!(dict.get(5), Err(Error::UnexpectedExoticCell))); 99 | } 100 | 101 | #[test] 102 | fn proof_with_subtree() -> anyhow::Result<()> { 103 | let mut dict = Dict::::new(); 104 | for i in 0..10 { 105 | dict.add(i, i * 10)?; 106 | } 107 | let dict = CellBuilder::build_from(dict)?; 108 | 109 | let some_other_cell = { 110 | let mut builder = CellBuilder::new(); 111 | builder.store_u128(123123)?; 112 | builder.store_reference(Cell::empty_cell())?; 113 | builder.store_reference(Cell::empty_cell())?; 114 | builder.build()? 115 | }; 116 | 117 | let root_cell = { 118 | let mut builder = CellBuilder::new(); 119 | builder.store_u128(321321)?; 120 | builder.store_reference(some_other_cell)?; 121 | builder.store_reference(dict.clone())?; 122 | builder.build()? 123 | }; 124 | 125 | let mut usage_tree = UsageTree::new(UsageTreeMode::OnDataAccess).with_subtrees(); 126 | let root_cell = usage_tree.track(&root_cell); 127 | 128 | { 129 | let mut root_cell = root_cell.as_ref().as_slice()?; 130 | root_cell.load_u32().unwrap(); 131 | 132 | assert!(usage_tree.add_subtree(dict.as_ref())); 133 | } 134 | 135 | let proof = MerkleProof::create(root_cell.as_ref(), usage_tree).build()?; 136 | let mut virtual_cell = proof.cell.as_ref().virtualize().as_slice()?; 137 | 138 | assert_eq!(virtual_cell.load_u128(), Ok(321321)); 139 | 140 | let first_ref = virtual_cell.load_reference()?; 141 | assert_eq!(first_ref.cell_type(), CellType::PrunedBranch); 142 | 143 | let second_ref = virtual_cell.load_reference()?; 144 | assert_eq!(second_ref.cell_type(), CellType::Ordinary); 145 | assert!(second_ref.descriptor().level_mask().is_empty()); 146 | 147 | let dict = second_ref.parse::>()?; 148 | for (i, entry) in dict.iter().enumerate() { 149 | let (key, value) = entry?; 150 | assert_eq!(i, key as usize); 151 | assert_eq!(key * 10, value); 152 | } 153 | assert_eq!(dict.values().count(), 10); 154 | 155 | Ok(()) 156 | } 157 | 158 | fn create_tree(depth: u32, num: u32) -> CellBuilder { 159 | let mut builder = CellBuilder::new(); 160 | builder.store_u32(num).unwrap(); 161 | if depth > 0 { 162 | let cell = create_tree(depth - 1, num); 163 | builder.store_reference(cell.build().unwrap()).unwrap(); 164 | } 165 | builder 166 | } 167 | 168 | #[test] 169 | fn test_merkle_update() { 170 | let build_cell = |num: u32| { 171 | let mut builder = CellBuilder::new(); 172 | builder.store_u32(num).unwrap(); 173 | builder.build().unwrap() 174 | }; 175 | 176 | // Create two trees with overlapping structure 177 | let mut old_tree = create_tree(5, 1); 178 | old_tree.store_reference(build_cell(1)).unwrap(); 179 | let old_tree = old_tree.build().unwrap(); 180 | let usage_tree = UsageTree::new(UsageTreeMode::OnDataAccess); 181 | let old_tree = usage_tree.track(&old_tree); 182 | 183 | let mut new_tree = create_tree(5, 1); 184 | new_tree.store_reference(build_cell(2)).unwrap(); 185 | let new_tree = new_tree.build().unwrap(); 186 | 187 | // Create a set of visited cells for the old tree 188 | let mut old_cells = HashSet::new(); 189 | let mut stack = vec![old_tree.as_ref()]; 190 | while let Some(cell) = stack.pop() { 191 | // trigger usage tracking) 192 | let _ = cell.data(); 193 | old_cells.insert(*cell.repr_hash()); 194 | for child in cell.references() { 195 | stack.push(child); 196 | } 197 | } 198 | 199 | // Create the Merkle update using HashSet 200 | let merkle_update = MerkleUpdate::create(old_tree.as_ref(), new_tree.as_ref(), old_cells) 201 | .build() 202 | .unwrap(); 203 | 204 | // Create the Merkle update using UsageTree 205 | let merkle_update_with_usage_tree = 206 | MerkleUpdate::create(old_tree.as_ref(), new_tree.as_ref(), usage_tree) 207 | .build() 208 | .unwrap(); 209 | 210 | // Print sizes 211 | println!( 212 | "Old tree size: {} bytes", 213 | BocRepr::encode(&old_tree).unwrap().len() 214 | ); 215 | println!( 216 | "New tree size: {} bytes", 217 | BocRepr::encode(&new_tree).unwrap().len() 218 | ); 219 | println!( 220 | "Update size (HashSet): {} bytes", 221 | BocRepr::encode(&merkle_update).unwrap().len() 222 | ); 223 | println!( 224 | "Update size (UsageTree): {} bytes", 225 | BocRepr::encode(&merkle_update_with_usage_tree) 226 | .unwrap() 227 | .len() 228 | ); 229 | 230 | // Verify the Merkle updates 231 | assert_eq!(merkle_update.old_hash, *old_tree.repr_hash()); 232 | assert_eq!(merkle_update.new_hash, *new_tree.repr_hash()); 233 | assert_eq!( 234 | merkle_update_with_usage_tree.old_hash, 235 | *old_tree.repr_hash() 236 | ); 237 | assert_eq!( 238 | merkle_update_with_usage_tree.new_hash, 239 | *new_tree.repr_hash() 240 | ); 241 | 242 | // Apply the Merkle updates to the old tree 243 | let result_hashset = merkle_update.apply(&old_tree).unwrap(); 244 | let result_usage_tree = merkle_update_with_usage_tree.apply(&old_tree).unwrap(); 245 | 246 | // Verify that the results match the new tree 247 | assert_eq!(result_hashset.as_ref(), new_tree.as_ref()); 248 | assert_eq!(result_usage_tree.as_ref(), new_tree.as_ref()); 249 | 250 | // Check that both Merkle updates are the same 251 | assert_eq!(merkle_update, merkle_update_with_usage_tree); 252 | } 253 | -------------------------------------------------------------------------------- /src/merkle/tests/simple_proof.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/merkle/tests/simple_proof.boc -------------------------------------------------------------------------------- /src/models/block/block_proof.rs: -------------------------------------------------------------------------------- 1 | use super::{BlockId, BlockSignature}; 2 | use crate::cell::*; 3 | use crate::dict::Dict; 4 | use crate::error::Error; 5 | #[cfg(feature = "tycho")] 6 | use crate::models::shard::ConsensusInfo; 7 | use crate::models::shard::ValidatorBaseInfo; 8 | 9 | /// Typed block proof. 10 | #[derive(Clone, Debug)] 11 | pub struct BlockProof { 12 | /// Id of the related block. 13 | pub proof_for: BlockId, 14 | /// Merkle proof cell. 15 | pub root: Cell, 16 | /// Optional references for the masterchain block. 17 | pub signatures: Option, 18 | } 19 | 20 | impl BlockProof { 21 | const TAG: u8 = 0xc3; 22 | } 23 | 24 | impl Store for BlockProof { 25 | fn store_into( 26 | &self, 27 | builder: &mut CellBuilder, 28 | context: &dyn CellContext, 29 | ) -> Result<(), Error> { 30 | let child_cell = match &self.signatures { 31 | Some(signatures) => { 32 | let mut builder = CellBuilder::new(); 33 | ok!(signatures.store_into(&mut builder, context)); 34 | Some(ok!(builder.build_ext(context))) 35 | } 36 | None => None, 37 | }; 38 | 39 | ok!(builder.store_u8(Self::TAG)); 40 | ok!(self.proof_for.store_into(builder, context)); 41 | ok!(builder.store_reference(self.root.clone())); 42 | match child_cell { 43 | Some(cell) => { 44 | ok!(builder.store_bit_one()); 45 | builder.store_reference(cell) 46 | } 47 | None => builder.store_bit_zero(), 48 | } 49 | } 50 | } 51 | 52 | impl<'a> Load<'a> for BlockProof { 53 | fn load_from(slice: &mut CellSlice<'a>) -> Result { 54 | match slice.load_u8() { 55 | Ok(Self::TAG) => {} 56 | Ok(_) => return Err(Error::InvalidTag), 57 | Err(e) => return Err(e), 58 | } 59 | 60 | Ok(Self { 61 | proof_for: ok!(BlockId::load_from(slice)), 62 | root: ok!(slice.load_reference_cloned()), 63 | signatures: if ok!(slice.load_bit()) { 64 | match slice.load_reference() { 65 | Ok(cell) => Some(ok!(cell.parse::())), 66 | Err(e) => return Err(e), 67 | } 68 | } else { 69 | None 70 | }, 71 | }) 72 | } 73 | } 74 | 75 | /// Masterchain block signatures. 76 | #[derive(Debug, Clone, Store, Load)] 77 | #[cfg_attr(not(feature = "tycho"), tlb(tag = "#11"))] 78 | #[cfg_attr(feature = "tycho", tlb(tag = "#12"))] 79 | pub struct BlockSignatures { 80 | /// Brief validator basic info. 81 | pub validator_info: ValidatorBaseInfo, 82 | /// Mempool bounds info. 83 | #[cfg(feature = "tycho")] 84 | pub consensus_info: ConsensusInfo, 85 | /// Total number of signatures. 86 | pub signature_count: u32, 87 | /// Total validators weight. 88 | pub total_weight: u64, 89 | /// Block signatures from all signers. 90 | pub signatures: Dict, 91 | } 92 | -------------------------------------------------------------------------------- /src/models/block/tests/empty_shard_block.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/block/tests/empty_shard_block.boc -------------------------------------------------------------------------------- /src/models/block/tests/mc_block_proof.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/block/tests/mc_block_proof.boc -------------------------------------------------------------------------------- /src/models/block/tests/mc_block_with_shards.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/block/tests/mc_block_with_shards.boc -------------------------------------------------------------------------------- /src/models/block/tests/mc_key_block.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/block/tests/mc_key_block.boc -------------------------------------------------------------------------------- /src/models/block/tests/mc_simple_block.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/block/tests/mc_simple_block.boc -------------------------------------------------------------------------------- /src/models/block/tests/shard_block_proof.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/block/tests/shard_block_proof.boc -------------------------------------------------------------------------------- /src/models/block/tests/simple_shard_block.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/block/tests/simple_shard_block.boc -------------------------------------------------------------------------------- /src/models/config/tests/new_config.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/config/tests/new_config.boc -------------------------------------------------------------------------------- /src/models/config/tests/old_config.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/config/tests/old_config.boc -------------------------------------------------------------------------------- /src/models/config/tests/simple_config.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/config/tests/simple_config.boc -------------------------------------------------------------------------------- /src/models/config/tests/test_state_2_master.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/config/tests/test_state_2_master.boc -------------------------------------------------------------------------------- /src/models/message/envelope.rs: -------------------------------------------------------------------------------- 1 | use super::IntMsgInfo; 2 | use crate::cell::*; 3 | use crate::error::Error; 4 | use crate::models::{Message, MsgInfo, OwnedMessage}; 5 | use crate::num::Tokens; 6 | use crate::util::unlikely; 7 | 8 | /// Next-hop address for a message. 9 | #[derive(Clone, Debug, PartialEq, Eq, Hash)] 10 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 11 | #[cfg_attr(feature = "serde", serde(tag = "ty"))] 12 | pub enum IntermediateAddr { 13 | /// Destination prefix length whithin the same workchain. 14 | Regular(IntermediateAddrRegular), 15 | /// Address prefix with a basic workchain id. 16 | Simple(IntermediateAddrSimple), 17 | /// Address prefix with an extended workchain id. 18 | Ext(IntermediateAddrExt), 19 | } 20 | 21 | impl IntermediateAddr { 22 | /// Full destination address within the same workchain. 23 | pub const FULL_DEST_SAME_WORKCHAIN: Self = Self::Regular(IntermediateAddrRegular { 24 | use_dest_bits: IntermediateAddrRegular::FULL_BITS, 25 | }); 26 | 27 | /// Full source address within the same workchain. 28 | pub const FULL_SRC_SAME_WORKCHAIN: Self = 29 | Self::Regular(IntermediateAddrRegular { use_dest_bits: 0 }); 30 | 31 | /// Full destination address within masterchain. 32 | pub const FULL_MASTERCHAIN: Self = Self::Simple(IntermediateAddrSimple { 33 | workchain: -1, 34 | address_prefix: 0b1 << 63, 35 | }); 36 | 37 | /// Returns target workchain id if specified. 38 | /// Returns `None` if the same workchain is used. 39 | pub fn workchain(&self) -> Option { 40 | match self { 41 | IntermediateAddr::Regular(_) => None, 42 | IntermediateAddr::Simple(simple) => Some(simple.workchain as i32), 43 | IntermediateAddr::Ext(ext) => Some(ext.workchain), 44 | } 45 | } 46 | 47 | /// Returns the address prefix if specified. 48 | /// Returns `None` if bits length is used. 49 | pub fn address_prefix(&self) -> Option { 50 | match self { 51 | IntermediateAddr::Regular(_) => None, 52 | IntermediateAddr::Simple(simple) => Some(simple.address_prefix), 53 | IntermediateAddr::Ext(ext) => Some(ext.address_prefix), 54 | } 55 | } 56 | } 57 | 58 | impl From for IntermediateAddr { 59 | #[inline] 60 | fn from(addr: IntermediateAddrRegular) -> Self { 61 | IntermediateAddr::Regular(addr) 62 | } 63 | } 64 | 65 | impl From for IntermediateAddr { 66 | #[inline] 67 | fn from(addr: IntermediateAddrSimple) -> Self { 68 | IntermediateAddr::Simple(addr) 69 | } 70 | } 71 | 72 | impl From for IntermediateAddr { 73 | #[inline] 74 | fn from(addr: IntermediateAddrExt) -> Self { 75 | IntermediateAddr::Ext(addr) 76 | } 77 | } 78 | 79 | impl Store for IntermediateAddr { 80 | fn store_into(&self, builder: &mut CellBuilder, cx: &dyn CellContext) -> Result<(), Error> { 81 | match self { 82 | IntermediateAddr::Regular(addr) => { 83 | ok!(builder.store_bit_zero()); // tag = $0 84 | addr.store_into(builder, cx) 85 | } 86 | IntermediateAddr::Simple(addr) => { 87 | ok!(builder.store_small_uint(0b10, 2)); // tag = $10 88 | addr.store_into(builder, cx) 89 | } 90 | IntermediateAddr::Ext(addr) => { 91 | ok!(builder.store_small_uint(0b11, 2)); // tag = $11 92 | addr.store_into(builder, cx) 93 | } 94 | } 95 | } 96 | } 97 | 98 | impl<'a> Load<'a> for IntermediateAddr { 99 | fn load_from(slice: &mut CellSlice<'a>) -> Result { 100 | if unlikely(slice.load_bit()?) { 101 | if unlikely(slice.load_bit()?) { 102 | // tag = $11 103 | IntermediateAddrExt::load_from(slice).map(IntermediateAddr::Ext) 104 | } else { 105 | // tag = $10 106 | IntermediateAddrSimple::load_from(slice).map(IntermediateAddr::Simple) 107 | } 108 | } else { 109 | // tag = $0 110 | IntermediateAddrRegular::load_from(slice).map(IntermediateAddr::Regular) 111 | } 112 | } 113 | } 114 | 115 | /// Destination prefix length whithin the same workchain. 116 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Store, Load)] 117 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 118 | #[tlb(validate_with = "Self::is_valid")] 119 | pub struct IntermediateAddrRegular { 120 | /// Destination address prefix length in bits. 121 | use_dest_bits: u8, 122 | } 123 | 124 | impl IntermediateAddrRegular { 125 | /// Full address prefix length in bits. 126 | pub const FULL_BITS: u8 = 96; 127 | 128 | /// Returns whether the address prefix length is valid. 129 | pub const fn is_valid(&self) -> bool { 130 | self.use_dest_bits <= Self::FULL_BITS 131 | } 132 | 133 | /// Create for the destination address. 134 | pub fn with_dest_bits(use_dest_bits: u8) -> Option { 135 | (use_dest_bits <= Self::FULL_BITS).then_some(IntermediateAddrRegular { use_dest_bits }) 136 | } 137 | 138 | /// Create for the source address. 139 | pub fn with_src_bits(use_src_bits: u8) -> Option { 140 | (use_src_bits <= Self::FULL_BITS).then(|| IntermediateAddrRegular { 141 | use_dest_bits: Self::FULL_BITS - use_src_bits, 142 | }) 143 | } 144 | 145 | /// Returns the destination address prefix length in bits. 146 | pub fn use_dest_bits(&self) -> u8 { 147 | self.use_dest_bits 148 | } 149 | 150 | /// Returns the source address prefix length in bits. 151 | pub fn use_src_bits(&self) -> u8 { 152 | Self::FULL_BITS - self.use_dest_bits 153 | } 154 | } 155 | 156 | /// Address prefix with a basic workchain id. 157 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Load, Store)] 158 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 159 | pub struct IntermediateAddrSimple { 160 | /// Basic workchain id. 161 | /// 162 | /// See [`WorkchainFormatBasic`]. 163 | /// 164 | /// [`WorkchainFormatBasic`]: crate::models::WorkchainFormatBasic 165 | pub workchain: i8, 166 | 167 | /// High 64 bits of the address. 168 | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_account_prefix"))] 169 | pub address_prefix: u64, 170 | } 171 | 172 | /// Address prefix with an extended workchain id. 173 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Store, Load)] 174 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 175 | pub struct IntermediateAddrExt { 176 | /// Workchain ID 177 | pub workchain: i32, 178 | 179 | /// High 64 bits of the address. 180 | #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_account_prefix"))] 181 | pub address_prefix: u64, 182 | } 183 | 184 | /// Message with routing information. 185 | #[derive(Clone, Debug, Eq, PartialEq, Store, Load)] 186 | #[cfg_attr(feature = "serde", derive(serde::Serialize))] 187 | #[tlb(tag = "#4")] 188 | pub struct MsgEnvelope { 189 | /// Current address. 190 | pub cur_addr: IntermediateAddr, 191 | /// Next-hop address. 192 | pub next_addr: IntermediateAddr, 193 | /// Remaining transit fee. 194 | pub fwd_fee_remaining: Tokens, 195 | /// The message itself. 196 | #[cfg_attr(feature = "serde", serde(serialize_with = "Lazy::serialize_repr_hash"))] 197 | pub message: Lazy, 198 | } 199 | 200 | impl MsgEnvelope { 201 | /// Load only message info. 202 | pub fn load_message_info(&self) -> Result { 203 | if let MsgInfo::Int(info) = ok!(<_>::load_from(&mut ok!(self.message.as_slice()))) { 204 | Ok(info) 205 | } else { 206 | Err(Error::InvalidData) 207 | } 208 | } 209 | 210 | /// Load a non-owned message. 211 | pub fn load_message(&self) -> Result, Error> { 212 | self.message.cast_ref::>().load() 213 | } 214 | 215 | /// Load an owned message. 216 | pub fn load_message_owned(&self) -> Result { 217 | self.message.load() 218 | } 219 | 220 | /// Returns a hash of the message. 221 | pub fn message_hash(&self) -> &HashBytes { 222 | self.message.repr_hash() 223 | } 224 | 225 | /// Tries to substract transfer fee from envelope. 226 | pub fn collect_fee(&mut self, fee: Tokens) -> bool { 227 | match self.fwd_fee_remaining.checked_sub(fee) { 228 | Some(remaining) => { 229 | self.fwd_fee_remaining = remaining; 230 | true 231 | } 232 | None => false, 233 | } 234 | } 235 | } 236 | 237 | #[cfg(feature = "serde")] 238 | fn serialize_account_prefix(prefix: &u64, serializer: S) -> Result 239 | where 240 | S: serde::Serializer, 241 | { 242 | serializer.serialize_str(&format!("{:08x}", prefix)) 243 | } 244 | -------------------------------------------------------------------------------- /src/models/message/tests/empty_internal_message.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/empty_internal_message.boc -------------------------------------------------------------------------------- /src/models/message/tests/external_message.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/external_message.boc -------------------------------------------------------------------------------- /src/models/message/tests/external_message_body.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/external_message_body.boc -------------------------------------------------------------------------------- /src/models/message/tests/external_out_message.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/external_out_message.boc -------------------------------------------------------------------------------- /src/models/message/tests/internal_message_body.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/internal_message_body.boc -------------------------------------------------------------------------------- /src/models/message/tests/internal_message_with_body.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/internal_message_with_body.boc -------------------------------------------------------------------------------- /src/models/message/tests/internal_message_with_deploy.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/internal_message_with_deploy.boc -------------------------------------------------------------------------------- /src/models/message/tests/internal_message_with_deploy_body.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/internal_message_with_deploy_body.boc -------------------------------------------------------------------------------- /src/models/message/tests/internal_message_with_deploy_special.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/internal_message_with_deploy_special.boc -------------------------------------------------------------------------------- /src/models/message/tests/internal_message_with_deploy_state_init.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/message/tests/internal_message_with_deploy_state_init.boc -------------------------------------------------------------------------------- /src/models/message/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Borrow; 2 | 3 | use super::*; 4 | use crate::prelude::*; 5 | 6 | fn serialize_message<'a, T: Borrow>>(message: T) -> Cell { 7 | CellBuilder::build_from(message.borrow()).unwrap() 8 | } 9 | 10 | fn check_message(boc: &[u8]) -> Cell { 11 | let boc = Boc::decode(boc).unwrap(); 12 | let message = boc.parse::().unwrap(); 13 | println!("message: {message:#?}"); 14 | 15 | if let Some(init) = &message.init { 16 | let init = CellBuilder::build_from(init).unwrap(); 17 | println!("{}", Boc::encode_base64(init)); 18 | } 19 | 20 | let serialized = serialize_message(&message); 21 | assert_eq!(serialized.as_ref(), boc.as_ref()); 22 | 23 | // Check an owned version 24 | { 25 | let owned = Lazy::>::from_raw(boc.clone()) 26 | .unwrap() 27 | .cast_into::() 28 | .load() 29 | .unwrap(); 30 | 31 | assert_eq!(CellBuilder::build_from(owned).unwrap(), boc); 32 | }; 33 | 34 | boc 35 | } 36 | 37 | #[test] 38 | fn external_message() -> anyhow::Result<()> { 39 | let boc = check_message(include_bytes!("external_message.boc")); 40 | assert_eq!(boc.parse::()?, MsgType::ExtIn); 41 | 42 | let body = Boc::decode(include_bytes!("external_message_body.boc")).unwrap(); 43 | let serialized = serialize_message(Message { 44 | info: MsgInfo::ExtIn(ExtInMsgInfo { 45 | dst: "0:8c8d0cc80ae34b93fe189fdefc0536745e40fab2a9179b37c24a419f04cd8e21" 46 | .parse() 47 | .unwrap(), 48 | ..Default::default() 49 | }), 50 | init: None, 51 | body: body.as_slice()?, 52 | layout: None, 53 | }); 54 | assert_eq!(boc.as_ref(), serialized.as_ref()); 55 | 56 | Ok(()) 57 | } 58 | 59 | #[test] 60 | fn external_outgoing() { 61 | let boc = check_message(include_bytes!("external_out_message.boc")); 62 | assert_eq!(boc.parse::().unwrap(), MsgType::ExtOut); 63 | 64 | let body = Boc::decode_base64("te6ccgEBAQEADgAAGJMdgs1k/wsgCERmwQ==").unwrap(); 65 | let serialized = serialize_message(Message { 66 | info: MsgInfo::ExtOut(ExtOutMsgInfo { 67 | src: "0:3addd84bf73267312a477049fd9b8db761bf39c585c150f8e6f9451347af2b6c" 68 | .parse() 69 | .unwrap(), 70 | dst: None, 71 | created_lt: 41854595000003, 72 | created_at: 1694436128, 73 | }), 74 | init: None, 75 | body: body.as_slice().unwrap(), 76 | layout: None, 77 | }); 78 | assert_eq!(boc.as_ref(), serialized.as_ref()); 79 | } 80 | 81 | #[test] 82 | fn internal_message_empty() { 83 | let boc = check_message(include_bytes!("empty_internal_message.boc")); 84 | assert_eq!(boc.parse::().unwrap(), MsgType::Int); 85 | 86 | let serialized = serialize_message(Message { 87 | info: MsgInfo::Int(IntMsgInfo { 88 | ihr_disabled: true, 89 | src: "0:a921453472366b7feeec15323a96b5dcf17197c88dc0d4578dfa52900b8a33cb" 90 | .parse() 91 | .unwrap(), 92 | dst: "0:a921453472366b7feeec15323a96b5dcf17197c88dc0d4578dfa52900b8a33cb" 93 | .parse() 94 | .unwrap(), 95 | value: CurrencyCollection::new(1000000000), 96 | fwd_fee: Tokens::new(666672), 97 | created_lt: 34447525000002, 98 | created_at: 1673886009, 99 | ..Default::default() 100 | }), 101 | init: None, 102 | body: Default::default(), 103 | layout: None, 104 | }); 105 | assert_eq!(boc.as_ref(), serialized.as_ref()); 106 | } 107 | 108 | #[test] 109 | fn internal_message_with_body() -> anyhow::Result<()> { 110 | let boc = check_message(include_bytes!("internal_message_with_body.boc")); 111 | assert_eq!(boc.parse::().unwrap(), MsgType::Int); 112 | 113 | let body = Boc::decode(include_bytes!("internal_message_body.boc")).unwrap(); 114 | 115 | let serialized = serialize_message(Message { 116 | info: MsgInfo::Int(IntMsgInfo { 117 | ihr_disabled: true, 118 | bounce: true, 119 | src: "0:82615d4ce6bcd9989a82c9329f65569922f3437830eaa1003444b3fa4a46490f".parse()?, 120 | dst: "0:a732bba1c348ddae0970a541276e9cde4e44ac2c55e8079d034f88b0304f7c08".parse()?, 121 | value: CurrencyCollection::new(97621000), 122 | fwd_fee: Tokens::new(1586013), 123 | created_lt: 34447244000006, 124 | created_at: 1673885188, 125 | ..Default::default() 126 | }), 127 | init: None, 128 | body: body.as_slice()?, 129 | layout: Some(MessageLayout { 130 | init_to_cell: false, 131 | body_to_cell: true, 132 | }), 133 | }); 134 | assert_eq!(boc.as_ref(), serialized.as_ref()); 135 | 136 | Ok(()) 137 | } 138 | 139 | #[test] 140 | fn internal_message_with_deploy() -> anyhow::Result<()> { 141 | let boc = check_message(include_bytes!("internal_message_with_deploy.boc")); 142 | assert_eq!(boc.parse::().unwrap(), MsgType::Int); 143 | 144 | let init = Boc::decode(include_bytes!( 145 | "internal_message_with_deploy_state_init.boc" 146 | )) 147 | .unwrap(); 148 | let init = init.parse::().unwrap(); 149 | 150 | let body = Boc::decode(include_bytes!("internal_message_with_deploy_body.boc")).unwrap(); 151 | 152 | let serialized = serialize_message(Message { 153 | info: MsgInfo::Int(IntMsgInfo { 154 | ihr_disabled: true, 155 | bounce: true, 156 | src: "0:098c37c0d8a78b32826de1d956242ee7830f83016eaa930e8c535295aea3ff1b".parse()?, 157 | dst: "0:a4232bb25ca73b09e1bb5200f87548f5a51a2d143d296a5a86b4bf74ec83e662".parse()?, 158 | value: CurrencyCollection::new(100000000), 159 | fwd_fee: Tokens::new(28859554), 160 | created_lt: 34447559000008, 161 | created_at: 1673886111, 162 | ..Default::default() 163 | }), 164 | init: Some(init), 165 | body: body.as_slice()?, 166 | layout: Some(MessageLayout { 167 | init_to_cell: true, 168 | body_to_cell: true, 169 | }), 170 | }); 171 | assert_eq!(boc.as_ref(), serialized.as_ref()); 172 | 173 | Ok(()) 174 | } 175 | 176 | #[test] 177 | fn internal_message_with_deploy_special() -> anyhow::Result<()> { 178 | use crate::models::account::*; 179 | 180 | let boc = check_message(include_bytes!("internal_message_with_deploy_special.boc")); 181 | assert_eq!(boc.parse::().unwrap(), MsgType::Int); 182 | 183 | let init = StateInit { 184 | split_depth: None, 185 | special: Some(SpecialFlags { 186 | tick: true, 187 | tock: true, 188 | }), 189 | code: Some(Boc::decode_base64("te6ccgEBAQEABQAABv8AAA==")?), 190 | data: Some(Boc::decode_base64("te6ccgEBAQEABQAABv8AAA==")?), 191 | libraries: Default::default(), 192 | }; 193 | 194 | let serialized = serialize_message(Message { 195 | info: MsgInfo::Int(IntMsgInfo { 196 | ihr_disabled: true, 197 | src: "0:b62450b8355ae57d4e1530dda442e17dda60f39cee7cc0a34795566e30630dbf".parse()?, 198 | dst: "-1:a0b65eadaf741a132467f027eedc971a3d4f0d7ad34cc18edafac9d3c198fd9b".parse()?, 199 | value: CurrencyCollection::new(969351000), 200 | fwd_fee: Tokens::new(8206730), 201 | created_lt: 11129123000005, 202 | created_at: 1668282519, 203 | ..Default::default() 204 | }), 205 | init: Some(init), 206 | body: Default::default(), 207 | layout: Some(MessageLayout { 208 | init_to_cell: true, 209 | body_to_cell: false, 210 | }), 211 | }); 212 | assert_eq!(boc.as_ref(), serialized.as_ref()); 213 | 214 | Ok(()) 215 | } 216 | -------------------------------------------------------------------------------- /src/models/mod.rs: -------------------------------------------------------------------------------- 1 | //! Blockchain models. 2 | 3 | pub use account::*; 4 | pub use block::*; 5 | pub use config::*; 6 | pub use currency::*; 7 | pub use global_version::*; 8 | pub use message::*; 9 | pub use shard::*; 10 | pub use transaction::*; 11 | pub use vm::*; 12 | 13 | pub mod account; 14 | pub mod block; 15 | pub mod config; 16 | pub mod currency; 17 | pub mod global_version; 18 | pub mod message; 19 | pub mod shard; 20 | pub mod transaction; 21 | pub mod vm; 22 | 23 | #[cfg(feature = "sync")] 24 | #[doc(hidden)] 25 | mod __checks { 26 | use super::*; 27 | 28 | assert_impl_all!(crate::cell::Lazy: Send, Sync); 29 | assert_impl_all!(Account: Send, Sync); 30 | assert_impl_all!(Block: Send, Sync); 31 | assert_impl_all!(Message: Send, Sync); 32 | assert_impl_all!(Transaction: Send, Sync); 33 | } 34 | -------------------------------------------------------------------------------- /src/models/shard/shard_accounts.rs: -------------------------------------------------------------------------------- 1 | use crate::cell::*; 2 | use crate::dict::{AugDict, AugDictExtra}; 3 | use crate::error::*; 4 | use crate::models::currency::CurrencyCollection; 5 | use crate::models::ShardAccount; 6 | 7 | /// A dictionary of account states. 8 | pub type ShardAccounts = AugDict; 9 | 10 | /// Intermediate balance info. 11 | #[derive(Debug, Default, Clone, Eq, PartialEq)] 12 | pub struct DepthBalanceInfo { 13 | /// Depth for which the balance was calculated. 14 | pub split_depth: u8, 15 | /// Total balance for a subtree. 16 | pub balance: CurrencyCollection, 17 | } 18 | 19 | impl DepthBalanceInfo { 20 | const SPLIT_DEPTH_BITS: u16 = 5; 21 | 22 | /// Returns `true` if the split depth is valid. 23 | #[inline] 24 | pub const fn is_valid(&self) -> bool { 25 | self.split_depth <= 30 26 | } 27 | } 28 | 29 | impl AugDictExtra for DepthBalanceInfo { 30 | fn comp_add( 31 | left: &mut CellSlice, 32 | right: &mut CellSlice, 33 | b: &mut CellBuilder, 34 | cx: &dyn CellContext, 35 | ) -> Result<(), Error> { 36 | let left = ok!(Self::load_from(left)); 37 | let right = ok!(Self::load_from(right)); 38 | Self { 39 | split_depth: std::cmp::max(left.split_depth, right.split_depth), 40 | balance: ok!(left.balance.checked_add(&right.balance)), 41 | } 42 | .store_into(b, cx) 43 | } 44 | } 45 | 46 | impl Store for DepthBalanceInfo { 47 | fn store_into( 48 | &self, 49 | builder: &mut CellBuilder, 50 | context: &dyn CellContext, 51 | ) -> Result<(), Error> { 52 | if !self.is_valid() { 53 | return Err(Error::IntOverflow); 54 | } 55 | ok!(builder.store_small_uint(self.split_depth, Self::SPLIT_DEPTH_BITS)); 56 | self.balance.store_into(builder, context) 57 | } 58 | } 59 | 60 | impl<'a> Load<'a> for DepthBalanceInfo { 61 | fn load_from(slice: &mut CellSlice<'a>) -> Result { 62 | let result = Self { 63 | split_depth: ok!(slice.load_small_uint(Self::SPLIT_DEPTH_BITS)), 64 | balance: ok!(CurrencyCollection::load_from(slice)), 65 | }; 66 | if result.is_valid() { 67 | Ok(result) 68 | } else { 69 | Err(Error::IntOverflow) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/models/shard/tests/everscale_zerostate.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/shard/tests/everscale_zerostate.boc -------------------------------------------------------------------------------- /src/models/shard/tests/first_block.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/shard/tests/first_block.boc -------------------------------------------------------------------------------- /src/models/shard/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::models::Block; 3 | use crate::prelude::Boc; 4 | 5 | fn check_master_state(cell: Cell) { 6 | let data = cell.parse::().unwrap(); 7 | println!("data: {data:#?}"); 8 | assert_eq!(CellBuilder::build_from(&data).unwrap(), cell); 9 | 10 | let shard_accounts = data.load_accounts().unwrap(); 11 | assert_eq!( 12 | CellBuilder::build_from(&shard_accounts).unwrap(), 13 | data.accounts 14 | ); 15 | 16 | for entry in shard_accounts.iter() { 17 | let (id, depth_balance, shard_state) = entry.unwrap(); 18 | let account = shard_state.load_account().unwrap(); 19 | println!("{id}: {depth_balance:?} {account:#?}"); 20 | } 21 | 22 | for (i, entry) in data.libraries.iter().enumerate() { 23 | let (hash, descr) = entry.unwrap(); 24 | println!("lib#{i} hash={hash}"); 25 | for entry in descr.publishers.keys() { 26 | let address = entry.unwrap(); 27 | println!("lib#{i} publisher={address}") 28 | } 29 | } 30 | 31 | let _elector = shard_accounts.get([0x33; 32]).unwrap().unwrap(); 32 | assert!(shard_accounts.contains_key([0x55; 32]).unwrap()); 33 | 34 | let custom = data.load_custom().unwrap().unwrap(); 35 | println!("custom: {custom:#?}"); 36 | assert_eq!( 37 | CellBuilder::build_from(&custom).unwrap(), 38 | data.custom.unwrap() 39 | ); 40 | } 41 | 42 | #[test] 43 | fn prod_zerostate() { 44 | const BOC: &[u8] = include_bytes!("everscale_zerostate.boc"); 45 | check_master_state(Boc::decode(BOC).unwrap()); 46 | } 47 | 48 | #[test] 49 | fn new_zerostate() { 50 | const BOC: &[u8] = include_bytes!("new_zerostate.boc"); 51 | let zerostate = Boc::decode(BOC).unwrap(); 52 | check_master_state(zerostate.clone()); 53 | 54 | let block = Boc::decode(include_bytes!("first_block.boc")).unwrap(); 55 | let block = block.parse::().unwrap(); 56 | let state_update = block.state_update.load().unwrap(); 57 | 58 | let new_state = state_update.apply(&zerostate).unwrap(); 59 | check_master_state(new_state); 60 | } 61 | -------------------------------------------------------------------------------- /src/models/shard/tests/new_zerostate.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/shard/tests/new_zerostate.boc -------------------------------------------------------------------------------- /src/models/transaction/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::prelude::{Boc, Cell, CellBuilder}; 3 | 4 | fn check_tx(boc: &[u8]) -> Cell { 5 | let boc = Boc::decode(boc).unwrap(); 6 | let tx = boc.parse::().unwrap(); 7 | 8 | #[cfg(feature = "serde")] 9 | { 10 | let json = serde_json::to_string_pretty(&tx).unwrap(); 11 | println!("{json}"); 12 | 13 | let parsed = serde_json::from_str::(&json).unwrap(); 14 | let parsed_boc = CellBuilder::build_from(&parsed).unwrap(); 15 | assert_eq!(boc.repr_hash(), parsed_boc.repr_hash()); 16 | } 17 | 18 | println!("tx: {tx:#?}"); 19 | 20 | let in_msg = tx.load_in_msg().unwrap(); 21 | println!("In message: {in_msg:?}"); 22 | 23 | for (i, entry) in tx.out_msgs.iter().enumerate() { 24 | let (number, cell) = entry.unwrap(); 25 | let message = cell.parse::().unwrap(); 26 | assert_eq!(number, i as u16); 27 | println!("Out message: {i}, message: {message:?}"); 28 | } 29 | assert_eq!( 30 | tx.out_msg_count.into_inner() as usize, 31 | tx.out_msgs.raw_values().count() 32 | ); 33 | 34 | let mut out_msg_count = 0; 35 | for msg in tx.iter_out_msgs() { 36 | msg.unwrap(); 37 | out_msg_count += 1; 38 | } 39 | assert_eq!(out_msg_count, tx.out_msg_count); 40 | 41 | let info = tx.load_info().unwrap(); 42 | println!("info: {info:#?}"); 43 | assert_eq!(tx.info, CellBuilder::build_from(info).unwrap()); 44 | 45 | let serialized = CellBuilder::build_from(tx).unwrap(); 46 | assert_eq!(serialized, boc); 47 | serialized 48 | } 49 | 50 | #[test] 51 | fn ordinary_tx_without_outgoing() { 52 | check_tx(include_bytes!("ordinary_tx_without_outgoing.boc")); 53 | } 54 | 55 | #[test] 56 | fn ordinary_tx_with_outgoing() { 57 | check_tx(include_bytes!("ordinary_tx_with_outgoing.boc")); 58 | } 59 | 60 | #[test] 61 | fn ordinary_tx_with_external() { 62 | check_tx(include_bytes!("ordinary_tx_with_external.boc")); 63 | } 64 | 65 | #[test] 66 | fn ordinary_tx_recursive() { 67 | check_tx(include_bytes!("ordinary_tx_recursive.boc")); 68 | } 69 | 70 | #[test] 71 | fn ordinary_bounce() { 72 | // Ok, no state 73 | check_tx(include_bytes!("ordinary_tx_bounce_no_state.boc")); 74 | 75 | // NoFunds 76 | check_tx(include_bytes!("ordinary_tx_bounce_no_funds.boc")); 77 | } 78 | 79 | #[test] 80 | fn tick_tx() { 81 | // Tick 82 | check_tx(include_bytes!("tick_tx.boc")); 83 | } 84 | 85 | #[test] 86 | fn tock_tx() { 87 | check_tx(include_bytes!("tock_tx.boc")); 88 | } 89 | -------------------------------------------------------------------------------- /src/models/transaction/tests/ordinary_tx_bounce_no_funds.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/transaction/tests/ordinary_tx_bounce_no_funds.boc -------------------------------------------------------------------------------- /src/models/transaction/tests/ordinary_tx_bounce_no_state.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/transaction/tests/ordinary_tx_bounce_no_state.boc -------------------------------------------------------------------------------- /src/models/transaction/tests/ordinary_tx_recursive.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/transaction/tests/ordinary_tx_recursive.boc -------------------------------------------------------------------------------- /src/models/transaction/tests/ordinary_tx_with_external.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/transaction/tests/ordinary_tx_with_external.boc -------------------------------------------------------------------------------- /src/models/transaction/tests/ordinary_tx_with_outgoing.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/transaction/tests/ordinary_tx_with_outgoing.boc -------------------------------------------------------------------------------- /src/models/transaction/tests/ordinary_tx_without_outgoing.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/transaction/tests/ordinary_tx_without_outgoing.boc -------------------------------------------------------------------------------- /src/models/transaction/tests/tick_tx.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/transaction/tests/tick_tx.boc -------------------------------------------------------------------------------- /src/models/transaction/tests/tock_tx.boc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/broxus/everscale-types/3c23f629b5b2144d5be596dcb93fd1a146484468/src/models/transaction/tests/tock_tx.boc -------------------------------------------------------------------------------- /src/models/vm/mod.rs: -------------------------------------------------------------------------------- 1 | //! VM related models. 2 | 3 | pub use self::out_actions::*; 4 | 5 | mod out_actions; 6 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! The `everscale-types` prelude. 2 | //! 3 | //! This brings into scope a number of traits and commonly used type aliases. 4 | 5 | pub use crate::boc::{Boc, BocRepr}; 6 | pub use crate::cell::{ 7 | Cell, CellBuilder, CellContext, CellDataBuilder, CellFamily, CellImpl, CellSlice, 8 | CellSliceParts, CellSliceRange, CellType, DynCell, EquivalentRepr, ExactSize, HashBytes, Load, 9 | LoadCell, Size, Store, UsageTree, UsageTreeMode, WeakCell, 10 | }; 11 | pub use crate::dict::{AugDict, Dict, DictKey, LoadDictKey, RawDict, StoreDictKey}; 12 | #[cfg(feature = "bigint")] 13 | pub use crate::util::BigIntExt; 14 | --------------------------------------------------------------------------------