├── .circleci └── config.yml ├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── editorconfig.yml │ └── main.yml ├── .gitignore ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── cardano-legacy-address ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches │ └── cbor.rs └── src │ ├── address.rs │ ├── base58.rs │ ├── cbor.rs │ ├── crc32.rs │ └── lib.rs ├── chain-addr ├── Cargo.toml └── src │ ├── lib.rs │ └── testing.rs ├── chain-core ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── property.rs ├── chain-crypto ├── Cargo.toml ├── benches │ ├── p256k1.rs │ ├── ristretto.rs │ ├── sumed25519.rs │ └── vrf.rs └── src │ ├── algorithms │ ├── ed25519.rs │ ├── ed25519_derive.rs │ ├── ed25519_extended.rs │ ├── mod.rs │ ├── sumed25519 │ │ ├── common.rs │ │ ├── mod.rs │ │ ├── sum.rs │ │ └── sumrec.rs │ ├── vrf │ │ ├── mod.rs │ │ └── vrf.rs │ └── zkps │ │ ├── challenge_context.rs │ │ ├── dleq.rs │ │ └── mod.rs │ ├── asymlock.rs │ ├── bech32.rs │ ├── digest.rs │ ├── ec │ ├── macros.rs │ ├── mod.rs │ ├── p256k1.rs │ └── ristretto255.rs │ ├── evolving.rs │ ├── hash.rs │ ├── kes.rs │ ├── key.rs │ ├── lib.rs │ ├── multilock.rs │ ├── role.rs │ ├── sign.rs │ ├── testing.rs │ └── vrf.rs ├── chain-evm ├── Cargo.toml ├── README.md └── src │ ├── crypto.rs │ ├── lib.rs │ ├── machine.rs │ ├── precompiles │ ├── blake2.rs │ ├── bn128.rs │ ├── hash.rs │ ├── identity.rs │ ├── mod.rs │ ├── modexp.rs │ ├── native.rs │ ├── prelude.rs │ ├── secp256k1.rs │ └── utils.rs │ ├── signature.rs │ ├── state │ ├── account.rs │ ├── logs.rs │ ├── mod.rs │ ├── storage.rs │ └── trie.rs │ ├── tests.rs │ ├── transaction.rs │ └── util.rs ├── chain-impl-mockchain ├── Cargo.toml ├── README.md ├── benches │ └── tally.rs ├── doc │ ├── address.md │ ├── format.abnf │ ├── format.md │ ├── incentives.md │ ├── multisig.md │ ├── overview.md │ └── update.md └── src │ ├── account.rs │ ├── accounting │ ├── account │ │ ├── account_state.rs │ │ ├── last_rewards.rs │ │ ├── mod.rs │ │ ├── spending.rs │ │ └── test.rs │ └── mod.rs │ ├── block │ ├── builder.rs │ ├── header.rs │ ├── mod.rs │ └── test.rs │ ├── certificate │ ├── delegation.rs │ ├── evm_mapping.rs │ ├── mint_token.rs │ ├── mod.rs │ ├── pool.rs │ ├── test.rs │ ├── update_proposal.rs │ ├── update_vote.rs │ ├── vote_cast.rs │ ├── vote_plan.rs │ └── vote_tally.rs │ ├── chaineval.rs │ ├── chaintypes.rs │ ├── config.rs │ ├── date.rs │ ├── error.rs │ ├── evm │ └── mod.rs │ ├── fee.rs │ ├── fragment │ ├── config.rs │ ├── content.rs │ ├── mod.rs │ └── test.rs │ ├── header │ ├── builder.rs │ ├── components.rs │ ├── cstruct.rs │ ├── deconstruct.rs │ ├── header.rs │ ├── mod.rs │ ├── test.rs │ └── version.rs │ ├── key.rs │ ├── leadership │ ├── bft.rs │ ├── genesis │ │ ├── mod.rs │ │ └── vrfeval.rs │ └── mod.rs │ ├── ledger │ ├── check.rs │ ├── evm.rs │ ├── governance │ │ ├── mod.rs │ │ ├── parameters.rs │ │ └── treasury.rs │ ├── info.rs │ ├── iter.rs │ ├── leaderlog.rs │ ├── ledger.rs │ ├── mod.rs │ ├── pots.rs │ ├── recovery.rs │ ├── reward_info.rs │ ├── tests │ │ ├── apply_block_tests.rs │ │ ├── certificate_tests │ │ │ ├── mod.rs │ │ │ ├── pool_registration.rs │ │ │ ├── pool_update.rs │ │ │ ├── tokens.rs │ │ │ ├── update.rs │ │ │ └── voting.rs │ │ ├── discrimination_tests.rs │ │ ├── initial_funds_tests.rs │ │ ├── ledger_tests.rs │ │ ├── macros.rs │ │ ├── mod.rs │ │ └── transaction_tests.rs │ └── token_distribution.rs │ ├── legacy.rs │ ├── lib.rs │ ├── milli.rs │ ├── multisig │ ├── declaration.rs │ ├── index.rs │ ├── ledger.rs │ ├── mod.rs │ └── witness.rs │ ├── multiverse.rs │ ├── rewards.rs │ ├── setting.rs │ ├── stake │ ├── controlled.rs │ ├── delegation.rs │ ├── distribution.rs │ ├── mod.rs │ └── stake.rs │ ├── testing │ ├── arbitrary │ │ ├── address.rs │ │ ├── config_builder.rs │ │ ├── kind_type.rs │ │ ├── ledger_builder.rs │ │ ├── mod.rs │ │ ├── output.rs │ │ ├── random.rs │ │ ├── transaction.rs │ │ ├── update_proposal.rs │ │ ├── utils.rs │ │ ├── utxo.rs │ │ └── wallet.rs │ ├── builders │ │ ├── block_builder.rs │ │ ├── cert_builder.rs │ │ ├── initial_builder.rs │ │ ├── mod.rs │ │ ├── old_address_builder.rs │ │ ├── stake_pool_builder.rs │ │ ├── tx_builder.rs │ │ ├── tx_cert_builder.rs │ │ ├── vote.rs │ │ └── witness_builder.rs │ ├── data │ │ ├── address.rs │ │ ├── keys.rs │ │ ├── leader.rs │ │ ├── mod.rs │ │ ├── stake_pool.rs │ │ ├── vote.rs │ │ └── wallet.rs │ ├── e2e │ │ ├── evm_mapping.rs │ │ ├── evm_transaction.rs │ │ ├── fees.rs │ │ ├── management_threshold.rs │ │ ├── mint_token.rs │ │ ├── mod.rs │ │ ├── owner_delegation.rs │ │ ├── pool_update.rs │ │ ├── rewards │ │ │ ├── mod.rs │ │ │ └── tax.rs │ │ ├── spending_counter_lanes.rs │ │ ├── stake_distribution.rs │ │ ├── transactions.rs │ │ ├── update_proposal.rs │ │ ├── vote_private.rs │ │ └── vote_public.rs │ ├── gen │ │ ├── mod.rs │ │ └── vote.rs │ ├── ledger.rs │ ├── mod.rs │ ├── scenario │ │ ├── controller.rs │ │ ├── fragment_factory.rs │ │ ├── mod.rs │ │ ├── scenario_builder.rs │ │ └── template │ │ │ ├── builders.rs │ │ │ └── mod.rs │ ├── serialization.rs │ └── verifiers │ │ ├── ledger_verifier.rs │ │ └── mod.rs │ ├── tokens │ ├── identifier.rs │ ├── minting_policy.rs │ ├── mod.rs │ ├── name.rs │ └── policy_hash.rs │ ├── transaction │ ├── builder.rs │ ├── element.rs │ ├── input.rs │ ├── io.rs │ ├── mod.rs │ ├── payload.rs │ ├── test.rs │ ├── transaction.rs │ ├── transfer.rs │ ├── utxo.rs │ └── witness.rs │ ├── treasury.rs │ ├── update.rs │ ├── utxo.rs │ ├── value.rs │ └── vote │ ├── choice.rs │ ├── committee.rs │ ├── ledger.rs │ ├── manager.rs │ ├── mod.rs │ ├── payload.rs │ ├── privacy.rs │ ├── status.rs │ └── tally.rs ├── chain-network ├── Cargo.toml ├── build.rs ├── proto │ ├── node.proto │ ├── types.proto │ └── watch.proto └── src │ ├── core │ ├── mod.rs │ ├── server │ │ ├── block.rs │ │ ├── fragment.rs │ │ ├── gossip.rs │ │ ├── mod.rs │ │ ├── node.rs │ │ └── push.rs │ └── watch │ │ ├── mod.rs │ │ └── server.rs │ ├── data │ ├── block │ │ ├── block.rs │ │ ├── header.rs │ │ ├── id.rs │ │ ├── mod.rs │ │ └── subscription.rs │ ├── fragment │ │ ├── fragment.rs │ │ ├── id.rs │ │ └── mod.rs │ ├── gossip │ │ ├── mod.rs │ │ └── node.rs │ ├── handshake.rs │ ├── mod.rs │ └── p2p.rs │ ├── error.rs │ ├── grpc │ ├── client.rs │ ├── convert.rs │ ├── legacy.rs │ ├── mod.rs │ ├── proto.rs │ ├── server.rs │ ├── streaming │ │ ├── inbound.rs │ │ ├── mod.rs │ │ └── outbound.rs │ └── watch │ │ ├── client.rs │ │ ├── mod.rs │ │ └── server.rs │ └── lib.rs ├── chain-ser ├── Cargo.toml └── src │ ├── abor.rs │ ├── deser.rs │ ├── lib.rs │ └── packer.rs ├── chain-storage ├── Cargo.toml ├── README.md ├── benches │ └── storage.rs └── src │ ├── block_info.rs │ ├── block_store.rs │ ├── error.rs │ ├── iterator.rs │ ├── lib.rs │ ├── permanent_store.rs │ ├── test_utils.rs │ ├── tests.rs │ └── value.rs ├── chain-time ├── Cargo.toml └── src │ ├── era.rs │ ├── lib.rs │ ├── testing.rs │ ├── timeframe.rs │ ├── timeline.rs │ └── units.rs ├── chain-vote ├── Cargo.toml ├── benches │ └── shvzk.rs ├── build.rs └── src │ ├── committee.rs │ ├── cryptography │ ├── commitment.rs │ ├── elgamal.rs │ ├── mod.rs │ └── zkps │ │ ├── correct_decryption │ │ ├── mod.rs │ │ └── zkp.rs │ │ ├── correct_hybrid_decryption_key │ │ ├── mod.rs │ │ └── zkp.rs │ │ ├── correct_share_generation │ │ ├── mod.rs │ │ └── zkp.rs │ │ ├── dl_equality │ │ ├── challenge_context.rs │ │ ├── mod.rs │ │ └── zkp.rs │ │ ├── mod.rs │ │ └── unit_vector │ │ ├── challenge_context.rs │ │ ├── messages.rs │ │ ├── mod.rs │ │ └── zkp.rs │ ├── encrypted_vote.rs │ ├── lib.rs │ ├── macros.rs │ ├── math │ ├── babystep.rs │ ├── mod.rs │ └── polynomial.rs │ └── tally.rs ├── imhamt ├── Cargo.toml ├── benches │ └── imhamt.rs ├── build.rs ├── examples │ └── memdump │ │ ├── main.rs │ │ └── unix.rs ├── src │ ├── bitmap.rs │ ├── hamt.rs │ ├── hash.rs │ ├── helper.rs │ ├── lib.rs │ ├── node │ │ ├── mod.rs │ │ └── reference.rs │ ├── operation.rs │ └── sharedref.rs └── testing │ └── ui │ └── fail │ ├── ignored_must_use.rs │ └── ignored_must_use.stderr ├── sparse-array ├── Cargo.toml └── src │ ├── bitmap.rs │ ├── fast.rs │ ├── lib.rs │ ├── sparse_array.rs │ └── testing.rs └── typed-bytes ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src ├── builder.rs └── lib.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | labels: 9 | - update-deps 10 | ignore: 11 | - dependency-name: bytes 12 | versions: 13 | - ">= 0.5.a, < 0.6" 14 | - dependency-name: futures 15 | versions: 16 | - ">= 0.3.a, < 0.4" 17 | - dependency-name: hyper 18 | versions: 19 | - ">= 0.13.a, < 0.14" 20 | - dependency-name: r2d2_sqlite 21 | versions: 22 | - "> 0.8, < 1" 23 | - dependency-name: rand 24 | versions: 25 | - ">= 0.8.a, < 0.9" 26 | - dependency-name: rand_chacha 27 | versions: 28 | - ">= 0.3.a, < 0.4" 29 | - dependency-name: rand_core 30 | versions: 31 | - ">= 0.6.a, < 0.7" 32 | - dependency-name: rusqlite 33 | versions: 34 | - "> 0.16.0, < 0.17" 35 | -------------------------------------------------------------------------------- /.github/workflows/editorconfig.yml: -------------------------------------------------------------------------------- 1 | name: EditorConfig check 2 | 3 | on: 4 | push: 5 | 6 | jobs: 7 | check: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: editorconfig-checker/action-editorconfig-checker@v1 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | 3 | /Cargo.lock 4 | **/target/ 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How Can I Contribute? 2 | 3 | ## Reporting Bugs 4 | 5 | This section guides you through submitting a bug report for rust cardano and 6 | related projects. Following these guidelines helps maintainers and the 7 | community understand your report, reproduce the behavior and find related 8 | reports. 9 | 10 | ## Before Submitting A Bug Report 11 | 12 | * Determine which repository the problem should be reported in. 13 | * Perform a cursory search to see if the problem has already been reported. If it has and the issue is still open, add a comment to the existing issue instead of opening a new one. 14 | 15 | ## How Do I Submit A (Good) Bug Report? 16 | 17 | Bug are tracked as github issues, do not email the developpers directly: 18 | 19 | * Use a clear and descriptive title for the issue to identify the problem. 20 | * Describe the exact steps which reproduce the problem 21 | * Provide specific examples to demonstrate the steps 22 | 23 | ## Pull Requests 24 | 25 | To contribute changes to the code, we use github pull requests: 26 | 27 | * Simple to accept changes. 28 | * Allow developpers community to review contributions 29 | * Easy to track what's being work on / in progress. 30 | 31 | ## Style 32 | 33 | ### Rust style 34 | 35 | * We don't enforce a *specific* style. 36 | * We mostly follow a style similar to rustfmt. 37 | * Follow the style of the code you're contributing to. 38 | * Do not submit auto-reformating, and/or code style change as part of another work. 39 | * Use markdown for documentation 40 | 41 | ### Commits 42 | 43 | * We value small descriptive commit that do one thing at a time, as much as possible. 44 | * Keep history clean : don't hesitate to reorder and squash commits if necessary. 45 | * Do not merge the master branch in your topic branch 46 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "imhamt", 4 | "chain-ser", 5 | "chain-core", 6 | "chain-vote", 7 | "chain-addr", 8 | "chain-time", 9 | "chain-crypto", 10 | "chain-network", 11 | "chain-storage", 12 | "chain-impl-mockchain", 13 | "chain-evm", 14 | "cardano-legacy-address", 15 | "sparse-array", 16 | "typed-bytes", 17 | ] 18 | 19 | [profile.bench] 20 | # Embed debuginfo into benchmarks for profiling. All other settings remain the 21 | # same and are similar to the `release` profile. 22 | debug = true 23 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2021 Input Output HK 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 | # Chain-libs 2 | 3 | 🚧 This repo is not maintained anymore please submit your feature proposal or bug report to [Catalyst Core](https://github.com/input-output-hk/catalyst-core). 4 | -------------------------------------------------------------------------------- /cardano-legacy-address/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "cardano-legacy-address" 3 | version = "0.1.1" 4 | authors = ["Vincent Hanquez ", "Nicolas Di Prima "] 5 | license = "MIT OR Apache-2.0" 6 | readme = "README.md" 7 | repository = "https://github.com/input-output-hk/rust-cardano" 8 | homepage = "https://github.com/input-output-hk/rust-cardano/cardano-legacy-address#README.md" 9 | description = """ 10 | Support for the useful part of legacy cardano address. 11 | """ 12 | keywords = [ "Cardano", "Wallet", "Crypto" ] 13 | edition = "2021" 14 | 15 | [build-dependencies] 16 | 17 | [dependencies] 18 | cryptoxide = "0.4" 19 | cbor_event = "^2.1.3" 20 | ed25519-bip32 = "0.4.1" 21 | 22 | criterion = { version = "0.3.0", optional = true } 23 | 24 | [features] 25 | default = [] 26 | with-bench = ["criterion"] 27 | 28 | [[bench]] 29 | harness = false 30 | name = "cbor" 31 | required-features = ["with-bench"] 32 | -------------------------------------------------------------------------------- /cardano-legacy-address/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2019 Input Output HK 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 | -------------------------------------------------------------------------------- /cardano-legacy-address/README.md: -------------------------------------------------------------------------------- 1 | # Cardano Legacy Address 2 | 3 | Support for the main part of old cardano addresses except: 4 | 5 | * stake attribute: never used. 6 | * script address: not used. 7 | * redeem address: deprecated / not used. 8 | 9 | # License 10 | 11 | This project is licensed under either of the following licenses: 12 | 13 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 14 | http://www.apache.org/licenses/LICENSE-2.0) 15 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 16 | http://opensource.org/licenses/MIT) 17 | 18 | Please choose the licence you want to use. 19 | -------------------------------------------------------------------------------- /cardano-legacy-address/benches/cbor.rs: -------------------------------------------------------------------------------- 1 | use cardano_legacy_address::cbor::util::{encode_with_crc32_, raw_with_crc32}; 2 | use cbor_event::{ 3 | self, 4 | de::Deserializer, 5 | se::{Serialize, Serializer}, 6 | }; 7 | use criterion::{criterion_group, criterion_main, Criterion}; 8 | 9 | const CBOR: &[u8] = &[ 10 | 0x82, 0xd8, 0x18, 0x53, 0x52, 0x73, 0x6f, 0x6d, 0x65, 0x20, 0x72, 0x61, 0x6e, 0x64, 0x6f, 0x6d, 11 | 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x1a, 0x71, 0xad, 0x58, 0x36, 12 | ]; 13 | 14 | const BYTES: &[u8] = b"some bytes"; 15 | 16 | struct Test(&'static [u8]); 17 | 18 | impl Serialize for Test { 19 | fn serialize<'se, W>( 20 | &self, 21 | serializer: &'se mut Serializer, 22 | ) -> cbor_event::Result<&'se mut Serializer> 23 | where 24 | W: std::io::Write, 25 | { 26 | serializer.write_bytes(self.0) 27 | } 28 | } 29 | 30 | fn encode_crc32_with_cbor_event(c: &mut Criterion) { 31 | c.bench_function("encode_crc32_with_cbor_event", |b| { 32 | b.iter(|| { 33 | let mut serializer = Serializer::new_vec(); 34 | encode_with_crc32_(&Test(BYTES), &mut serializer).unwrap(); 35 | }) 36 | }); 37 | } 38 | 39 | fn decode_crc32_with_cbor_event(c: &mut Criterion) { 40 | c.bench_function("decode_crc32_with_cbor_event", |b| { 41 | b.iter(|| { 42 | let mut raw = Deserializer::from(CBOR); 43 | let _bytes = raw_with_crc32(&mut raw).unwrap(); 44 | }) 45 | }); 46 | } 47 | 48 | criterion_group!( 49 | benches, 50 | encode_crc32_with_cbor_event, 51 | decode_crc32_with_cbor_event, 52 | ); 53 | criterion_main!(benches); 54 | -------------------------------------------------------------------------------- /cardano-legacy-address/src/cbor.rs: -------------------------------------------------------------------------------- 1 | //! the CBOR util and compatible with the haskell usage... 2 | 3 | pub mod util { 4 | //! CBor util and other stuff 5 | 6 | use crate::crc32::crc32; 7 | use cbor_event::{self, de::Deserializer, se::Serializer, Len}; 8 | 9 | pub fn encode_with_crc32_(t: &T, s: &mut Serializer) -> cbor_event::Result<()> 10 | where 11 | T: cbor_event::Serialize, 12 | W: ::std::io::Write + Sized, 13 | { 14 | let bytes = cbor!(t)?; 15 | let crc32 = crc32(&bytes); 16 | s.write_array(Len::Len(2))? 17 | .write_tag(24)? 18 | .write_bytes(&bytes)? 19 | .write_unsigned_integer(crc32 as u64)?; 20 | Ok(()) 21 | } 22 | 23 | pub fn raw_with_crc32( 24 | raw: &mut Deserializer, 25 | ) -> cbor_event::Result> { 26 | let len = raw.array()?; 27 | assert!(len == Len::Len(2)); 28 | 29 | let tag = raw.tag()?; 30 | if tag != 24 { 31 | return Err(cbor_event::Error::CustomError(format!( 32 | "Invalid Tag: {} but expected 24", 33 | tag 34 | ))); 35 | } 36 | let bytes = raw.bytes()?; 37 | 38 | let crc = raw.unsigned_integer()?; 39 | 40 | let found_crc = crc32(&bytes); 41 | 42 | if crc != found_crc as u64 { 43 | return Err(cbor_event::Error::CustomError(format!( 44 | "Invalid CRC32: 0x{:x} but expected 0x{:x}", 45 | crc, found_crc 46 | ))); 47 | } 48 | 49 | Ok(bytes) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /cardano-legacy-address/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! Cardano Legacy Address generation and parsing 3 | //! 4 | #[macro_use] 5 | extern crate cbor_event; 6 | 7 | extern crate cryptoxide; 8 | 9 | extern crate ed25519_bip32; 10 | 11 | mod address; 12 | mod base58; 13 | mod crc32; 14 | 15 | #[cfg(not(feature = "with-bench"))] 16 | mod cbor; 17 | #[cfg(feature = "with-bench")] 18 | pub mod cbor; 19 | 20 | pub use address::{Addr, AddressMatchXPub, Attributes, ExtendedAddr, ParseExtendedAddrError}; 21 | -------------------------------------------------------------------------------- /chain-addr/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-addr" 3 | version = "0.1.0" 4 | authors = ["Vincent Hanquez ", "Nicolas Di Prima "] 5 | license = "MIT OR Apache-2.0" 6 | edition = "2021" 7 | keywords = [ "Cardano", "Wallet", "Crypto", "Address" ] 8 | 9 | [features] 10 | property-test-api = ["chain-crypto/property-test-api", "quickcheck", "proptest", "test-strategy"] 11 | 12 | [dependencies] 13 | bech32 = "0.8" 14 | chain-core = { path = "../chain-core" } 15 | chain-crypto = { path = "../chain-crypto" } 16 | cryptoxide = "0.4" 17 | 18 | quickcheck = { version = "0.9", optional = true } 19 | proptest = { git = "https://github.com/input-output-hk/proptest.git", optional = true } 20 | test-strategy = { version = "0.1", optional = true } 21 | 22 | [dev-dependencies] 23 | quickcheck = "0.9" 24 | chain-crypto = { path = "../chain-crypto", features = [ "property-test-api" ] } 25 | proptest = { git = "https://github.com/input-output-hk/proptest.git" } 26 | test-strategy = "0.1" 27 | -------------------------------------------------------------------------------- /chain-addr/src/testing.rs: -------------------------------------------------------------------------------- 1 | use crate::{Address, AddressReadable, Discrimination, Kind, KindType}; 2 | use chain_crypto::{Ed25519, KeyPair, PublicKey}; 3 | use quickcheck::{Arbitrary, Gen}; 4 | 5 | impl Arbitrary for Discrimination { 6 | fn arbitrary(g: &mut G) -> Self { 7 | match u8::arbitrary(g) % 2 { 8 | 0 => Discrimination::Production, 9 | 1 => Discrimination::Test, 10 | _ => unreachable!(), 11 | } 12 | } 13 | } 14 | 15 | impl Arbitrary for KindType { 16 | fn arbitrary(g: &mut G) -> Self { 17 | match u8::arbitrary(g) % 5 { 18 | 0 => KindType::Single, 19 | 1 => KindType::Group, 20 | 2 => KindType::Account, 21 | 3 => KindType::Multisig, 22 | 4 => KindType::Script, 23 | _ => unreachable!(), 24 | } 25 | } 26 | } 27 | 28 | const ARBITRARY_PREFIX: &str = "qaddr"; 29 | 30 | impl Arbitrary for AddressReadable { 31 | fn arbitrary(g: &mut G) -> Self { 32 | AddressReadable::from_address(ARBITRARY_PREFIX, &Arbitrary::arbitrary(g)) 33 | } 34 | } 35 | 36 | fn arbitrary_public_key(g: &mut G) -> PublicKey { 37 | let kp: KeyPair = Arbitrary::arbitrary(g); 38 | kp.into_keys().1 39 | } 40 | 41 | fn arbitrary_32bytes(g: &mut G) -> [u8; 32] { 42 | let mut h = [0u8; 32]; 43 | for i in h.iter_mut() { 44 | *i = Arbitrary::arbitrary(g) 45 | } 46 | h 47 | } 48 | 49 | impl Arbitrary for Address { 50 | fn arbitrary(g: &mut G) -> Self { 51 | let discrimination = Discrimination::arbitrary(g); 52 | let kind = Kind::arbitrary(g); 53 | Self(discrimination, kind) 54 | } 55 | } 56 | 57 | impl Arbitrary for Kind { 58 | fn arbitrary(g: &mut G) -> Self { 59 | match u8::arbitrary(g) % 5 { 60 | 0 => Kind::Single(arbitrary_public_key(g)), 61 | 1 => Kind::Group(arbitrary_public_key(g), arbitrary_public_key(g)), 62 | 2 => Kind::Account(arbitrary_public_key(g)), 63 | 3 => { 64 | let h = arbitrary_32bytes(g); 65 | Kind::Multisig(h) 66 | } 67 | 4 => Kind::Script(arbitrary_32bytes(g)), 68 | _ => unreachable!(), 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /chain-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-core" 3 | version = "0.1.0" 4 | authors = ["dev@iohk.io"] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [dependencies] 9 | chain-ser = { path = "../chain-ser" } 10 | -------------------------------------------------------------------------------- /chain-core/README.md: -------------------------------------------------------------------------------- 1 | # Chain Core 2 | 3 | It defines all the core feature of blockchain we are using. 4 | 5 | # Features 6 | 7 | ## Property testing functions 8 | 9 | This crate exports some generic function to test the property of some the trait 10 | that are being provided. To enable them, use the feature `property-test-apis` 11 | when adding the dependency on this module. 12 | -------------------------------------------------------------------------------- /chain-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use chain_ser::abor; 2 | pub use chain_ser::packer; 3 | pub mod property; 4 | -------------------------------------------------------------------------------- /chain-crypto/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-crypto" 3 | version = "0.1.0" 4 | authors = ["Vincent Hanquez "] 5 | license = "MIT OR Apache-2.0" 6 | edition = "2021" 7 | keywords = [ "Crypto", "VRF", "Ed25519", "MMM" ] 8 | 9 | [dependencies] 10 | bech32 = "0.8" 11 | cryptoxide = "0.4" 12 | curve25519-dalek-ng = { version = "4.0" } 13 | eccoxide = { version = "0.3", optional = true } 14 | ed25519-dalek = "1.0" 15 | sha2 = "0.10" 16 | generic-array = "^0.14" 17 | rand_core = "0.6" 18 | rand = { version = "0.8", features = ["small_rng"], optional = true } 19 | rayon = "1.5" 20 | ed25519-bip32 = "0.4.1" 21 | hex = "0.4.0" 22 | typed-bytes = { path = "../typed-bytes" } 23 | 24 | criterion = { version = "0.3.0", optional = true } 25 | quickcheck = { version = "0.9", optional = true } 26 | proptest = { git = "https://github.com/input-output-hk/proptest.git", optional = true } 27 | test-strategy = { version = "0.1", optional = true } 28 | 29 | [dev-dependencies] 30 | quickcheck = "0.9" 31 | proptest = { git = "https://github.com/input-output-hk/proptest.git" } 32 | test-strategy = "0.1" 33 | rand = { version = "0.8", features = ["small_rng"] } 34 | smoke = "^0.2.1" 35 | 36 | [features] 37 | with-bench = ["criterion"] 38 | property-test-api = [ "quickcheck", "rand", "proptest", "test-strategy" ] 39 | p256k1 = ["eccoxide"] 40 | 41 | [[bench]] 42 | harness = false 43 | name = "vrf" 44 | required-features = ["with-bench"] 45 | 46 | [[bench]] 47 | harness = false 48 | name = "sumed25519" 49 | required-features = ["with-bench"] 50 | 51 | [[bench]] 52 | harness = false 53 | name = "ristretto" 54 | required-features = ["with-bench"] 55 | 56 | [[bench]] 57 | harness = false 58 | name = "p256k1" 59 | required-features = ["with-bench", "p256k1"] 60 | -------------------------------------------------------------------------------- /chain-crypto/benches/p256k1.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks curve p256k1 2 | 3 | use chain_crypto::ec::p256k1::{GroupElement, Scalar}; 4 | 5 | use criterion::{criterion_group, criterion_main, Criterion}; 6 | use rand_core::OsRng; 7 | 8 | fn mul(c: &mut Criterion) { 9 | let mut rng = OsRng; 10 | let g = GroupElement::from_hash(b"random element"); 11 | let scalar = Scalar::random(&mut rng); 12 | 13 | c.bench_function("Point - Scalar multiplication", |b| b.iter(|| &g * &scalar)); 14 | } 15 | 16 | fn addition(c: &mut Criterion) { 17 | let point1 = GroupElement::from_hash(b"random element"); 18 | let point2 = GroupElement::from_hash(b"random element"); 19 | 20 | c.bench_function("Group element addition", |b| b.iter(|| &point1 + &point2)); 21 | } 22 | 23 | fn from_hash(c: &mut Criterion) { 24 | let string = [0u8; 32]; 25 | 26 | c.bench_function("Group element from hash", |b| { 27 | b.iter(|| GroupElement::from_hash(&string)) 28 | }); 29 | } 30 | 31 | fn to_bytes(c: &mut Criterion) { 32 | let point = GroupElement::from_hash(b"random element"); 33 | 34 | c.bench_function("Group element compression", |b| b.iter(|| point.to_bytes())); 35 | } 36 | 37 | fn decompress(c: &mut Criterion) { 38 | let point = GroupElement::from_hash(b"random element"); 39 | let bytes = point.to_bytes(); 40 | 41 | c.bench_function("Group element decompression", |b| { 42 | b.iter(|| GroupElement::from_bytes(&bytes)) 43 | }); 44 | } 45 | 46 | fn scalar_multiplication(c: &mut Criterion) { 47 | let mut rng = OsRng; 48 | let scalar1 = Scalar::random(&mut rng); 49 | let scalar2 = Scalar::random(&mut rng); 50 | 51 | c.bench_function("Scalar multiplication", |b| b.iter(|| &scalar1 * &scalar2)); 52 | } 53 | 54 | fn scalar_power(c: &mut Criterion) { 55 | let mut rng = OsRng; 56 | let scalar = Scalar::random(&mut rng); 57 | 58 | c.bench_function("Scalar exponentiation", |b| { 59 | b.iter(|| scalar.power(191328475619823764usize)) 60 | }); 61 | } 62 | 63 | fn scalar_inversion(c: &mut Criterion) { 64 | let mut rng = OsRng; 65 | let scalar = Scalar::random(&mut rng); 66 | 67 | c.bench_function("Scalar inversion", |b| b.iter(|| scalar.inverse())); 68 | } 69 | 70 | fn random_scalar(c: &mut Criterion) { 71 | let mut rng = OsRng; 72 | c.bench_function("Random scalar", |b| b.iter(|| Scalar::random(&mut rng))); 73 | } 74 | 75 | criterion_group!( 76 | name = group_ops; 77 | config = Criterion::default(); 78 | targets = 79 | mul, 80 | from_hash, 81 | to_bytes, 82 | decompress, 83 | addition, 84 | scalar_multiplication, 85 | scalar_power, 86 | scalar_inversion, 87 | random_scalar 88 | ); 89 | criterion_main!(group_ops); 90 | -------------------------------------------------------------------------------- /chain-crypto/benches/ristretto.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks of the ristretto group over curve25519 2 | 3 | use chain_crypto::ec::ristretto255::{GroupElement, Scalar}; 4 | 5 | use criterion::{criterion_group, criterion_main, Criterion}; 6 | use rand_core::OsRng; 7 | 8 | fn mul(c: &mut Criterion) { 9 | let mut rng = OsRng; 10 | let g = GroupElement::from_hash(b"random element"); 11 | let scalar = Scalar::random(&mut rng); 12 | 13 | c.bench_function("Point - Scalar multiplication", |b| b.iter(|| &g * &scalar)); 14 | } 15 | 16 | fn addition(c: &mut Criterion) { 17 | let point1 = GroupElement::from_hash(b"random element"); 18 | let point2 = GroupElement::from_hash(b"random element"); 19 | 20 | c.bench_function("Group element addition", |b| b.iter(|| &point1 + &point2)); 21 | } 22 | 23 | fn from_hash(c: &mut Criterion) { 24 | let string = [0u8; 32]; 25 | 26 | c.bench_function("Group element from hash", |b| { 27 | b.iter(|| GroupElement::from_hash(&string)) 28 | }); 29 | } 30 | 31 | fn to_bytes(c: &mut Criterion) { 32 | let point = GroupElement::from_hash(b"random element"); 33 | 34 | c.bench_function("Group element compression", |b| b.iter(|| point.to_bytes())); 35 | } 36 | 37 | fn decompress(c: &mut Criterion) { 38 | let point = GroupElement::from_hash(b"random element"); 39 | let bytes = point.to_bytes(); 40 | 41 | c.bench_function("Group element decompression", |b| { 42 | b.iter(|| GroupElement::from_bytes(&bytes)) 43 | }); 44 | } 45 | 46 | fn scalar_multiplication(c: &mut Criterion) { 47 | let mut rng = OsRng; 48 | let scalar1 = Scalar::random(&mut rng); 49 | let scalar2 = Scalar::random(&mut rng); 50 | 51 | c.bench_function("Scalar multiplication", |b| b.iter(|| &scalar1 * &scalar2)); 52 | } 53 | 54 | fn scalar_power(c: &mut Criterion) { 55 | let mut rng = OsRng; 56 | let scalar = Scalar::random(&mut rng); 57 | 58 | c.bench_function("Scalar exponentiation", |b| { 59 | b.iter(|| scalar.power(191328475619823764usize)) 60 | }); 61 | } 62 | 63 | fn scalar_inversion(c: &mut Criterion) { 64 | let mut rng = OsRng; 65 | let scalar = Scalar::random(&mut rng); 66 | 67 | c.bench_function("Scalar inversion", |b| b.iter(|| scalar.inverse())); 68 | } 69 | 70 | fn random_scalar(c: &mut Criterion) { 71 | let mut rng = OsRng; 72 | c.bench_function("Random scalar", |b| b.iter(|| Scalar::random(&mut rng))); 73 | } 74 | 75 | criterion_group!( 76 | name = group_ops; 77 | config = Criterion::default(); 78 | targets = 79 | mul, 80 | from_hash, 81 | to_bytes, 82 | decompress, 83 | addition, 84 | scalar_multiplication, 85 | scalar_power, 86 | scalar_inversion, 87 | random_scalar 88 | ); 89 | criterion_main!(group_ops); 90 | -------------------------------------------------------------------------------- /chain-crypto/benches/vrf.rs: -------------------------------------------------------------------------------- 1 | use chain_crypto::algorithms::vrf::vrf::{PublicKey, SecretKey}; 2 | use criterion::{criterion_group, criterion_main, Criterion}; 3 | use rand_core::{OsRng, RngCore}; 4 | 5 | fn common() -> (OsRng, SecretKey, PublicKey, [u8; 10], [u8; 10]) { 6 | let mut csprng: OsRng = OsRng; 7 | let sk = SecretKey::random(&mut csprng); 8 | let pk = sk.public(); 9 | 10 | let sk_other = SecretKey::random(&mut csprng); 11 | let _pk_other = sk_other.public(); 12 | 13 | let mut b1 = [0u8; 10]; 14 | for i in b1.iter_mut() { 15 | *i = csprng.next_u32() as u8; 16 | } 17 | let mut b2 = [0u8; 10]; 18 | for i in b2.iter_mut() { 19 | *i = csprng.next_u32() as u8; 20 | } 21 | 22 | (csprng, sk, pk, b1, b2) 23 | } 24 | 25 | fn generate(c: &mut Criterion) { 26 | let (mut csprng, sk, _pk, b1, _) = common(); 27 | 28 | c.bench_function("generate", |b| { 29 | b.iter(|| { 30 | let _ = sk.evaluate(&mut csprng, &b1[..]); 31 | }) 32 | }); 33 | } 34 | 35 | fn verify_success(c: &mut Criterion) { 36 | let (mut csprng, sk, pk, b1, _) = common(); 37 | let po = sk.evaluate(&mut csprng, &b1[..]); 38 | 39 | c.bench_function("verify_success", |b| { 40 | b.iter(|| { 41 | let _ = po.verify(&pk, &b1[..]); 42 | }) 43 | }); 44 | } 45 | 46 | fn verify_fail(c: &mut Criterion) { 47 | let (mut csprng, sk, _pk, b1, _b2) = common(); 48 | let (_, _, pk2, _, _) = common(); 49 | let po = sk.evaluate(&mut csprng, &b1[..]); 50 | 51 | c.bench_function("verify_fail", |b| { 52 | b.iter(|| { 53 | let _ = po.verify(&pk2, &b1[..]); 54 | }) 55 | }); 56 | } 57 | 58 | criterion_group!(vrf, generate, verify_success, verify_fail); 59 | criterion_main!(vrf); 60 | -------------------------------------------------------------------------------- /chain-crypto/src/algorithms/mod.rs: -------------------------------------------------------------------------------- 1 | mod ed25519; 2 | mod ed25519_derive; 3 | mod ed25519_extended; 4 | pub mod vrf; 5 | pub mod zkps; 6 | 7 | #[cfg(not(feature = "with-bench"))] 8 | mod sumed25519; 9 | #[cfg(feature = "with-bench")] 10 | pub mod sumed25519; 11 | 12 | pub use ed25519::Ed25519; 13 | pub use ed25519_derive::Ed25519Bip32; 14 | pub use ed25519_extended::Ed25519Extended; 15 | pub use sumed25519::SumEd25519_12; 16 | pub use vrf::RistrettoGroup2HashDh; 17 | -------------------------------------------------------------------------------- /chain-crypto/src/algorithms/sumed25519/common.rs: -------------------------------------------------------------------------------- 1 | use ed25519_dalek as ed25519; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq)] 4 | pub struct Hash([u8; 32]); 5 | 6 | #[derive(Debug, Clone, PartialEq, Eq)] 7 | #[cfg_attr( 8 | any(test, feature = "property-test-api"), 9 | derive(test_strategy::Arbitrary) 10 | )] 11 | pub struct Seed([u8; 32]); 12 | 13 | impl AsRef<[u8]> for Seed { 14 | fn as_ref(&self) -> &[u8] { 15 | &self.0 16 | } 17 | } 18 | 19 | impl Seed { 20 | pub const SIZE: usize = 32; 21 | 22 | pub fn zero() -> Seed { 23 | Seed([0u8; Self::SIZE]) 24 | } 25 | 26 | pub fn set_zero(&mut self) { 27 | self.0.copy_from_slice(&[0u8; Self::SIZE]) 28 | } 29 | 30 | pub fn from_bytes(b: [u8; Self::SIZE]) -> Seed { 31 | Seed(b) 32 | } 33 | 34 | pub fn from_slice(b: &[u8]) -> Seed { 35 | assert_eq!(b.len(), Self::SIZE); 36 | let mut out = [0u8; Self::SIZE]; 37 | out.copy_from_slice(b); 38 | Seed(out) 39 | } 40 | } 41 | 42 | #[derive(Debug, Copy, Clone)] 43 | #[cfg_attr( 44 | any(test, feature = "property-test-api"), 45 | derive(test_strategy::Arbitrary) 46 | )] 47 | pub struct Depth( 48 | // a bigger range here would result into unreasobable testing times and 49 | // possible segfaults due to excessive allocations 50 | #[cfg_attr( 51 | any(test, feature = "property-test-api"), 52 | strategy(..10usize) 53 | )] 54 | pub usize, 55 | ); 56 | 57 | impl Depth { 58 | pub const fn total(self) -> usize { 59 | usize::pow(2, self.0 as u32) 60 | } 61 | 62 | pub fn half(self) -> usize { 63 | assert!(self.0 > 0); 64 | usize::pow(2, (self.0 - 1) as u32) 65 | } 66 | 67 | pub fn decr(self) -> Self { 68 | assert!(self.0 > 0); 69 | Depth(self.0 - 1) 70 | } 71 | pub fn incr(self) -> Self { 72 | Depth(self.0 + 1) 73 | } 74 | } 75 | 76 | pub fn split_seed(r: &Seed) -> (Seed, Seed) { 77 | use sha2::Digest; 78 | let mut hleft = sha2::Sha256::default(); 79 | let mut hright = sha2::Sha256::default(); 80 | 81 | hleft.update(&[1]); 82 | hleft.update(&r.0); 83 | 84 | hright.update(&[2]); 85 | hright.update(&r.0); 86 | 87 | let o1 = hleft.finalize(); 88 | let o2 = hright.finalize(); 89 | let s1 = Seed::from_slice(&o1); 90 | let s2 = Seed::from_slice(&o2); 91 | (s1, s2) 92 | } 93 | 94 | pub fn keygen_1(r: &Seed) -> ed25519::Keypair { 95 | let sk = ed25519::SecretKey::from_bytes(&r.0).unwrap(); 96 | let pk = (&sk).into(); 97 | ed25519::Keypair { 98 | secret: sk, 99 | public: pk, 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /chain-crypto/src/algorithms/zkps/challenge_context.rs: -------------------------------------------------------------------------------- 1 | use crate::ec::ristretto255::{GroupElement, Scalar}; 2 | use cryptoxide::blake2b::Blake2b; 3 | use cryptoxide::digest::Digest; 4 | 5 | /// Challenge context for Discrete Logarithm Equality proof. The common reference string 6 | /// are two EC bases, and the statement consists of two EC points. 7 | /// The challenge computation takes as input the two announcements 8 | /// computed in the sigma protocol, `a1` and `a2`, and the full 9 | /// statement. 10 | pub struct ChallengeContext(Blake2b); 11 | 12 | impl ChallengeContext { 13 | /// Initialise the challenge context, by including the common reference string and the full statement 14 | pub(crate) fn new( 15 | base_1: &GroupElement, 16 | base_2: &GroupElement, 17 | point_1: &GroupElement, 18 | point_2: &GroupElement, 19 | ) -> Self { 20 | let mut ctx = Blake2b::new(64); 21 | ctx.input(&base_1.to_bytes()); 22 | ctx.input(&base_2.to_bytes()); 23 | ctx.input(&point_1.to_bytes()); 24 | ctx.input(&point_2.to_bytes()); 25 | 26 | ChallengeContext(ctx) 27 | } 28 | 29 | /// Generation of the `first_challenge`. This challenge is generated after the `Announcement` is 30 | /// "sent". Hence, we include the latter to the challenge context and generate its 31 | /// corresponding scalar. 32 | pub(crate) fn first_challenge(&mut self, a1: &GroupElement, a2: &GroupElement) -> Scalar { 33 | self.0.input(&a1.to_bytes()); 34 | self.0.input(&a2.to_bytes()); 35 | 36 | Scalar::hash_to_scalar(&self.0) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chain-crypto/src/algorithms/zkps/mod.rs: -------------------------------------------------------------------------------- 1 | mod challenge_context; 2 | pub mod dleq; 3 | -------------------------------------------------------------------------------- /chain-crypto/src/ec/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module defining the Group Elements and Scalar structures in one primer order group (over sec2 2 | //! curves), or the other (ristretto255). 3 | #[macro_use] 4 | mod macros; 5 | #[cfg(feature = "p256k1")] 6 | pub mod p256k1; 7 | pub mod ristretto255; 8 | -------------------------------------------------------------------------------- /chain-crypto/src/evolving.rs: -------------------------------------------------------------------------------- 1 | use crate::key::{AsymmetricKey, SecretKey}; 2 | 3 | /// Evolving status 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 5 | pub enum EvolvingStatus { 6 | Success, 7 | Failed, 8 | } 9 | 10 | pub trait KeyEvolvingAlgorithm: AsymmetricKey { 11 | /// Get the period associated with this signature 12 | fn get_period(key: &Self::Secret) -> u32; 13 | 14 | /// Update the secret key to the next period 15 | /// 16 | /// if EvolvingStatus::Failed is returned, then the key couldn't be updated 17 | fn update(key: &mut Self::Secret) -> EvolvingStatus; 18 | } 19 | 20 | impl SecretKey { 21 | /// Evolve the secret key to the next period 22 | pub fn evolve(key: &mut Self) -> EvolvingStatus { 23 | A::update(&mut key.0) 24 | } 25 | /// Get the period associated with the current instance of the key 26 | pub fn get_period(key: &Self) -> u32 { 27 | A::get_period(&key.0) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chain-crypto/src/kes.rs: -------------------------------------------------------------------------------- 1 | use crate::sign::VerificationAlgorithm; 2 | 3 | pub trait KeyEvolvingSignatureAlgorithm: VerificationAlgorithm { 4 | /// Get the period associated with this signature 5 | fn get_period(sig: &Self::Signature) -> u32; 6 | } 7 | -------------------------------------------------------------------------------- /chain-crypto/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(any(test, feature = "property-test-api"))] 2 | pub mod testing; 3 | 4 | pub mod algorithms; 5 | pub mod asymlock; 6 | pub mod bech32; 7 | pub mod digest; 8 | mod evolving; 9 | #[macro_use] 10 | pub mod ec; 11 | pub mod hash; 12 | mod kes; 13 | mod key; 14 | pub mod multilock; 15 | mod sign; 16 | mod vrf; 17 | 18 | pub mod role; 19 | 20 | pub use evolving::{EvolvingStatus, KeyEvolvingAlgorithm}; 21 | pub use kes::KeyEvolvingSignatureAlgorithm; 22 | pub use key::{ 23 | AsymmetricKey, AsymmetricPublicKey, KeyPair, PublicKey, PublicKeyError, PublicKeyFromStrError, 24 | SecretKey, SecretKeyError, SecretKeySizeStatic, 25 | }; 26 | pub use sign::{ 27 | Signature, SignatureError, SignatureFromStrError, SigningAlgorithm, Verification, 28 | VerificationAlgorithm, 29 | }; 30 | pub use vrf::{ 31 | vrf_evaluate_and_prove, vrf_verified_get_output, vrf_verify, VerifiableRandomFunction, 32 | VrfVerification, 33 | }; 34 | 35 | pub use algorithms::*; 36 | pub use hash::Blake2b256; 37 | -------------------------------------------------------------------------------- /chain-crypto/src/role.rs: -------------------------------------------------------------------------------- 1 | use crate::key; 2 | use rand_core::{CryptoRng, RngCore}; 3 | use std::hash::Hash; 4 | use std::marker::PhantomData; 5 | 6 | pub struct SecretKey { 7 | inner: key::SecretKey, 8 | marker: PhantomData, 9 | } 10 | 11 | pub struct PublicKey { 12 | inner: key::PublicKey, 13 | marker: PhantomData, 14 | } 15 | 16 | impl SecretKey { 17 | pub fn role(sk: key::SecretKey) -> Self { 18 | SecretKey { 19 | inner: sk, 20 | marker: PhantomData, 21 | } 22 | } 23 | 24 | pub fn unrole(self) -> key::SecretKey { 25 | self.inner 26 | } 27 | 28 | pub fn generate(rng: T) -> Self { 29 | Self::role(key::SecretKey::generate(rng)) 30 | } 31 | 32 | pub fn to_public(&self) -> PublicKey { 33 | PublicKey::role(self.inner.to_public()) 34 | } 35 | } 36 | 37 | impl PublicKey { 38 | pub fn role(pk: key::PublicKey) -> Self { 39 | PublicKey { 40 | inner: pk, 41 | marker: PhantomData, 42 | } 43 | } 44 | 45 | pub fn unrole(self) -> key::PublicKey { 46 | self.inner 47 | } 48 | } 49 | 50 | impl Clone for SecretKey { 51 | fn clone(&self) -> Self { 52 | SecretKey { 53 | inner: self.inner.clone(), 54 | marker: self.marker, 55 | } 56 | } 57 | } 58 | 59 | impl Clone for PublicKey { 60 | fn clone(&self) -> Self { 61 | PublicKey { 62 | inner: self.inner.clone(), 63 | marker: self.marker, 64 | } 65 | } 66 | } 67 | 68 | impl std::cmp::PartialEq for PublicKey { 69 | fn eq(&self, other: &Self) -> bool { 70 | self.inner.eq(&other.inner) 71 | } 72 | } 73 | 74 | impl std::cmp::Eq for PublicKey {} 75 | 76 | impl std::cmp::PartialOrd for PublicKey { 77 | fn partial_cmp(&self, other: &Self) -> Option { 78 | self.inner.partial_cmp(&other.inner) 79 | } 80 | } 81 | 82 | impl std::cmp::Ord for PublicKey { 83 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 84 | self.inner.cmp(&other.inner) 85 | } 86 | } 87 | 88 | impl Hash for PublicKey { 89 | fn hash(&self, state: &mut H) 90 | where 91 | H: std::hash::Hasher, 92 | { 93 | self.inner.hash(state) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /chain-crypto/src/vrf.rs: -------------------------------------------------------------------------------- 1 | use crate::key; 2 | use rand_core::{CryptoRng, RngCore}; 3 | 4 | #[derive(Debug, Clone, PartialEq, Eq)] 5 | pub enum VrfVerification { 6 | Success, 7 | Failed, 8 | } 9 | 10 | pub trait VerifiableRandomFunction: key::AsymmetricPublicKey + key::AsymmetricKey { 11 | type VerifiedRandomOutput; 12 | type RandomOutput; 13 | type Input: ?Sized; 14 | 15 | const VERIFIED_RANDOM_SIZE: usize; 16 | 17 | fn evaluate_and_prove( 18 | secret: &Self::Secret, 19 | input: &Self::Input, 20 | rng: T, 21 | ) -> Self::VerifiedRandomOutput; 22 | 23 | fn verify( 24 | public: &Self::Public, 25 | input: &Self::Input, 26 | vrand: &Self::VerifiedRandomOutput, 27 | ) -> VrfVerification; 28 | 29 | fn strip_verification_output(vr: &Self::VerifiedRandomOutput) -> Self::RandomOutput; 30 | } 31 | 32 | /// Evaluate the VRF for a specific input and return a verified output 33 | pub fn vrf_evaluate_and_prove( 34 | secret: &key::SecretKey, 35 | input: &::Input, 36 | rng: T, 37 | ) -> ::VerifiedRandomOutput { 38 | VRF::evaluate_and_prove(&secret.0, input, rng) 39 | } 40 | 41 | /// Verify the VRF output for a specific input is correct 42 | pub fn vrf_verify( 43 | public: &key::PublicKey, 44 | input: &::Input, 45 | vrand: &::VerifiedRandomOutput, 46 | ) -> VrfVerification { 47 | VRF::verify(&public.0, input, vrand) 48 | } 49 | 50 | pub fn vrf_verified_get_output( 51 | vr: &::VerifiedRandomOutput, 52 | ) -> ::RandomOutput { 53 | VRF::strip_verification_output(vr) 54 | } 55 | -------------------------------------------------------------------------------- /chain-evm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-evm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | imhamt = { path = "../imhamt" } 8 | chain-ser = { path = "../chain-ser" } 9 | chain-core = { path = "../chain-core" } 10 | typed-bytes = { path = "../typed-bytes" } 11 | base64 = { version = "0.13.0", default-features = false, features = ["alloc"] } 12 | blake2 = { version = "0.9.1", git = "https://github.com/near/near-blake2.git", rev = "736ff607cc8160af87ffa697c14ebef85050138f", default-features = false } 13 | bn = { package = "aurora-bn", version = "0.1.0", git = "https://github.com/aurora-is-near/aurora-bn.git", default-features = false, features = ["std"] } 14 | libsecp256k1 = { version = "0.7.0", default-features = false, features = ["static-context"] } 15 | secp256k1 = { version = "0.23.0", features = ["global-context", "rand-std", "recovery"] } 16 | num = { version = "0.4.0", default-features = false, features = ["alloc"] } 17 | ripemd = { version = "0.1", default-features = false } 18 | rlp = { version = "0.5.1", default-features = false } 19 | sha2 = { version = "0.10.1", default-features = false } 20 | sha3 = { version = "0.10.0", default-features = false } 21 | ethabi = { version = "17.0", default-features = false } 22 | hex = { version = "0.4", default-features = false, features = ["alloc"] } 23 | byte-slice-cast = { version = "1.0", default-features = false } 24 | thiserror = "1.0" 25 | quickcheck = { version = "0.9", optional = true } 26 | evm = { version = "0.35.0" } 27 | ethereum = { version = "0.12.0", features = ["with-serde"] } 28 | ethereum-types = { version = "0.13.1", features = ["rlp"] } 29 | 30 | [dev-dependencies] 31 | rand = "0.7.3" 32 | proptest = "1.0.0" 33 | test-strategy = "0.1" 34 | quickcheck = "0.9" 35 | evm-test-suite = { git = "https://github.com/input-output-hk/evm-test-suite.git"} 36 | 37 | [features] 38 | property-test-api = ["quickcheck"] 39 | -------------------------------------------------------------------------------- /chain-evm/README.md: -------------------------------------------------------------------------------- 1 | ## Legal notes 2 | 3 | The code under `./src/precompiles` was originally copied from [Aurora Project][ap] repository which 4 | was licensed under CC0-1.0 at the time of copying and thus did not require any copyright notice. 5 | 6 | [ap]: https://github.com/aurora-is-near/aurora-engine 7 | -------------------------------------------------------------------------------- /chain-evm/src/crypto.rs: -------------------------------------------------------------------------------- 1 | //! Cryptography for Ethereum types. 2 | pub mod secp256k1 { 3 | //! Re-export of types for constructing Ethereum signatures. 4 | pub use secp256k1::{ 5 | ecdsa::{RecoverableSignature, RecoveryId}, 6 | Error, Message, 7 | }; 8 | } 9 | 10 | pub mod sha3 { 11 | //! Re-export of types and traits for constructing Ethereum hashes used in signatures. 12 | pub use sha3::{Digest, Keccak256}; 13 | } 14 | -------------------------------------------------------------------------------- /chain-evm/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use ethereum; 2 | pub use ethereum_types; 3 | pub use rlp; 4 | 5 | pub mod crypto; 6 | pub mod machine; 7 | mod precompiles; 8 | pub mod signature; 9 | pub mod state; 10 | pub mod transaction; 11 | pub mod util; 12 | 13 | #[cfg(test)] 14 | mod tests; 15 | 16 | pub use machine::{AccessList, Address, BlockGasLimit, Config, Environment, GasLimit, GasPrice}; 17 | -------------------------------------------------------------------------------- /chain-evm/src/precompiles/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::Address; 2 | pub use ethereum_types::{H160, H256, U256}; 3 | pub use std::collections::BTreeMap; 4 | pub use std::marker::PhantomData; 5 | pub use std::mem; 6 | pub use std::string::{String, ToString}; 7 | pub use std::vec; 8 | pub use std::vec::Vec; 9 | -------------------------------------------------------------------------------- /chain-evm/src/precompiles/utils.rs: -------------------------------------------------------------------------------- 1 | use evm::Context; 2 | 3 | pub fn new_context() -> Context { 4 | Context { 5 | address: Default::default(), 6 | caller: Default::default(), 7 | apparent_value: Default::default(), 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chain-evm/src/signature.rs: -------------------------------------------------------------------------------- 1 | //! Ethereum signatures for EIP-191, Legacy Transactions (EIP-155), EIP-2930, and EIP-1559. 2 | use crate::util::Secret; 3 | use ethereum::{LegacyTransactionMessage, TransactionSignature}; 4 | use ethereum_types::H256; 5 | use secp256k1::ecdsa::RecoverableSignature; 6 | 7 | /// Byte size for 'r' and 's' components of a signature. 8 | const SIGNATURE_BYTES: usize = 32; 9 | 10 | /// Legacy transaction signature, as specified in [EIP-155](https://eips.ethereum.org/EIPS/eip-155). 11 | pub fn sign_eip_155( 12 | tx: &LegacyTransactionMessage, 13 | secret: &Secret, 14 | ) -> Result { 15 | let sig = super::util::sign_data_hash(&tx.hash(), secret)?; 16 | let (recovery_id, sig_bytes) = sig.serialize_compact(); 17 | let v = if let Some(chain_id) = tx.chain_id { 18 | recovery_id.to_i32() as u64 + chain_id * 2 + 35 19 | } else { 20 | recovery_id.to_i32() as u64 + 27 21 | }; 22 | let (r, s) = sig_bytes.split_at(SIGNATURE_BYTES); 23 | TransactionSignature::new(v, H256::from_slice(r), H256::from_slice(s)) 24 | .ok_or(secp256k1::Error::InvalidSignature) 25 | } 26 | 27 | /// Type 1 transaction signature, as specified in [EIP-2930](https://eips.ethereum.org/EIPS/eip-2930). 28 | pub fn eip_2930_signature( 29 | tx_hash: &H256, 30 | secret: &Secret, 31 | ) -> Result { 32 | let sig = super::util::sign_data_hash(tx_hash, secret)?; 33 | let (recovery_id, sig_bytes) = sig.serialize_compact(); 34 | let (r, s) = sig_bytes.split_at(SIGNATURE_BYTES); 35 | TransactionSignature::new( 36 | recovery_id.to_i32() as u64 % 2, 37 | H256::from_slice(r), 38 | H256::from_slice(s), 39 | ) 40 | .ok_or(secp256k1::Error::InvalidSignature) 41 | } 42 | 43 | /// Type 2 transaction signature, as specified in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559). 44 | pub fn eip_1559_signature( 45 | tx_hash: &H256, 46 | secret: &Secret, 47 | ) -> Result { 48 | eip_2930_signature(tx_hash, secret) 49 | } 50 | 51 | /// Signature for hex-encoded strings, as specified in [EIP-191](https://eips.ethereum.org/EIPS/eip-191). 52 | pub fn eip_191_signature>( 53 | data: T, 54 | secret: &Secret, 55 | ) -> Result { 56 | // first we check if the message is a valid hex-encoded message 57 | let msg = hex::decode(data).map_err(|_| secp256k1::Error::InvalidSignature)?; 58 | let msg_for_hash = format!("\x19Ethereum Signed Message:\n{}{:?}", msg.len(), msg); 59 | super::util::sign_data(msg_for_hash.as_bytes(), secret) 60 | } 61 | -------------------------------------------------------------------------------- /chain-evm/src/state/logs.rs: -------------------------------------------------------------------------------- 1 | use evm::backend::Log; 2 | 3 | use crate::machine::BlockHash; 4 | 5 | use super::Trie; 6 | 7 | /// In-memory representation of all logs. 8 | pub type BlockLogsTrie = Trie>; 9 | 10 | #[derive(Default, Debug, Clone, PartialEq, Eq)] 11 | pub struct LogsState { 12 | block_logs: BlockLogsTrie, 13 | } 14 | 15 | impl LogsState { 16 | pub fn put(&mut self, block_hash: BlockHash, logs: Vec) { 17 | self.block_logs = self.block_logs.clone().put(block_hash, logs); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chain-evm/src/state/mod.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | This module contains a simple representation of all EVM-related data based on 3 | immutable data structures. It is designed to be used with the ledger 4 | implementation from `chain-impl-mockchain` in Jormungandr multiverse of ledgers. 5 | */ 6 | 7 | mod account; 8 | mod logs; 9 | mod storage; 10 | mod trie; 11 | 12 | pub use account::{Account, AccountState, AccountTrie, Balance, ByteCode}; 13 | use evm::ExitError; 14 | pub use logs::LogsState; 15 | use std::borrow::Cow; 16 | pub use storage::{Key, Storage, Value}; 17 | pub use trie::Trie; 18 | 19 | /// Definition for state-related errors. 20 | #[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] 21 | pub enum Error { 22 | #[error("EVM values cannot exceed 64 significant bits")] 23 | ValueOverflow, 24 | } 25 | 26 | impl From for crate::machine::Error { 27 | fn from(other: Error) -> Self { 28 | Self::TransactionError(ExitError::Other(Cow::from(String::from(other)))) 29 | } 30 | } 31 | 32 | impl From for String { 33 | fn from(other: Error) -> Self { 34 | format!("{:?}", other) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /chain-evm/src/state/storage.rs: -------------------------------------------------------------------------------- 1 | use crate::state::trie::Trie; 2 | 3 | use ethereum_types::H256; 4 | 5 | /// Representation of a storage key. Fixed-size uninterpreted hash type with 32 bytes (256 bits) size. 6 | pub type Key = H256; 7 | /// Representation of a storage key. Fixed-size uninterpreted hash type with 32 bytes (256 bits) size. 8 | pub type Value = H256; 9 | /// In-memory representation of account storage. 10 | pub type Storage = Trie; 11 | -------------------------------------------------------------------------------- /chain-impl-mockchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-impl-mockchain" 3 | version = "0.1.0" 4 | authors = ["dev@iohk.io"] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [dependencies] 9 | chain-core = { path = "../chain-core" } 10 | chain-addr = { path = "../chain-addr" } 11 | chain-crypto = { path = "../chain-crypto" } 12 | chain-ser = { path = "../chain-ser" } 13 | chain-time = { path = "../chain-time" } 14 | chain-vote = { path = "../chain-vote" } 15 | chain-evm = { path = "../chain-evm", optional = true } 16 | typed-bytes = { path = "../typed-bytes" } 17 | rand_core = "0.6" 18 | imhamt = { path = "../imhamt" } 19 | sparse-array = { path = "../sparse-array" } 20 | strum = "0.23.0" 21 | strum_macros = "0.23.1" 22 | hex = { version = "0.4.2", default-features = false, features = [ "std" ] } 23 | quickcheck = { version = "0.9", optional = true } 24 | quickcheck_macros = { version = "0.9", optional = true } 25 | proptest = { git = "https://github.com/input-output-hk/proptest.git", optional = true } 26 | test-strategy = { version = "0.1", optional = true } 27 | ed25519-bip32 = { version = "0.4.1", optional = true } 28 | thiserror = "1.0" 29 | lazy_static = { version = "1.3.0", optional = true } 30 | cardano-legacy-address = { path= "../cardano-legacy-address" } 31 | rand_chacha = { version = "0.3", optional = true } 32 | criterion = { version = "0.3.0", optional = true } 33 | rand = "0.8" 34 | cryptoxide = "0.4" 35 | 36 | [features] 37 | property-test-api = [ 38 | "chain-crypto/property-test-api", 39 | "chain-time/property-test-api", 40 | "chain-addr/property-test-api", 41 | "chain-evm/property-test-api", 42 | "imhamt/property-test-api", 43 | "quickcheck", 44 | "quickcheck_macros", 45 | "proptest", 46 | "test-strategy", 47 | "lazy_static", 48 | "rand_chacha", 49 | "ed25519-bip32"] 50 | with-bench = ["criterion","property-test-api"] 51 | evm = ["chain-evm", "proptest/evm"] 52 | 53 | [dev-dependencies] 54 | quickcheck = "0.9" 55 | quickcheck_macros = "0.9" 56 | proptest = { git = "https://github.com/input-output-hk/proptest.git" } 57 | test-strategy = "0.1" 58 | chain-core = { path = "../chain-core"} 59 | chain-crypto = { path = "../chain-crypto", features=["property-test-api"]} 60 | chain-time = { path = "../chain-time", features=["property-test-api"]} 61 | chain-addr = { path = "../chain-addr", features=["property-test-api"]} 62 | chain-evm = { path = "../chain-evm", features=["property-test-api"]} 63 | ed25519-bip32 = "0.4.1" 64 | rand_chacha = "0.3" 65 | lazy_static = "1.3.0" 66 | tempfile = "3.1.0" 67 | serde = { version = "1.0", features = ["derive"] } 68 | serde_json = { version = "1.0.74" } 69 | rayon = "1.5.0" 70 | 71 | [[bench]] 72 | harness = false 73 | name = "tally" 74 | required-features = ["with-bench"] 75 | -------------------------------------------------------------------------------- /chain-impl-mockchain/README.md: -------------------------------------------------------------------------------- 1 | # Mock implementation of the chain. 2 | 3 | It defines mock implementation of the chain and can be used 4 | for testing blockchain algorithms and work as an example of 5 | the implementation. 6 | 7 | # Testing 8 | 9 | Tests are covering internals of ledger library, validating particular modules. 10 | There are also scenarios for testing ledger in a 'turn-based' aproach. For example: 11 | 12 | ``` 13 | /// Prepare blockchain settings and actors 14 | let (mut ledger, controller) = prepare_scenario() 15 | .with_config( 16 | ConfigBuilder::new(0) 17 | .with_discrimination(Discrimination::Test) 18 | .with_fee(LinearFee::new(1, 1, 1)), 19 | ) 20 | .with_initials(vec![ 21 | wallet("Alice").with(1_000).owns("alice_stake_pool"), 22 | wallet("Bob").with(1_000).owns("bob_stake_pool"), 23 | wallet("Clarice").with(1_000).owns("clarice_stake_pool"), 24 | wallet("David").with(1_003), 25 | ]) 26 | .build() 27 | .unwrap(); 28 | 29 | /// Retrieve actors 30 | let alice_stake_pool = controller.stake_pool("alice_stake_pool").unwrap(); 31 | let bob_stake_pool = controller.stake_pool("bob_stake_pool").unwrap(); 32 | let clarice_stake_pool = controller.stake_pool("clarice_stake_pool").unwrap(); 33 | 34 | let david = controller.wallet("David").unwrap(); 35 | 36 | // prepare delegation ratio 37 | let delegation_ratio = vec![ 38 | (&alice_stake_pool, 2u8), 39 | (&bob_stake_pool, 3u8), 40 | (&clarice_stake_pool, 5u8), 41 | ]; 42 | 43 | /// post delegation certificates 44 | controller 45 | .delegates_to_many(&david, &delegation_ratio, &mut ledger) 46 | .unwrap(); 47 | 48 | /// verify distribution is correct 49 | let expected_distribution = vec![ 50 | (alice_stake_pool.id(), Value(200)), 51 | (bob_stake_pool.id(), Value(300)), 52 | (clarice_stake_pool.id(), Value(500)), 53 | ]; 54 | 55 | LedgerStateVerifier::new(ledger.clone().into()) 56 | .info("after delegation to many stake pools") 57 | .distribution() 58 | .pools_distribution_is(expected_distribution); 59 | 60 | ``` 61 | 62 | 63 | ### How to run tests 64 | ``` 65 | cd chain-impl-mockchain 66 | cargo test 67 | ``` 68 | -------------------------------------------------------------------------------- /chain-impl-mockchain/doc/overview.md: -------------------------------------------------------------------------------- 1 | ## Block 2 | 3 | The Block is a Header followed by its associated Content. 4 | 5 | ``` 6 | +-----------+ 7 | | Header | 8 | +-----------+ 9 | | Contents | 10 | +-----------+ 11 | ``` 12 | 13 | ## Header 14 | 15 | The header contains blockchain organisation metadata (chain length, date, etc), 16 | along with all the per-consensus metadata (VRF, KES, Signature). 17 | 18 | It is refered using a cryptography unique identifier called the HeaderId, which 19 | is computed on the whole serialized header **minus** its leading size. 20 | 21 | The block **chain** component is handled by the parent-hash, which points 22 | through the HeaderId to a unique header. 23 | 24 | ``` 25 | HeaderId A <- HeaderId B 26 | +------------------+ \ +---------------+ 27 | | depth=0 | \ | depth=1 | 28 | |--| parent-hash=0*32 | \-----| parent-hash=A | 29 | | ... | | ... | 30 | +------------------+ +---------------+ 31 | ``` 32 | 33 | The header also uniquely point to some content by means of a ContentId. 34 | 35 | The header with depth=0, defines the "genesis block" 36 | 37 | ## Content 38 | 39 | Content is composed of zero to many fragments appended one after another 40 | 41 | ContentId is computed as the cryptographic hash of the whole content 42 | 43 | ``` 44 | 45 | +---------------+ 46 | | Fragment 1 | 47 | +---------------+ 48 | | .... | 49 | +---------------+ 50 | | Fragment N | 51 | +---------------+ 52 | 53 | ``` 54 | 55 | ## Fragment 56 | 57 | Fragments are specific content that act on the state of the overall chain ledger. 58 | 59 | Current fragments defined are : 60 | 61 | * INITIAL: only found in genesis block, define the parameter of the blockchain 62 | * OLD-UTXO-DECL: only found in genesis block, declares a set of old addresses with certain value, existing on this chain. 63 | * SIMPLE-TRANSACTION: a spending transaction from some inputs to some outputs. 64 | * OWNER-STAKE-DELEGATION: Establish the delegation setting of an account, where the fees are paid by the account. 65 | * STAKE-DELEGATION: Establish the delegation settings of an account but fees are handled by a 3rd party. 66 | * POOL-REGISTRATION: Register a new pool 67 | * POOL-RETIREMENT: Retire a pool 68 | * POOL-UPDATE: Update parameters of a pool 69 | * UPDATE-PROPOSAL 70 | * UPDATE-VOTE 71 | 72 | The most common fragments are the transaction, and owner-stake-delegation. 73 | -------------------------------------------------------------------------------- /chain-impl-mockchain/doc/update.md: -------------------------------------------------------------------------------- 1 | # Update semantics 2 | 3 | Currently, the update semantics are very simple: 4 | 5 | * Update proposal messages can only be made by BFT leaders: they must 6 | be signed by the secret key of a BFT leader. 7 | 8 | * Only BFT leaders can vote for an update proposal: update vote 9 | messages must be signed by the secret key of a BFT leader. 10 | 11 | * Votes are always positive. It is not possible (or necessary) to 12 | register a vote against a proposal. 13 | 14 | * A proposal is accepted if an absolute majority of BFT leaders has 15 | cast a vote for the proposal. (Note that the update proposal message 16 | itself is *not* an implied vote for the proposal by the issuing BFT 17 | leader; that leader needs to separately vote for its own proposal.) 18 | Thus, with 7 BFT leaders, a proposal is accepted once 4 valid votes 19 | are on the chain. 20 | 21 | * An accepted proposal becomes active at the start of the epoch 22 | following the slot containg the deciding vote. 23 | 24 | TBD: what to do if multiple versions are accepted at the same time? 25 | 26 | TODO: add a way to delay activation of accepted proposals by a number 27 | of epochs. This number could be a setting, or a part of the proposal 28 | (e.g. "this proposal takes effect N epochs after acceptance"). 29 | 30 | TODO: maybe we can use the update proposal ID as the version? That's a 31 | lot more unique than integers. Also, update proposals can then have a 32 | parent version, so you get a chain of versions. Downside: it makes the 33 | block header a bit bigger. 34 | 35 | TODO: need a way to distinguish between setting changes (which don't 36 | require a software update) and other changes (which do). Currently 37 | UpdateProposal can only do the former. 38 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/account.rs: -------------------------------------------------------------------------------- 1 | use crate::accounting::account; 2 | use crate::key::{deserialize_public_key, serialize_public_key}; 3 | use crate::transaction::WitnessAccountData; 4 | use chain_core::property::WriteError; 5 | use chain_core::{ 6 | packer::Codec, 7 | property::{DeserializeFromSlice, ReadError, Serialize}, 8 | }; 9 | use chain_crypto::{Ed25519, PublicKey, Signature}; 10 | 11 | pub use account::{DelegationRatio, DelegationType, LedgerError, SpendingCounter}; 12 | 13 | pub type AccountAlg = Ed25519; 14 | 15 | pub type Witness = Signature; 16 | 17 | /// Account Identifier (also used as Public Key) 18 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 19 | #[cfg_attr( 20 | any(test, feature = "property-test-api"), 21 | derive(test_strategy::Arbitrary) 22 | )] 23 | pub struct Identifier(PublicKey); 24 | 25 | impl From> for Identifier { 26 | fn from(pk: PublicKey) -> Self { 27 | Identifier(pk) 28 | } 29 | } 30 | 31 | impl From for PublicKey { 32 | fn from(i: Identifier) -> Self { 33 | i.0 34 | } 35 | } 36 | 37 | impl AsRef> for Identifier { 38 | fn as_ref(&self) -> &PublicKey { 39 | &self.0 40 | } 41 | } 42 | 43 | impl Serialize for Identifier { 44 | fn serialized_size(&self) -> usize { 45 | self.0.as_ref().len() 46 | } 47 | 48 | fn serialize(&self, codec: &mut Codec) -> Result<(), WriteError> { 49 | serialize_public_key(&self.0, codec) 50 | } 51 | } 52 | 53 | impl DeserializeFromSlice for Identifier { 54 | fn deserialize_from_slice(codec: &mut Codec<&[u8]>) -> Result { 55 | deserialize_public_key(codec).map(Identifier) 56 | } 57 | } 58 | 59 | /// The public ledger of all accounts associated with their current state 60 | pub type Ledger = account::Ledger; 61 | 62 | impl std::fmt::Display for Identifier { 63 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 64 | self.0.fmt(f) 65 | } 66 | } 67 | 68 | #[cfg(any(test, feature = "property-test-api"))] 69 | mod test { 70 | use super::*; 71 | #[cfg(test)] 72 | use crate::testing::serialization::serialization_bijection; 73 | use chain_crypto::{Ed25519, KeyPair}; 74 | #[cfg(test)] 75 | use quickcheck::TestResult; 76 | use quickcheck::{Arbitrary, Gen}; 77 | 78 | impl Arbitrary for Identifier { 79 | fn arbitrary(g: &mut G) -> Self { 80 | let kp: KeyPair = Arbitrary::arbitrary(g); 81 | Identifier::from(kp.into_keys().1) 82 | } 83 | } 84 | 85 | quickcheck! { 86 | fn identifier_serialization_bijection(id: Identifier) -> TestResult { 87 | serialization_bijection(id) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/accounting/account/test.rs: -------------------------------------------------------------------------------- 1 | #[warn(unused_imports)] 2 | use super::{ 3 | AccountState, DelegationType, LastRewards, SpendingCounter, SpendingCounterIncreasing, 4 | }; 5 | use imhamt::Hamt; 6 | use quickcheck::{Arbitrary, Gen}; 7 | 8 | impl Arbitrary for SpendingCounter { 9 | fn arbitrary(gen: &mut G) -> Self { 10 | u32::arbitrary(gen).into() 11 | } 12 | } 13 | 14 | impl Arbitrary for SpendingCounterIncreasing { 15 | fn arbitrary(gen: &mut G) -> Self { 16 | SpendingCounterIncreasing::new_from_counter(SpendingCounter::arbitrary(gen)) 17 | } 18 | } 19 | 20 | impl Arbitrary for AccountState<()> { 21 | fn arbitrary(gen: &mut G) -> Self { 22 | AccountState { 23 | spending: Arbitrary::arbitrary(gen), 24 | delegation: DelegationType::Full(Arbitrary::arbitrary(gen)), 25 | value: Arbitrary::arbitrary(gen), 26 | tokens: Hamt::new(), 27 | last_rewards: LastRewards::default(), 28 | #[cfg(feature = "evm")] 29 | evm_state: chain_evm::state::AccountState::default(), 30 | extra: (), 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/accounting/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod account; 2 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/block/builder.rs: -------------------------------------------------------------------------------- 1 | use super::Block; 2 | use crate::fragment::Contents; 3 | use crate::header::{BlockVersion, Header, HeaderBuilderNew}; 4 | 5 | /// Create a block from a block version, content and a header builder closure 6 | /// 7 | /// If the header builder returns an error, it is returns as is 8 | pub fn builder(version: BlockVersion, contents: Contents, hdr_builder: F) -> Result 9 | where 10 | F: FnOnce(HeaderBuilderNew) -> Result, 11 | { 12 | hdr_builder(HeaderBuilderNew::new(version, &contents)) 13 | .map(|header| Block::new_unchecked(header, contents)) 14 | } 15 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/block/header.rs: -------------------------------------------------------------------------------- 1 | use crate::date::BlockDate; 2 | use crate::header::{BlockVersion, ChainLength, Header, HeaderId}; 3 | use chain_core::property; 4 | 5 | impl property::ChainLength for ChainLength { 6 | fn next(&self) -> Self { 7 | self.increase() 8 | } 9 | } 10 | 11 | impl property::Header for Header { 12 | type Id = HeaderId; 13 | type Date = BlockDate; 14 | type Version = BlockVersion; 15 | type ChainLength = ChainLength; 16 | 17 | fn id(&self) -> Self::Id { 18 | self.hash() 19 | } 20 | fn parent_id(&self) -> Self::Id { 21 | self.block_parent_hash() 22 | } 23 | fn chain_length(&self) -> Self::ChainLength { 24 | self.chain_length() 25 | } 26 | fn date(&self) -> Self::Date { 27 | self.block_date() 28 | } 29 | fn version(&self) -> Self::Version { 30 | self.block_version() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/chaintypes.rs: -------------------------------------------------------------------------------- 1 | //! Shared Types between ledger and chain 2 | //! 3 | //! Common types that are found in the header but are used or stored in the 4 | //! ledger for either validation or some state tracking 5 | 6 | use crate::key::Hash; 7 | use strum_macros::{Display, EnumString, IntoStaticStr}; 8 | 9 | pub type HeaderId = Hash; // TODO: change to DigestOf 10 | 11 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 12 | pub struct ChainLength(pub(crate) u32); 13 | 14 | impl From for ChainLength { 15 | fn from(n: u32) -> ChainLength { 16 | ChainLength(n) 17 | } 18 | } 19 | 20 | impl From for u32 { 21 | fn from(chain_length: ChainLength) -> u32 { 22 | chain_length.0 23 | } 24 | } 25 | 26 | impl ChainLength { 27 | #[inline] 28 | #[must_use = "this returns the result of the operation, without modifying the original"] 29 | pub fn increase(self) -> Self { 30 | ChainLength(self.0.checked_add(1).unwrap()) 31 | } 32 | 33 | #[inline] 34 | #[must_use = "this returns the result of the operation, without modifying the original"] 35 | pub fn nth_ancestor(self, depth: u32) -> Option { 36 | self.0.checked_sub(depth).map(ChainLength) 37 | } 38 | } 39 | 40 | impl std::fmt::Display for ChainLength { 41 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 42 | write!(f, "{}", self.0) 43 | } 44 | } 45 | 46 | #[derive( 47 | Debug, Clone, Copy, Display, EnumString, IntoStaticStr, PartialEq, Eq, PartialOrd, Ord, Hash, 48 | )] 49 | pub enum ConsensusType { 50 | #[strum(to_string = "bft")] 51 | Bft = 1, 52 | #[strum(to_string = "genesis")] 53 | GenesisPraos = 2, 54 | } 55 | 56 | // alias for old name 57 | pub type ConsensusVersion = ConsensusType; 58 | 59 | impl ConsensusType { 60 | pub fn from_u16(v: u16) -> Option { 61 | match v { 62 | 1 => Some(ConsensusType::Bft), 63 | 2 => Some(ConsensusType::GenesisPraos), 64 | _ => None, 65 | } 66 | } 67 | } 68 | 69 | #[cfg(any(test, feature = "property-test-api"))] 70 | mod tests { 71 | use super::*; 72 | use quickcheck::{Arbitrary, Gen}; 73 | 74 | impl Arbitrary for ConsensusType { 75 | fn arbitrary(g: &mut G) -> Self { 76 | ConsensusType::from_u16(u16::arbitrary(g) % 2 + 1).unwrap() 77 | } 78 | } 79 | 80 | impl Arbitrary for ChainLength { 81 | fn arbitrary(g: &mut G) -> Self { 82 | ChainLength(Arbitrary::arbitrary(g)) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/fragment/config.rs: -------------------------------------------------------------------------------- 1 | use crate::config::ConfigParam; 2 | use chain_core::{ 3 | packer::Codec, 4 | property::{DeserializeFromSlice, ReadError, Serialize, WriteError}, 5 | }; 6 | 7 | #[derive(Debug, Clone, PartialEq, Eq, Default)] 8 | pub struct ConfigParams(pub(crate) Vec); 9 | 10 | impl ConfigParams { 11 | pub fn new() -> Self { 12 | ConfigParams(Vec::new()) 13 | } 14 | 15 | pub fn push(&mut self, config: ConfigParam) { 16 | self.0.push(config) 17 | } 18 | 19 | pub fn iter(&self) -> std::slice::Iter { 20 | self.0.iter() 21 | } 22 | } 23 | 24 | impl Serialize for ConfigParams { 25 | fn serialized_size(&self) -> usize { 26 | let mut res = Codec::u16_size(); 27 | for config in &self.0 { 28 | res += config.serialized_size(); 29 | } 30 | res 31 | } 32 | 33 | fn serialize(&self, codec: &mut Codec) -> Result<(), WriteError> { 34 | // FIXME: put params in canonical order (e.g. sorted by tag)? 35 | codec.put_be_u16(self.0.len() as u16)?; 36 | for config in &self.0 { 37 | config.serialize(codec)? 38 | } 39 | Ok(()) 40 | } 41 | } 42 | 43 | impl DeserializeFromSlice for ConfigParams { 44 | fn deserialize_from_slice(codec: &mut Codec<&[u8]>) -> Result { 45 | // FIXME: check canonical order? 46 | let len = codec.get_be_u16()?; 47 | let mut configs: Vec = Vec::with_capacity(len as usize); 48 | for _ in 0..len { 49 | configs.push(ConfigParam::deserialize_from_slice(codec)?); 50 | } 51 | Ok(ConfigParams(configs)) 52 | } 53 | } 54 | 55 | #[cfg(test)] 56 | mod tests { 57 | use super::*; 58 | 59 | quickcheck! { 60 | fn config_params_serialize(params: ConfigParams) -> bool { 61 | use chain_core::property::{Serialize as _,}; 62 | let bytes = params.serialize_as_vec().unwrap(); 63 | let decoded = ConfigParams::deserialize_from_slice(&mut Codec::new(bytes.as_slice())).unwrap(); 64 | 65 | params == decoded 66 | } 67 | 68 | fn config_params_serialize_readable(params: ConfigParams) -> bool { 69 | use chain_core::property::Serialize as _; 70 | let bytes = params.serialize_as_vec().unwrap(); 71 | let decoded = ConfigParams::deserialize_from_slice(&mut Codec::new(bytes.as_slice())).unwrap(); 72 | 73 | params == decoded 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/fragment/content.rs: -------------------------------------------------------------------------------- 1 | use crate::fragment::Fragment; 2 | use crate::key::Hash; 3 | use chain_core::{packer::Codec, property::Serialize}; 4 | use std::slice; 5 | 6 | pub type BlockContentHash = Hash; 7 | pub type BlockContentSize = u32; 8 | 9 | /// Block Contents 10 | /// 11 | /// To create this structure, make a ContentsBuilder and use into() 12 | #[derive(Debug, Clone)] 13 | pub struct Contents(pub(super) Box<[Fragment]>); 14 | 15 | impl PartialEq for Contents { 16 | fn eq(&self, rhs: &Self) -> bool { 17 | self.compute_hash_size() == rhs.compute_hash_size() 18 | } 19 | } 20 | impl Eq for Contents {} 21 | 22 | impl From for Contents { 23 | fn from(content_builder: ContentsBuilder) -> Self { 24 | Contents(content_builder.fragments.into()) 25 | } 26 | } 27 | 28 | impl Contents { 29 | pub fn empty() -> Self { 30 | Contents(Vec::with_capacity(0).into()) 31 | } 32 | 33 | #[inline] 34 | pub fn iter(&self) -> impl Iterator { 35 | self.0.iter() 36 | } 37 | 38 | #[inline] 39 | pub fn iter_slice(&self) -> slice::Iter<'_, Fragment> { 40 | self.0.iter() 41 | } 42 | 43 | pub fn compute_hash_size(&self) -> (BlockContentHash, BlockContentSize) { 44 | let mut bytes = Vec::with_capacity(4096); 45 | 46 | for message in self.iter() { 47 | message.serialize(&mut Codec::new(&mut bytes)).unwrap(); 48 | } 49 | 50 | let hash = Hash::hash_bytes(&bytes); 51 | (hash, bytes.len() as u32) 52 | } 53 | 54 | pub fn compute_hash(&self) -> BlockContentHash { 55 | self.compute_hash_size().0 56 | } 57 | 58 | pub fn len(&self) -> usize { 59 | self.0.len() 60 | } 61 | 62 | pub fn is_empty(&self) -> bool { 63 | self.0.is_empty() 64 | } 65 | } 66 | 67 | #[derive(Clone, Default)] 68 | pub struct ContentsBuilder { 69 | fragments: Vec, 70 | } 71 | 72 | impl ContentsBuilder { 73 | pub fn new() -> Self { 74 | ContentsBuilder { 75 | fragments: Vec::new(), 76 | } 77 | } 78 | 79 | pub fn push(&mut self, fragment: Fragment) { 80 | self.fragments.push(fragment) 81 | } 82 | 83 | /// set multiple messages in the block to build 84 | pub fn push_many(&mut self, fragments: I) -> &mut Self 85 | where 86 | I: IntoIterator, 87 | { 88 | self.fragments.extend(fragments); 89 | self 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/fragment/test.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::config::ConfigParam; 3 | #[cfg(test)] 4 | use crate::testing::serialization::serialization_bijection; 5 | #[cfg(test)] 6 | use quickcheck::TestResult; 7 | use quickcheck::{Arbitrary, Gen}; 8 | use quickcheck_macros::quickcheck; 9 | 10 | impl Arbitrary for Fragment { 11 | fn arbitrary(g: &mut G) -> Self { 12 | #[cfg(not(feature = "evm"))] 13 | let r = g.next_u32() % 14; 14 | #[cfg(feature = "evm")] 15 | let r = g.next_u32() % 16; 16 | match r { 17 | 0 => Fragment::Initial(Arbitrary::arbitrary(g)), 18 | 1 => Fragment::OldUtxoDeclaration(Arbitrary::arbitrary(g)), 19 | 2 => Fragment::Transaction(Arbitrary::arbitrary(g)), 20 | 3 => Fragment::OwnerStakeDelegation(Arbitrary::arbitrary(g)), 21 | 4 => Fragment::StakeDelegation(Arbitrary::arbitrary(g)), 22 | 5 => Fragment::PoolRegistration(Arbitrary::arbitrary(g)), 23 | 6 => Fragment::PoolRetirement(Arbitrary::arbitrary(g)), 24 | 7 => Fragment::PoolUpdate(Arbitrary::arbitrary(g)), 25 | 8 => Fragment::UpdateProposal(Arbitrary::arbitrary(g)), 26 | 9 => Fragment::UpdateVote(Arbitrary::arbitrary(g)), 27 | 10 => Fragment::VotePlan(Arbitrary::arbitrary(g)), 28 | 11 => Fragment::VoteCast(Arbitrary::arbitrary(g)), 29 | 12 => Fragment::VoteTally(Arbitrary::arbitrary(g)), 30 | 13 => Fragment::MintToken(Arbitrary::arbitrary(g)), 31 | #[cfg(feature = "evm")] 32 | 14 => Fragment::Evm(Arbitrary::arbitrary(g)), 33 | #[cfg(feature = "evm")] 34 | 15 => Fragment::EvmMapping(Arbitrary::arbitrary(g)), 35 | _ => unreachable!(), 36 | } 37 | } 38 | } 39 | 40 | impl Arbitrary for ConfigParams { 41 | fn arbitrary(g: &mut G) -> Self { 42 | let size = u8::arbitrary(g) as usize; 43 | ConfigParams( 44 | std::iter::repeat_with(|| ConfigParam::arbitrary(g)) 45 | .take(size) 46 | .collect(), 47 | ) 48 | } 49 | } 50 | 51 | quickcheck! { 52 | fn fragment_serialization_bijection(b: Fragment) -> TestResult { 53 | serialization_bijection(b) 54 | } 55 | 56 | fn initial_ents_serialization_bijection(config_params: ConfigParams) -> TestResult { 57 | serialization_bijection(config_params) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/header/components.rs: -------------------------------------------------------------------------------- 1 | use super::cstruct; 2 | use chain_crypto::algorithms::vrf::ProvenOutputSeed; 3 | use chain_crypto::{Ed25519, PublicKey, Signature, SumEd25519_12, Verification}; 4 | use std::fmt::{self, Debug}; 5 | 6 | #[derive(Debug, Clone)] 7 | pub struct HeaderAuth; 8 | 9 | #[derive(Debug, Clone)] 10 | pub struct KesSignature(pub(crate) Signature); 11 | 12 | impl From for KesSignature { 13 | fn from(b: cstruct::GpKesSignature) -> KesSignature { 14 | KesSignature( 15 | Signature::from_binary(&b[..]).expect("internal error: KES signature length invalid"), 16 | ) 17 | } 18 | } 19 | 20 | impl From> for KesSignature { 21 | fn from(sig: Signature) -> KesSignature { 22 | KesSignature(sig) 23 | } 24 | } 25 | 26 | impl KesSignature { 27 | pub fn verify(&self, pk: &PublicKey, data: &[u8]) -> Verification { 28 | self.0.verify_slice(pk, data) 29 | } 30 | } 31 | 32 | #[derive(Debug, Clone)] 33 | pub struct BftSignature(pub(crate) Signature); 34 | 35 | impl From for BftSignature { 36 | fn from(b: cstruct::BftSignature) -> BftSignature { 37 | BftSignature( 38 | Signature::from_binary(&b[..]).expect("internal error: BFT signature length invalid"), 39 | ) 40 | } 41 | } 42 | 43 | impl From> for BftSignature { 44 | fn from(sig: Signature) -> BftSignature { 45 | BftSignature(sig) 46 | } 47 | } 48 | 49 | #[derive(Clone)] 50 | pub struct VrfProof(pub(super) cstruct::GpVrfProof); 51 | 52 | impl Debug for VrfProof { 53 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 54 | f.debug_struct("VrfProof") 55 | .field("data", &&self.0[..]) 56 | .finish() 57 | } 58 | } 59 | 60 | impl VrfProof { 61 | pub fn to_vrf_proof(&self) -> Option { 62 | ProvenOutputSeed::from_bytes_unverified(&self.0) 63 | } 64 | } 65 | 66 | impl From for VrfProof { 67 | fn from(v: ProvenOutputSeed) -> VrfProof { 68 | VrfProof(v.bytes()) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/header/deconstruct.rs: -------------------------------------------------------------------------------- 1 | use super::components::{BftSignature, KesSignature, VrfProof}; 2 | use super::version::BlockVersion; 3 | use crate::certificate::PoolId; 4 | use crate::chaintypes::{ChainLength, HeaderId}; 5 | use crate::date::BlockDate; 6 | use crate::fragment::{BlockContentHash, BlockContentSize}; 7 | use crate::key::BftLeaderId; 8 | 9 | #[derive(Debug, Clone, PartialEq, Eq)] 10 | pub struct Common { 11 | pub block_version: BlockVersion, 12 | pub block_date: BlockDate, 13 | pub block_content_size: BlockContentSize, 14 | pub block_content_hash: BlockContentHash, 15 | pub block_parent_hash: HeaderId, 16 | pub chain_length: ChainLength, 17 | } 18 | 19 | #[derive(Debug, Clone)] 20 | pub enum Proof { 21 | /// In case there is no need for consensus layer and no need for proof of the 22 | /// block. This may apply to the genesis block for example. 23 | None, 24 | Bft(BftProof), 25 | GenesisPraos(GenesisPraosProof), 26 | } 27 | 28 | #[derive(Debug, Clone)] 29 | pub struct BftProof { 30 | pub(crate) leader_id: BftLeaderId, 31 | pub(crate) signature: BftSignature, 32 | } 33 | 34 | #[derive(Debug, Clone)] 35 | pub struct GenesisPraosProof { 36 | pub(crate) node_id: PoolId, 37 | pub(crate) vrf_proof: VrfProof, 38 | pub(crate) kes_proof: KesSignature, 39 | } 40 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/header/mod.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | mod components; 3 | mod cstruct; 4 | mod deconstruct; 5 | #[allow(clippy::module_inception)] 6 | mod header; 7 | mod version; 8 | 9 | #[cfg(any(test, feature = "property-test-api"))] 10 | pub mod test; 11 | 12 | pub use crate::chaintypes::{ChainLength, HeaderId}; 13 | pub use crate::date::{BlockDate, Epoch, SlotId}; 14 | 15 | pub use builder::{ 16 | header_builder, HeaderBftBuilder, HeaderBuilder, HeaderBuilderNew, HeaderGenesisPraosBuilder, 17 | HeaderSetConsensusData, HeaderSetConsensusSignature, 18 | }; 19 | pub use components::{BftSignature, KesSignature, VrfProof}; 20 | pub use deconstruct::{BftProof, Common, GenesisPraosProof, Proof}; 21 | pub use header::{Header, HeaderBft, HeaderDesc, HeaderGenesisPraos, HeaderUnsigned}; 22 | pub use version::{AnyBlockVersion, BlockVersion}; 23 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/ledger/governance/mod.rs: -------------------------------------------------------------------------------- 1 | //! define how to rule over the blockchain 2 | //! 3 | 4 | mod parameters; 5 | mod treasury; 6 | 7 | pub use self::{ 8 | parameters::{ 9 | ParametersGovernance, ParametersGovernanceAction, ParametersGovernanceActionType, 10 | }, 11 | treasury::{TreasuryGovernance, TreasuryGovernanceAction, TreasuryGovernanceActionType}, 12 | }; 13 | use crate::{ 14 | rewards::Ratio, 15 | vote::{Choice, Options}, 16 | }; 17 | use std::num::NonZeroU64; 18 | 19 | #[derive(Clone, Default, Eq, PartialEq)] 20 | pub struct Governance { 21 | pub treasury: treasury::TreasuryGovernance, 22 | pub parameters: parameters::ParametersGovernance, 23 | } 24 | 25 | #[derive(Debug, Clone, Eq, PartialEq)] 26 | pub struct GovernanceAcceptanceCriteria { 27 | pub minimum_stake_participation: Option, 28 | pub minimum_approval: Option, 29 | pub blank: Choice, 30 | pub favorable: Choice, 31 | pub rejection: Choice, 32 | pub options: Options, 33 | } 34 | 35 | impl Default for GovernanceAcceptanceCriteria { 36 | fn default() -> Self { 37 | const CENT: NonZeroU64 = unsafe { NonZeroU64::new_unchecked(100) }; 38 | 39 | Self { 40 | minimum_stake_participation: Some(Ratio { 41 | numerator: 30, 42 | denominator: CENT, 43 | }), 44 | minimum_approval: Some(Ratio { 45 | numerator: 50, 46 | denominator: CENT, 47 | }), 48 | blank: Choice::new(0), 49 | favorable: Choice::new(1), 50 | rejection: Choice::new(2), 51 | options: Options::new_length(3).expect("3 valid choices possible"), 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/ledger/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod check; 2 | #[cfg(feature = "evm")] 3 | pub(crate) mod evm; 4 | pub mod governance; 5 | mod info; 6 | pub mod iter; 7 | mod leaderlog; 8 | #[allow(clippy::module_inception)] 9 | pub mod ledger; 10 | mod pots; 11 | pub mod recovery; 12 | mod reward_info; 13 | pub mod token_distribution; 14 | 15 | pub use iter::*; 16 | pub use leaderlog::LeadersParticipationRecord; 17 | pub use ledger::*; 18 | pub use pots::Pots; 19 | pub use reward_info::{EpochRewardsInfo, RewardsInfoParameters}; 20 | 21 | #[cfg(test)] 22 | pub mod tests; 23 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/ledger/reward_info.rs: -------------------------------------------------------------------------------- 1 | use crate::account; 2 | use crate::certificate::PoolId; 3 | use crate::value::Value; 4 | use std::collections::BTreeMap; 5 | use std::default::Default; 6 | 7 | /// Control what information will be extracted from the rewarding distribution process 8 | /// 9 | /// By default, only the really cheap basic information will be added, 10 | /// but for specific it's possible to get extra information that cost. 11 | #[derive(Default, Debug, Clone, Copy)] 12 | pub struct RewardsInfoParameters { 13 | report_stake_pools: bool, 14 | report_accounts: bool, 15 | } 16 | 17 | impl RewardsInfoParameters { 18 | pub fn report_all() -> Self { 19 | RewardsInfoParameters { 20 | report_stake_pools: true, 21 | report_accounts: true, 22 | } 23 | } 24 | } 25 | 26 | /// The epoch reward information. 27 | /// 28 | /// note that stake_pools and accounts are 29 | /// only filled up if the reward info parameters 30 | /// report_stake_pools and report_accounts (respectively) 31 | /// are turned on. 32 | #[derive(Debug, Clone)] 33 | pub struct EpochRewardsInfo { 34 | /// Params used 35 | pub params: RewardsInfoParameters, 36 | /// Total Drawn from reward escrow pot for this epoch 37 | pub drawn: Value, 38 | /// Fees contributed into the pot this epoch 39 | pub fees: Value, 40 | /// Value added to the treasury 41 | pub treasury: Value, 42 | /// Amount added to each pool id. structure can be empty. 43 | pub stake_pools: BTreeMap, 44 | /// Amount added to each account. structure can be empty. 45 | pub accounts: BTreeMap, 46 | } 47 | 48 | impl EpochRewardsInfo { 49 | pub fn new(params: RewardsInfoParameters) -> EpochRewardsInfo { 50 | EpochRewardsInfo { 51 | params, 52 | drawn: Value::zero(), 53 | fees: Value::zero(), 54 | treasury: Value::zero(), 55 | stake_pools: BTreeMap::new(), 56 | accounts: BTreeMap::new(), 57 | } 58 | } 59 | 60 | pub fn set_treasury(&mut self, value: Value) { 61 | // mostly prevent this to be set twice (unless it is set zero) 62 | assert_eq!(self.treasury, Value::zero()); 63 | self.treasury = value; 64 | } 65 | 66 | pub fn set_stake_pool(&mut self, pool: &PoolId, owned: Value, distributed: Value) { 67 | if self.params.report_stake_pools { 68 | self.stake_pools.insert(pool.clone(), (owned, distributed)); 69 | } 70 | } 71 | 72 | pub fn add_to_account(&mut self, account: &account::Identifier, value: Value) { 73 | if self.params.report_accounts { 74 | let ent = self.accounts.entry(account.clone()).or_default(); 75 | *ent = (*ent + value).unwrap() 76 | } 77 | } 78 | 79 | pub fn total(&self) -> Value { 80 | (self.drawn + self.fees).unwrap() 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/ledger/tests/certificate_tests/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod pool_registration; 2 | pub mod pool_update; 3 | pub mod tokens; 4 | pub mod update; 5 | pub mod voting; 6 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/ledger/tests/certificate_tests/tokens.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | use crate::{ 4 | key::BftLeaderId, 5 | testing::{builders::*, data::Wallet, ConfigBuilder, LedgerBuilder, TestGen}, 6 | value::*, 7 | }; 8 | 9 | #[test] 10 | pub fn mint_token_in_block0() { 11 | let alice = Wallet::from_value(Value(100)); 12 | let token_certificate = 13 | create_initial_mint_token(TestGen::mint_token_for_wallet(alice.public_key().into())); 14 | 15 | let leader = BftLeaderId::from(alice.public_key()); 16 | let config_builder = ConfigBuilder::new().with_leaders(&[leader]); 17 | 18 | LedgerBuilder::from_config(config_builder) 19 | .faucets_wallets(vec![&alice]) 20 | .certs(&[token_certificate]) 21 | .build() 22 | .expect("ledger should be built with mint_token certificate"); 23 | } 24 | 25 | #[test] 26 | pub fn mint_token_in_block0_with_input() { 27 | let alice = Wallet::from_value(Value(100)); 28 | let mint_token = TestGen::mint_token_for_wallet(alice.public_key().into()); 29 | let mint_token_tx = InitialFaultTolerantTxCertBuilder::new(mint_token.into(), alice.clone()) 30 | .transaction_with_input_only(); 31 | 32 | let result = LedgerBuilder::from_config(ConfigBuilder::new()) 33 | .faucets_wallets(vec![&alice]) 34 | .certs(&[mint_token_tx]) 35 | .build(); 36 | 37 | assert!(result.is_err()); 38 | } 39 | 40 | #[test] 41 | pub fn mint_token_in_block0_with_output() { 42 | let alice = Wallet::from_value(Value(100)); 43 | let mint_token = TestGen::mint_token_for_wallet(alice.public_key().into()); 44 | let mint_token_tx = InitialFaultTolerantTxCertBuilder::new(mint_token.into(), alice.clone()) 45 | .transaction_with_output_only(); 46 | 47 | let ledger_build_result = LedgerBuilder::from_config(ConfigBuilder::new()) 48 | .faucets_wallets(vec![&alice]) 49 | .certs(&[mint_token_tx]) 50 | .build(); 51 | 52 | assert!( 53 | ledger_build_result.is_err(), 54 | "ledger should not be built with faulty mint token certificate" 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/ledger/tests/macros.rs: -------------------------------------------------------------------------------- 1 | // assert_err( ExpectedErrorExpression , Expression ) 2 | // 3 | // succeed if Expression's value returns a value equal to Err(ExpectedErrorExpression), 4 | // otherwise panic!() with some diagnostic 5 | #[allow(unused_macros)] 6 | macro_rules! assert_err { 7 | ($left: expr, $right: expr) => { 8 | match &($left) { 9 | left_val => match &($right) { 10 | Err(e) => { 11 | if !(e == left_val) { 12 | panic!( 13 | "assertion failed: error mismatch \ 14 | (left: `{:?}, right: `{:?}`)", 15 | *left_val, *e 16 | ) 17 | } 18 | } 19 | Ok(_) => panic!( 20 | "assertion failed: expected error {:?} but got success", 21 | *left_val 22 | ), 23 | }, 24 | } 25 | }; 26 | } 27 | 28 | // assert_err_match( ExpectedErrorPattern , Expression ) 29 | // 30 | // succeed if Expression's value a Err(E) where E match the ExpectedErrorPattern, 31 | // otherwise panic!() with some diagnostic 32 | #[allow(unused_macros)] 33 | macro_rules! assert_err_match { 34 | ($left: pat, $right: expr) => { 35 | match &($right) { 36 | Err(e) => match e { 37 | $left => {} 38 | _ => panic!( 39 | "assertion failed: error mismatch got: `{:?}` but expecting {}", 40 | *e, 41 | stringify!($left) 42 | ), 43 | }, 44 | Ok(_) => panic!( 45 | "assertion failed: expected error {:?} but got success", 46 | stringify!($left) 47 | ), 48 | } 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/ledger/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | pub mod apply_block_tests; 4 | pub mod certificate_tests; 5 | pub mod discrimination_tests; 6 | pub mod initial_funds_tests; 7 | pub mod ledger_tests; 8 | pub mod transaction_tests; 9 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/ledger/token_distribution.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | account::{self, LedgerError}, 3 | ledger::Error, 4 | tokens::identifier::TokenIdentifier, 5 | value::Value, 6 | }; 7 | use imhamt::Hamt; 8 | use std::collections::hash_map::DefaultHasher; 9 | 10 | #[derive(PartialEq, Eq)] 11 | pub struct TokenDistribution<'a, T: Clone + PartialEq + Eq> { 12 | token_totals: &'a TokenTotals, 13 | account_ledger: &'a account::Ledger, 14 | token: &'a T, 15 | } 16 | 17 | impl Clone for TokenDistribution<'_, ()> { 18 | fn clone(&self) -> Self { 19 | Self { 20 | token_totals: self.token_totals, 21 | account_ledger: self.account_ledger, 22 | token: &(), 23 | } 24 | } 25 | } 26 | 27 | impl<'a> TokenDistribution<'a, ()> { 28 | pub fn new(token_totals: &'a TokenTotals, account_ledger: &'a account::Ledger) -> Self { 29 | Self { 30 | token_totals, 31 | account_ledger, 32 | token: &(), 33 | } 34 | } 35 | 36 | pub fn token(self, token: &'a TokenIdentifier) -> TokenDistribution { 37 | TokenDistribution { 38 | token_totals: self.token_totals, 39 | account_ledger: self.account_ledger, 40 | token, 41 | } 42 | } 43 | } 44 | 45 | impl<'a> TokenDistribution<'a, TokenIdentifier> { 46 | pub fn get_total(&self) -> Value { 47 | self.token_totals 48 | .get_total(self.token) 49 | // maybe this should be an error? but errors in the tally are not really recoverable 50 | // OTOH, this will make the tally succeed but not have any effect, so it's more or less 51 | // the same effect 52 | .unwrap_or_else(Value::zero) 53 | } 54 | 55 | pub fn get_account(&self, account: &account::Identifier) -> Result, LedgerError> { 56 | self.account_ledger 57 | .get_state(account) 58 | .map(|account_state| account_state.tokens.lookup(self.token).copied()) 59 | } 60 | } 61 | 62 | #[derive(Clone, PartialEq, Eq, Default)] 63 | pub struct TokenTotals(Hamt); 64 | 65 | impl TokenTotals { 66 | #[must_use = "Does not modify the internal state"] 67 | pub fn add(&self, token: TokenIdentifier, value: Value) -> Result { 68 | self.0 69 | .insert_or_update(token, value, |v| v.checked_add(value).map(Some)) 70 | .map(TokenTotals) 71 | .map_err(Into::into) 72 | } 73 | 74 | pub fn get_total(&self, token: &TokenIdentifier) -> Option { 75 | self.0.lookup(token).copied() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::all)] 2 | 3 | #[cfg(any(test, feature = "property-test-api"))] 4 | #[macro_use] 5 | extern crate quickcheck; 6 | 7 | pub mod account; 8 | pub mod accounting; 9 | pub mod block; 10 | pub mod certificate; 11 | pub mod chaineval; 12 | pub mod chaintypes; 13 | pub mod config; 14 | mod date; 15 | pub mod error; 16 | pub mod evm; 17 | pub mod fee; 18 | pub mod fragment; 19 | pub mod header; 20 | pub mod key; 21 | pub mod leadership; 22 | pub mod ledger; 23 | pub mod legacy; 24 | pub mod milli; 25 | pub mod multisig; 26 | pub mod multiverse; 27 | pub mod rewards; 28 | pub mod setting; 29 | pub mod stake; 30 | pub mod tokens; 31 | pub mod transaction; 32 | pub mod treasury; 33 | pub mod update; 34 | pub mod utxo; 35 | pub mod value; 36 | pub mod vote; 37 | 38 | #[cfg(any(test, feature = "property-test-api", feature = "with-bench"))] 39 | pub mod testing; 40 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/stake/mod.rs: -------------------------------------------------------------------------------- 1 | mod controlled; 2 | mod delegation; 3 | mod distribution; 4 | #[allow(clippy::module_inception)] 5 | mod stake; 6 | 7 | pub use controlled::StakeControl; 8 | pub use delegation::*; 9 | pub use distribution::*; 10 | pub use stake::*; 11 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/arbitrary/config_builder.rs: -------------------------------------------------------------------------------- 1 | use quickcheck::{Arbitrary, Gen}; 2 | 3 | use crate::{ 4 | chaintypes::ConsensusType, 5 | config::{Block0Date, RewardParams}, 6 | fee::{LinearFee, PerCertificateFee}, 7 | milli::Milli, 8 | rewards::TaxType, 9 | testing::{arbitrary::NonZeroValue, ledger::ConfigBuilder}, 10 | }; 11 | use chain_addr::Discrimination; 12 | 13 | impl Arbitrary for ConfigBuilder { 14 | fn arbitrary(g: &mut G) -> Self { 15 | ConfigBuilder::new() 16 | .with_rewards(NonZeroValue::arbitrary(g).0) 17 | .with_treasury(NonZeroValue::arbitrary(g).0) 18 | .with_treasury_params(TaxType::arbitrary(g)) 19 | .with_rewards_params(RewardParams::arbitrary(g)) 20 | .with_discrimination(Discrimination::arbitrary(g)) 21 | .with_slot_duration(u8::arbitrary(g)) 22 | .with_fee(LinearFee::arbitrary(g)) 23 | .with_per_certificate_fee(PerCertificateFee::arbitrary(g)) 24 | .with_slots_per_epoch(u32::arbitrary(g)) 25 | .with_active_slots_coeff(Milli::arbitrary(g)) 26 | .with_block_content_max_size(u32::arbitrary(g)) 27 | .with_kes_update_speed(u32::arbitrary(g)) 28 | .with_block0_date(Block0Date::arbitrary(g)) 29 | .with_consensus_version(ConsensusType::arbitrary(g)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/arbitrary/kind_type.rs: -------------------------------------------------------------------------------- 1 | use chain_addr::{Kind, KindType}; 2 | use quickcheck::{Arbitrary, Gen}; 3 | use std::iter; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct KindTypeWithoutMultisig(pub KindType); 7 | 8 | impl Arbitrary for KindTypeWithoutMultisig { 9 | fn arbitrary(g: &mut G) -> Self { 10 | KindTypeWithoutMultisig( 11 | iter::from_fn(|| Some(KindType::arbitrary(g))) 12 | .find(|x| !matches!(x, KindType::Multisig | KindType::Script)) 13 | .unwrap(), 14 | ) 15 | } 16 | } 17 | 18 | impl KindTypeWithoutMultisig { 19 | pub fn kind_type(&self) -> KindType { 20 | self.0 21 | } 22 | } 23 | 24 | impl From for KindType { 25 | fn from(kind_type_without_multisig: KindTypeWithoutMultisig) -> Self { 26 | kind_type_without_multisig.kind_type() 27 | } 28 | } 29 | 30 | #[derive(Clone, Debug)] 31 | pub struct KindWithoutMultisig(pub Kind); 32 | 33 | impl Arbitrary for KindWithoutMultisig { 34 | fn arbitrary(g: &mut G) -> Self { 35 | KindWithoutMultisig( 36 | iter::from_fn(|| Some(Kind::arbitrary(g))) 37 | .find(|x| !matches!(x, Kind::Multisig { .. })) 38 | .unwrap(), 39 | ) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/arbitrary/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod address; 2 | pub mod config_builder; 3 | pub mod kind_type; 4 | pub mod ledger_builder; 5 | pub mod output; 6 | pub mod random; 7 | pub mod transaction; 8 | pub mod update_proposal; 9 | pub mod utils; 10 | pub mod utxo; 11 | pub mod wallet; 12 | 13 | use crate::{key::BftLeaderId, transaction::Output, value::Value}; 14 | use chain_addr::Address; 15 | use chain_crypto::{Ed25519, SecretKey}; 16 | use quickcheck::{Arbitrary, Gen}; 17 | 18 | pub use address::*; 19 | pub use kind_type::*; 20 | pub use output::*; 21 | pub use random::*; 22 | use std::cmp; 23 | pub use transaction::*; 24 | pub use update_proposal::*; 25 | pub use wallet::WalletCollection; 26 | 27 | impl Arbitrary for Value { 28 | fn arbitrary(gen: &mut G) -> Self { 29 | Value(u64::arbitrary(gen)) 30 | } 31 | } 32 | 33 | // Average value used in test where value is larger than zero 34 | #[derive(Debug, Copy, Clone)] 35 | pub struct NonZeroValue(pub Value); 36 | 37 | impl Arbitrary for NonZeroValue { 38 | fn arbitrary(gen: &mut G) -> Self { 39 | NonZeroValue(Value(cmp::max(u64::arbitrary(gen), 1))) 40 | } 41 | } 42 | 43 | impl From for Value { 44 | fn from(value: NonZeroValue) -> Self { 45 | value.0 46 | } 47 | } 48 | 49 | // Average value used in test where value is larger than zero and not too big 50 | // in case we would like to sum up values and not suffer with buffer overflow 51 | #[derive(Debug, Copy, Clone)] 52 | pub struct AverageValue(pub Value); 53 | 54 | impl Arbitrary for AverageValue { 55 | fn arbitrary(gen: &mut G) -> Self { 56 | AverageValue(Value(u64::arbitrary(gen) % 10000 + 253)) 57 | } 58 | } 59 | 60 | impl From for Value { 61 | fn from(value: AverageValue) -> Self { 62 | value.0 63 | } 64 | } 65 | 66 | impl Arbitrary for Output
{ 67 | fn arbitrary(g: &mut G) -> Self { 68 | Output { 69 | address: Arbitrary::arbitrary(g), 70 | value: Arbitrary::arbitrary(g), 71 | } 72 | } 73 | } 74 | 75 | impl Arbitrary for BftLeaderId { 76 | fn arbitrary(g: &mut G) -> Self { 77 | let mut seed = [0; 32]; 78 | for byte in seed.iter_mut() { 79 | *byte = Arbitrary::arbitrary(g); 80 | } 81 | let sk: SecretKey = Arbitrary::arbitrary(g); 82 | BftLeaderId(sk.to_public()) 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/arbitrary/output.rs: -------------------------------------------------------------------------------- 1 | use crate::transaction::Output; 2 | use chain_addr::{Address, Discrimination, Kind}; 3 | use quickcheck::{Arbitrary, Gen}; 4 | use std::iter; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct OutputsWithoutMultisig(pub Vec>); 8 | 9 | impl Arbitrary for OutputsWithoutMultisig { 10 | fn arbitrary(gen: &mut G) -> Self { 11 | let n = usize::arbitrary(gen); 12 | OutputsWithoutMultisig( 13 | iter::from_fn(|| Some(Output::arbitrary(gen))) 14 | .filter(|x| !matches!(x.address.1, Kind::Multisig { .. })) 15 | .take(n) 16 | .collect(), 17 | ) 18 | } 19 | } 20 | 21 | impl OutputsWithoutMultisig { 22 | pub fn set_discrimination(&mut self, discrimination: Discrimination) { 23 | for output in &mut self.0 { 24 | output.address.0 = discrimination 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/arbitrary/random.rs: -------------------------------------------------------------------------------- 1 | use quickcheck::{Arbitrary, Gen}; 2 | 3 | #[derive(Clone, Debug)] 4 | pub struct Random1to10(pub u64); 5 | 6 | impl Arbitrary for Random1to10 { 7 | fn arbitrary(g: &mut G) -> Self { 8 | Self(u64::arbitrary(g) % 10 + 1) 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/arbitrary/utxo.rs: -------------------------------------------------------------------------------- 1 | use super::AverageValue; 2 | use crate::{key::Hash, testing::data::AddressData, transaction::Output, utxo::Ledger}; 3 | use chain_addr::{Address, Discrimination}; 4 | use quickcheck::{Arbitrary, Gen}; 5 | use std::{collections::HashMap, iter}; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct ArbitaryLedgerUtxo(pub Ledger
); 9 | 10 | impl Arbitrary for ArbitaryLedgerUtxo { 11 | fn arbitrary(g: &mut G) -> Self { 12 | let mut ledger = Ledger::new(); 13 | let size = usize::arbitrary(g) % 50 + 1; 14 | let arbitrary_utxos: HashMap)> = iter::from_fn(|| { 15 | let outs = match u8::arbitrary(g) % 2 { 16 | 0 => ( 17 | 0u8, 18 | AddressData::utxo(Discrimination::Test) 19 | .make_output(AverageValue::arbitrary(g).into()), 20 | ), 21 | 1 => ( 22 | 0u8, 23 | AddressData::delegation(Discrimination::Test) 24 | .make_output(AverageValue::arbitrary(g).into()), 25 | ), 26 | _ => unreachable!(), 27 | }; 28 | Some((Hash::arbitrary(g), outs)) 29 | }) 30 | .take(size) 31 | .collect(); 32 | 33 | for (key, value) in arbitrary_utxos { 34 | ledger = ledger.add(&key, &[value]).unwrap(); 35 | } 36 | ArbitaryLedgerUtxo(ledger) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/arbitrary/wallet.rs: -------------------------------------------------------------------------------- 1 | use crate::testing::{arbitrary::AverageValue, data::Wallet}; 2 | use quickcheck::{Arbitrary, Gen}; 3 | use std::iter; 4 | 5 | #[derive(Clone, Debug)] 6 | pub struct WalletCollection(pub Vec); 7 | 8 | impl Arbitrary for WalletCollection { 9 | fn arbitrary(gen: &mut G) -> Self { 10 | let size_limit = 253; 11 | let n = usize::arbitrary(gen) % size_limit + 1; 12 | let addresses = iter::from_fn(|| Some(Wallet::arbitrary(gen))).take(n); 13 | WalletCollection(addresses.collect()) 14 | } 15 | } 16 | 17 | impl Arbitrary for Wallet { 18 | fn arbitrary(gen: &mut G) -> Self { 19 | Wallet::from_value(AverageValue::arbitrary(gen).0) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/builders/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block_builder; 2 | pub mod cert_builder; 3 | mod initial_builder; 4 | pub mod old_address_builder; 5 | pub mod stake_pool_builder; 6 | pub mod tx_builder; 7 | mod tx_cert_builder; 8 | mod vote; 9 | pub mod witness_builder; 10 | 11 | pub use block_builder::*; 12 | pub use cert_builder::*; 13 | pub use initial_builder::*; 14 | pub use old_address_builder::*; 15 | pub use stake_pool_builder::*; 16 | pub use tx_builder::*; 17 | pub use tx_cert_builder::*; 18 | pub use vote::*; 19 | pub use witness_builder::*; 20 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/builders/old_address_builder.rs: -------------------------------------------------------------------------------- 1 | use crate::{legacy::UtxoDeclaration, value::Value}; 2 | use cardano_legacy_address::Addr; 3 | use cardano_legacy_address::ExtendedAddr; 4 | use ed25519_bip32::{XPub, XPUB_SIZE}; 5 | use rand_core::RngCore; 6 | 7 | #[derive(Default)] 8 | pub struct OldAddressBuilder; 9 | 10 | impl OldAddressBuilder { 11 | pub fn build_utxo_declaration(size: Option) -> UtxoDeclaration { 12 | let nb = match size { 13 | Some(size) => size, 14 | None => { 15 | let mut rng = rand_core::OsRng; 16 | let nb: usize = rng.next_u32() as usize; 17 | nb % 255 18 | } 19 | }; 20 | let mut addrs = Vec::with_capacity(nb); 21 | for _ in 0..nb { 22 | addrs.push(Self::build_old_address()); 23 | } 24 | UtxoDeclaration { addrs } 25 | } 26 | 27 | pub fn build_old_address() -> (Addr, Value) { 28 | // some reasonable value 29 | let mut rng = rand_core::OsRng; 30 | let value = Value(rng.next_u64() % 2_000_000 + 1); 31 | let xpub = { 32 | let mut buf = [0u8; XPUB_SIZE]; 33 | rng.fill_bytes(&mut buf); 34 | XPub::from_bytes(buf) 35 | }; 36 | let ea = ExtendedAddr::new_simple(&xpub, None); 37 | (ea.to_address(), value) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/builders/vote.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | certificate::{ 3 | DecryptedPrivateTally, DecryptedPrivateTallyError, DecryptedPrivateTallyProposal, 4 | }, 5 | testing::data::CommitteeMembersManager, 6 | vote::VotePlanStatus, 7 | }; 8 | 9 | use chain_vote::tally::batch_decrypt; 10 | use rand::thread_rng; 11 | 12 | pub fn decrypt_tally( 13 | vote_plan_status: &VotePlanStatus, 14 | members: &CommitteeMembersManager, 15 | ) -> Result { 16 | let members_pks: Vec = members 17 | .members() 18 | .iter() 19 | .map(|member| member.public_key()) 20 | .collect(); 21 | 22 | let (shares, tallies): (Vec<_>, Vec<_>) = vote_plan_status 23 | .proposals 24 | .iter() 25 | .map(|proposal| { 26 | let encrypted_tally = proposal.tally.private_encrypted().unwrap(); 27 | let decrypt_shares = members 28 | .members() 29 | .iter() 30 | .map(|member| member.secret_key()) 31 | .map(|secret_key| encrypted_tally.partial_decrypt(&mut thread_rng(), secret_key)) 32 | .collect::>(); 33 | let validated_tally = encrypted_tally 34 | .validate_partial_decryptions(&members_pks, &decrypt_shares) 35 | .expect("Invalid shares"); 36 | 37 | (decrypt_shares, validated_tally) 38 | }) 39 | .unzip(); 40 | 41 | let tallies = batch_decrypt(&tallies).unwrap(); 42 | 43 | let proposals = shares 44 | .into_iter() 45 | .zip(tallies.into_iter()) 46 | .map(|(shares, tally)| DecryptedPrivateTallyProposal { 47 | decrypt_shares: shares.into_boxed_slice(), 48 | tally_result: tally.votes.into_boxed_slice(), 49 | }) 50 | .collect(); 51 | 52 | DecryptedPrivateTally::new(proposals) 53 | } 54 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/builders/witness_builder.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | chaintypes::HeaderId, testing::data::AddressData, transaction::TransactionSignDataHash, 3 | }; 4 | use chain_addr::Kind; 5 | 6 | pub use crate::transaction::Witness; 7 | 8 | pub fn make_witnesses( 9 | block0: &HeaderId, 10 | addresses_data: Vec<&AddressData>, 11 | transaction_hash: &TransactionSignDataHash, 12 | ) -> Vec { 13 | addresses_data 14 | .iter() 15 | .map(|x| make_witness(block0, x, transaction_hash)) 16 | .collect() 17 | } 18 | 19 | pub fn make_witness( 20 | block0: &HeaderId, 21 | addres_data: &AddressData, 22 | transaction_hash: &TransactionSignDataHash, 23 | ) -> Witness { 24 | match addres_data.address.kind() { 25 | Kind::Account(_) => Witness::new_account( 26 | block0, 27 | transaction_hash, 28 | addres_data.spending_counter.get_valid_counter(), 29 | |d| addres_data.private_key().sign(d), 30 | ), 31 | _ => Witness::new_utxo(block0, transaction_hash, |d| { 32 | addres_data.private_key().sign(d) 33 | }), 34 | } 35 | } 36 | 37 | pub fn make_witness_with_lane( 38 | block0: &HeaderId, 39 | addres_data: &AddressData, 40 | lane: usize, 41 | transaction_hash: &TransactionSignDataHash, 42 | ) -> Witness { 43 | match addres_data.address.kind() { 44 | Kind::Account(_) => Witness::new_account( 45 | block0, 46 | transaction_hash, 47 | addres_data.spending_counter_at_lane(lane).unwrap(), 48 | |d| addres_data.private_key().sign(d), 49 | ), 50 | _ => Witness::new_utxo(block0, transaction_hash, |d| { 51 | addres_data.private_key().sign(d) 52 | }), 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/data/keys.rs: -------------------------------------------------------------------------------- 1 | use crate::key::EitherEd25519SecretKey; 2 | use chain_addr::{Address, Discrimination, Kind}; 3 | use chain_crypto::testing::TestCryptoGen; 4 | use chain_crypto::{Ed25519, PublicKey, SecretKey}; 5 | use std::collections::HashMap; 6 | 7 | pub struct KeysDb { 8 | rng: u32, 9 | tcg: TestCryptoGen, 10 | ed25519: HashMap, EitherEd25519SecretKey>, 11 | } 12 | 13 | impl KeysDb { 14 | /// Create a new keys DB 15 | pub fn new(tcg: TestCryptoGen) -> Self { 16 | KeysDb { 17 | rng: 0, 18 | tcg, 19 | ed25519: HashMap::new(), 20 | } 21 | } 22 | 23 | pub fn empty() -> Self { 24 | KeysDb::new(TestCryptoGen(0)) 25 | } 26 | 27 | pub fn add_key(&mut self, sk: EitherEd25519SecretKey) { 28 | let pk = sk.to_public(); 29 | self.ed25519.insert(pk, sk); 30 | } 31 | 32 | /// Create a new Ed25519 and record it 33 | pub fn new_ed25519_secret_key(&mut self) -> SecretKey { 34 | let sk = self.tcg.secret_key(self.rng); 35 | self.rng += 1; 36 | 37 | let pk = sk.to_public(); 38 | self.ed25519 39 | .insert(pk, EitherEd25519SecretKey::Normal(sk.clone())); 40 | sk 41 | } 42 | 43 | /// same as new_ed25519_secret_key but instead return the public key directly 44 | pub fn new_ed25519_public_key(&mut self) -> PublicKey { 45 | self.new_ed25519_secret_key().to_public() 46 | } 47 | 48 | pub fn new_account_address(&mut self) -> Address { 49 | let pk = self.new_ed25519_public_key(); 50 | Address(Discrimination::Test, Kind::Account(pk)) 51 | } 52 | 53 | /// Try to get the associated secret key for a given public key 54 | pub fn find_ed25519_secret_key<'a>( 55 | &'a self, 56 | pk: &PublicKey, 57 | ) -> Option<&'a EitherEd25519SecretKey> { 58 | self.ed25519.get(pk) 59 | } 60 | 61 | pub fn find_by_address<'a>(&'a self, addr: &Address) -> Option<&'a EitherEd25519SecretKey> { 62 | match addr.kind() { 63 | Kind::Single(pk) => self.find_ed25519_secret_key(pk), 64 | Kind::Group(pk, _) => self.find_ed25519_secret_key(pk), 65 | Kind::Account(pk) => self.find_ed25519_secret_key(pk), 66 | Kind::Multisig(_) => unimplemented!(), 67 | Kind::Script(_) => unimplemented!(), 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/data/leader.rs: -------------------------------------------------------------------------------- 1 | use crate::key::BftLeaderId; 2 | use chain_crypto::{Ed25519, KeyPair, SecretKey}; 3 | use quickcheck::{Arbitrary, Gen}; 4 | use std::fmt::{self, Debug}; 5 | 6 | #[derive(Clone)] 7 | pub struct LeaderPair { 8 | leader_key: SecretKey, 9 | } 10 | 11 | impl Debug for LeaderPair { 12 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 13 | f.debug_struct("LeaderPair") 14 | .field("leader id", &self.id()) 15 | .finish() 16 | } 17 | } 18 | 19 | impl LeaderPair { 20 | pub fn new(leader_key: SecretKey) -> Self { 21 | LeaderPair { leader_key } 22 | } 23 | 24 | pub fn id(&self) -> BftLeaderId { 25 | BftLeaderId(self.leader_key.to_public()) 26 | } 27 | 28 | pub fn key(&self) -> SecretKey { 29 | self.leader_key.clone() 30 | } 31 | } 32 | 33 | impl Arbitrary for LeaderPair { 34 | fn arbitrary(g: &mut G) -> Self { 35 | LeaderPair { 36 | leader_key: KeyPair::::arbitrary(g).private_key().clone(), 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/data/mod.rs: -------------------------------------------------------------------------------- 1 | mod address; 2 | mod keys; 3 | mod leader; 4 | mod stake_pool; 5 | mod vote; 6 | mod wallet; 7 | 8 | pub use address::*; 9 | pub use keys::KeysDb; 10 | pub use leader::*; 11 | pub use stake_pool::*; 12 | pub use vote::{CommitteeMember, CommitteeMembersManager}; 13 | pub use wallet::*; 14 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/data/stake_pool.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | certificate::{PoolId, PoolRegistration}, 3 | testing::{builders::StakePoolBuilder, data::address::AddressData, TestGen}, 4 | }; 5 | 6 | use chain_crypto::{Ed25519, KeyPair, PublicKey, RistrettoGroup2HashDh, SumEd25519_12}; 7 | use quickcheck::{Arbitrary, Gen}; 8 | use std::iter; 9 | 10 | #[derive(Clone, Debug)] 11 | pub struct StakePool { 12 | alias: String, 13 | id: PoolId, 14 | vrf: KeyPair, 15 | kes: KeyPair, 16 | pool_info: PoolRegistration, 17 | reward_account: Option, 18 | } 19 | 20 | impl StakePool { 21 | pub fn new( 22 | alias: &str, 23 | id: PoolId, 24 | vrf: KeyPair, 25 | kes: KeyPair, 26 | pool_info: PoolRegistration, 27 | reward_account: Option, 28 | ) -> Self { 29 | StakePool { 30 | alias: alias.to_owned(), 31 | id, 32 | vrf, 33 | kes, 34 | pool_info, 35 | reward_account, 36 | } 37 | } 38 | 39 | pub fn id(&self) -> PoolId { 40 | self.id.clone() 41 | } 42 | 43 | pub fn vrf(&self) -> KeyPair { 44 | self.vrf.clone() 45 | } 46 | 47 | pub fn kes(&self) -> KeyPair { 48 | self.kes.clone() 49 | } 50 | 51 | pub fn info_mut(&mut self) -> &mut PoolRegistration { 52 | &mut self.pool_info 53 | } 54 | 55 | pub fn info(&self) -> PoolRegistration { 56 | self.pool_info.clone() 57 | } 58 | 59 | pub fn alias(&self) -> String { 60 | self.alias.clone() 61 | } 62 | 63 | pub fn reward_account(&self) -> Option<&AddressData> { 64 | self.reward_account.as_ref() 65 | } 66 | } 67 | 68 | impl Arbitrary for StakePool { 69 | fn arbitrary(gen: &mut G) -> Self { 70 | let owners_count = std::cmp::max(usize::arbitrary(gen) % 8, 1); 71 | let operators_count = usize::arbitrary(gen) % 4; 72 | let owners: Vec> = iter::from_fn(|| Some(TestGen::public_key())) 73 | .take(owners_count) 74 | .collect(); 75 | let operators: Vec> = iter::from_fn(|| Some(TestGen::public_key())) 76 | .take(operators_count) 77 | .collect(); 78 | 79 | StakePoolBuilder::new() 80 | .with_owners(owners) 81 | .with_operators(operators) 82 | .with_pool_permissions(Arbitrary::arbitrary(gen)) 83 | .with_reward_account(Arbitrary::arbitrary(gen)) 84 | .with_tax_type(Arbitrary::arbitrary(gen)) 85 | .build() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/data/vote.rs: -------------------------------------------------------------------------------- 1 | use crate::vote::VotePlanStatus; 2 | use chain_vote::{ 3 | committee::MemberSecretKey, Crs, ElectionPublicKey, MemberCommunicationKey, MemberPublicKey, 4 | MemberState, TallyDecryptShare, 5 | }; 6 | use rand::thread_rng; 7 | use rand_core::CryptoRng; 8 | use rand_core::RngCore; 9 | 10 | pub struct CommitteeMembersManager { 11 | members: Vec, 12 | crs: Crs, 13 | } 14 | 15 | pub struct CommitteeMember { 16 | state: MemberState, 17 | } 18 | 19 | impl CommitteeMembersManager { 20 | pub fn new( 21 | rng: &mut (impl RngCore + CryptoRng), 22 | crs_seed: &[u8], 23 | threshold: usize, 24 | members_no: usize, 25 | ) -> Self { 26 | let mut public_keys = Vec::new(); 27 | for _ in 0..members_no { 28 | let private_key = MemberCommunicationKey::new(rng); 29 | let public_key = private_key.to_public(); 30 | public_keys.push(public_key); 31 | } 32 | 33 | let crs = Crs::from_hash(crs_seed); 34 | 35 | let mut members = Vec::new(); 36 | for i in 0..members_no { 37 | let state = MemberState::new(rng, threshold, &crs, &public_keys, i); 38 | members.push(CommitteeMember { state }) 39 | } 40 | 41 | Self { members, crs } 42 | } 43 | 44 | pub fn members(&self) -> &[CommitteeMember] { 45 | &self.members 46 | } 47 | 48 | pub fn members_keys(&self) -> Vec { 49 | self.members() 50 | .iter() 51 | .map(|committee_member| committee_member.public_key()) 52 | .collect() 53 | } 54 | 55 | pub fn election_pk(&self) -> ElectionPublicKey { 56 | let keys: Vec<_> = self.members().iter().map(|x| x.public_key()).collect(); 57 | ElectionPublicKey::from_participants(&keys) 58 | } 59 | 60 | pub fn crs(&self) -> &Crs { 61 | &self.crs 62 | } 63 | } 64 | 65 | impl CommitteeMember { 66 | pub fn public_key(&self) -> MemberPublicKey { 67 | self.state.public_key() 68 | } 69 | 70 | pub fn secret_key(&self) -> &MemberSecretKey { 71 | self.state.secret_key() 72 | } 73 | 74 | pub fn produce_decrypt_shares( 75 | &self, 76 | vote_plan_status: &VotePlanStatus, 77 | ) -> Vec { 78 | vote_plan_status 79 | .proposals 80 | .iter() 81 | .map(|proposal| { 82 | let encrypted_tally = proposal.tally.private_encrypted().unwrap().clone(); 83 | encrypted_tally.partial_decrypt(&mut thread_rng(), self.secret_key()) 84 | }) 85 | .collect() 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/e2e/evm_transaction.rs: -------------------------------------------------------------------------------- 1 | use crate::testing::scenario::wallet; 2 | use crate::testing::TestGen; 3 | use crate::testing::{scenario::prepare_scenario, verifiers::LedgerStateVerifier, ConfigBuilder}; 4 | use crate::value::Value; 5 | use chain_evm::Config; 6 | 7 | const ALICE: &str = "Alice"; 8 | const BOB: &str = "Bob"; 9 | const INITIAL_FUNDS: u64 = 1000; 10 | const TRANSACTION_AMOUNT: u64 = 100; 11 | const MAX_GAS_FEE: u64 = u64::MAX; 12 | const FIRST_NONCE: u64 = 0; 13 | 14 | #[test] 15 | pub fn evm_transaction_call_no_data() { 16 | let (mut ledger, controller) = prepare_scenario() 17 | .with_initials(vec![ 18 | wallet(ALICE).with(INITIAL_FUNDS).owns("alice_stake_pool"), 19 | wallet(BOB).with(INITIAL_FUNDS).owns("bob_stake_pool"), 20 | ]) 21 | .with_config(ConfigBuilder::new().with_evm_params(Config::default())) 22 | .build() 23 | .unwrap(); 24 | 25 | let mut alice = controller.wallet(ALICE).unwrap(); 26 | let mut bob = controller.wallet(BOB).unwrap(); 27 | 28 | let alice_evm_mapping = TestGen::evm_mapping_for_wallet(&alice); 29 | let bob_evm_mapping = TestGen::evm_mapping_for_wallet(&bob); 30 | 31 | controller 32 | .evm_mapping(&alice, alice_evm_mapping, &mut ledger) 33 | .unwrap(); 34 | 35 | controller 36 | .evm_mapping(&bob, bob_evm_mapping, &mut ledger) 37 | .unwrap(); 38 | 39 | alice.confirm_transaction(); 40 | bob.confirm_transaction(); 41 | 42 | let alice_evm_address = ledger 43 | .get_evm_mapped_address(&alice.as_account().to_id()) 44 | .unwrap(); 45 | let bob_evm_address = ledger 46 | .get_evm_mapped_address(&bob.as_account().to_id()) 47 | .unwrap(); 48 | 49 | let evm_transaction = TestGen::evm_transaction( 50 | alice_evm_address, 51 | bob_evm_address, 52 | TRANSACTION_AMOUNT, 53 | MAX_GAS_FEE, 54 | FIRST_NONCE, 55 | ); 56 | 57 | controller 58 | .evm_transaction(evm_transaction, &mut ledger) 59 | .unwrap(); 60 | 61 | alice.confirm_transaction(); 62 | 63 | LedgerStateVerifier::new(ledger.clone().into()) 64 | .info("Alice final balance is incorrect.") 65 | .account_has_expected_balance( 66 | alice.as_account_data(), 67 | Value(INITIAL_FUNDS - TRANSACTION_AMOUNT), 68 | ); 69 | 70 | LedgerStateVerifier::new(ledger.clone().into()) 71 | .info("Bob final balance is incorrect.") 72 | .account_has_expected_balance( 73 | bob.as_account_data(), 74 | Value(INITIAL_FUNDS + TRANSACTION_AMOUNT), 75 | ); 76 | } 77 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/e2e/management_threshold.rs: -------------------------------------------------------------------------------- 1 | use crate::testing::{ 2 | scenario::{prepare_scenario, wallet}, 3 | verifiers::LedgerStateVerifier, 4 | }; 5 | 6 | #[test] 7 | pub fn management_threshold() { 8 | let (mut ledger, controller) = prepare_scenario() 9 | .with_initials(vec![ 10 | wallet("Alice").with(1_000).owns("stake_pool"), 11 | wallet("Bob").with(1_000).owns("stake_pool"), 12 | wallet("Clarice").with(1_000).owns("stake_pool"), 13 | wallet("David").with(1_000).owns("stake_pool"), 14 | ]) 15 | .build() 16 | .unwrap(); 17 | let alice = controller.wallet("Alice").unwrap(); 18 | let bob = controller.wallet("Bob").unwrap(); 19 | let stake_pool = controller.stake_pool("stake_pool").unwrap(); 20 | 21 | // by default we need owners/2 votes to update pool 22 | assert!(controller 23 | .retire(Some(&alice), &stake_pool, &mut ledger) 24 | .is_err()); 25 | 26 | LedgerStateVerifier::new(ledger.clone().into()) 27 | .info("after owner delegation") 28 | .stake_pools() 29 | .is_not_retired(&stake_pool); 30 | 31 | assert!(controller 32 | .retire(&[alice, bob], &stake_pool, &mut ledger) 33 | .is_ok()); 34 | 35 | LedgerStateVerifier::new(ledger.into()) 36 | .info("after owner delegation") 37 | .stake_pools() 38 | .is_retired(&stake_pool); 39 | } 40 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/e2e/mint_token.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | ledger::Error::MintingPolicyViolation, 3 | testing::{ 4 | ledger::ConfigBuilder, 5 | scenario::{prepare_scenario, wallet}, 6 | TestGen, 7 | }, 8 | tokens::minting_policy::MintingPolicyViolation::AdditionalMintingNotAllowed, 9 | }; 10 | 11 | const ALICE: &str = "ALICE"; 12 | 13 | #[test] 14 | pub fn mint_token_not_allowed_outside_block_0() { 15 | let (mut ledger, controller) = prepare_scenario() 16 | .with_config(ConfigBuilder::new()) 17 | .with_initials(vec![wallet(ALICE).with(1_000)]) 18 | .build() 19 | .unwrap(); 20 | 21 | let alice = controller.wallet(ALICE).unwrap(); 22 | 23 | assert_eq!( 24 | controller 25 | .mint_token( 26 | &alice, 27 | TestGen::mint_token_for_wallet(alice.public_key().into()), 28 | &mut ledger, 29 | ) 30 | .err() 31 | .unwrap(), 32 | MintingPolicyViolation(AdditionalMintingNotAllowed) 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/e2e/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "evm")] 2 | pub mod evm_mapping; 3 | #[cfg(feature = "evm")] 4 | pub mod evm_transaction; 5 | pub mod fees; 6 | pub mod management_threshold; 7 | pub mod mint_token; 8 | pub mod owner_delegation; 9 | pub mod pool_update; 10 | pub mod rewards; 11 | pub mod spending_counter_lanes; 12 | pub mod stake_distribution; 13 | pub mod transactions; 14 | pub mod update_proposal; 15 | pub mod vote_private; 16 | pub mod vote_public; 17 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/e2e/owner_delegation.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | fee::LinearFee, 3 | stake::Stake, 4 | testing::{ 5 | data::Wallet, 6 | ledger::ConfigBuilder, 7 | scenario::{prepare_scenario, wallet}, 8 | verifiers::LedgerStateVerifier, 9 | }, 10 | value::Value, 11 | }; 12 | use chain_addr::Discrimination; 13 | 14 | #[test] 15 | pub fn owner_delegation() { 16 | let (mut ledger, controller) = prepare_scenario() 17 | .with_config( 18 | ConfigBuilder::new() 19 | .with_discrimination(Discrimination::Test) 20 | .with_fee(LinearFee::new(1, 1, 1)), 21 | ) 22 | .with_initials(vec![ 23 | wallet("Alice").with(1_000), 24 | wallet("Bob").with(1_000).owns("stake_pool"), 25 | ]) 26 | .build() 27 | .unwrap(); 28 | let mut alice = controller.wallet("Alice").unwrap(); 29 | let stake_pool = controller.stake_pool("stake_pool").unwrap(); 30 | 31 | controller 32 | .owner_delegates(&alice, &stake_pool, &mut ledger) 33 | .unwrap(); 34 | alice.confirm_transaction(); 35 | 36 | LedgerStateVerifier::new(ledger.clone().into()) 37 | .info("after owner delegation") 38 | .distribution() 39 | .unassigned_is(Stake::from_value(Value(1000))) 40 | .and() 41 | .dangling_is(Stake::from_value(Value::zero())) 42 | .and() 43 | .pools_total_stake_is(Stake::from_value(Value(997))); 44 | 45 | controller.removes_delegation(&alice, &mut ledger).unwrap(); 46 | alice.confirm_transaction(); 47 | 48 | LedgerStateVerifier::new(ledger.into()) 49 | .info("after owner delegation removal") 50 | .distribution() 51 | .unassigned_is(Stake::from_value(Value(1994))) 52 | .and() 53 | .dangling_is(Stake::from_value(Value::zero())) 54 | .and() 55 | .pools_total_stake_is(Stake::from_value(Value::zero())); 56 | } 57 | 58 | #[test] 59 | pub fn delegation_for_non_existing_account() { 60 | let (mut ledger, controller) = prepare_scenario() 61 | .with_initials(vec![wallet("Alice").with(1_000).owns("stake_pool")]) 62 | .build() 63 | .unwrap(); 64 | 65 | let alice = controller.wallet("Alice").unwrap(); 66 | let bob = Wallet::from_value(Value(1_000)); 67 | let stake_pool = controller.stake_pool("stake_pool").unwrap(); 68 | 69 | assert!(controller 70 | .delegates_different_funder(&alice, &bob, &stake_pool, &mut ledger) 71 | .is_err()); 72 | 73 | LedgerStateVerifier::new(ledger.into()) 74 | .info("after owner delegation removal") 75 | .distribution() 76 | .unassigned_is(Stake::from_value(Value(1_000))) 77 | .and() 78 | .dangling_is(Stake::from_value(Value::zero())) 79 | .and() 80 | .pools_total_stake_is(Stake::from_value(Value::zero())); 81 | } 82 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/e2e/update_proposal.rs: -------------------------------------------------------------------------------- 1 | use crate::config::ConfigParam; 2 | use crate::fragment::ConfigParams; 3 | use crate::key::EitherEd25519SecretKey; 4 | use crate::{ 5 | certificate::{UpdateProposal, UpdateVote}, 6 | header::BlockDate, 7 | testing::{ 8 | ledger::ConfigBuilder, 9 | scenario::{prepare_scenario, wallet}, 10 | TestGen, 11 | }, 12 | }; 13 | use chain_addr::Discrimination; 14 | 15 | const ALICE: &str = "ALICE"; 16 | 17 | #[test] 18 | pub fn update_slot_duration() { 19 | let initial_slot_duration = 10; 20 | let final_slot_duration = 100; 21 | let leader_pair = TestGen::leader_pair(); 22 | let (mut ledger, controller) = prepare_scenario() 23 | .with_config( 24 | ConfigBuilder::new() 25 | .with_slot_duration(initial_slot_duration) 26 | .with_discrimination(Discrimination::Test) 27 | .with_leaders(&[leader_pair.id()]), 28 | ) 29 | .with_initials(vec![wallet(ALICE) 30 | .key(EitherEd25519SecretKey::Normal(leader_pair.key())) 31 | .with(1_000)]) 32 | .build() 33 | .unwrap(); 34 | 35 | let mut alice = controller.wallet(ALICE).unwrap(); 36 | let mut config_params = ConfigParams::new(); 37 | config_params.push(ConfigParam::SlotDuration(final_slot_duration)); 38 | 39 | let update_proposal = UpdateProposal::new(config_params, leader_pair.id()); 40 | 41 | let proposal_id = controller 42 | .update_proposal(&alice, update_proposal, &mut ledger) 43 | .unwrap(); 44 | alice.confirm_transaction(); 45 | 46 | controller 47 | .update_vote( 48 | &alice, 49 | UpdateVote::new(proposal_id, leader_pair.id()), 50 | &mut ledger, 51 | ) 52 | .unwrap(); 53 | alice.confirm_transaction(); 54 | 55 | ledger.fast_forward_to(BlockDate { 56 | epoch: 0, 57 | slot_id: 1, 58 | }); 59 | 60 | assert_eq!(initial_slot_duration, ledger.settings().slot_duration); 61 | 62 | assert!(ledger 63 | .apply_empty_bft_block_with_date( 64 | &leader_pair, 65 | BlockDate { 66 | epoch: 1, 67 | slot_id: 0, 68 | } 69 | ) 70 | .is_ok()); 71 | 72 | assert_eq!(final_slot_duration, ledger.settings().slot_duration); 73 | } 74 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arbitrary; 2 | pub mod builders; 3 | pub mod data; 4 | #[cfg(test)] 5 | pub mod e2e; 6 | mod gen; 7 | pub mod ledger; 8 | pub mod scenario; 9 | pub mod verifiers; 10 | pub use arbitrary::*; 11 | pub use builders::*; 12 | pub use data::KeysDb; 13 | pub use gen::{TestGen, VoteTestGen}; 14 | pub use ledger::{ConfigBuilder, LedgerBuilder, TestLedger, UtxoDb}; 15 | pub mod serialization; 16 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/scenario/mod.rs: -------------------------------------------------------------------------------- 1 | mod controller; 2 | mod fragment_factory; 3 | mod scenario_builder; 4 | pub mod template; 5 | 6 | pub use controller::Controller; 7 | pub use fragment_factory::FragmentFactory; 8 | pub use scenario_builder::{prepare_scenario, proposal, stake_pool, vote_plan, wallet}; 9 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/serialization.rs: -------------------------------------------------------------------------------- 1 | use chain_core::{ 2 | packer::Codec, 3 | property::{DeserializeFromSlice, Serialize}, 4 | }; 5 | use quickcheck::{Arbitrary, TestResult}; 6 | 7 | /// test that any arbitrary given object can serialize and deserialize 8 | /// back into itself (i.e. it is a bijection, or a one to one match 9 | /// between the serialized bytes and the object) 10 | pub fn serialization_bijection(t: T) -> TestResult 11 | where 12 | T: Arbitrary + Serialize + DeserializeFromSlice + Eq, 13 | { 14 | let vec = match t.serialize_as_vec() { 15 | Err(error) => return TestResult::error(format!("serialization: {}", error)), 16 | Ok(v) => v, 17 | }; 18 | let decoded_t = match T::deserialize_from_slice(&mut Codec::new(vec.as_slice())) { 19 | Err(error) => return TestResult::error(format!("deserialization: {:?}", error)), 20 | Ok(v) => v, 21 | }; 22 | assert_eq!(vec.len(), t.serialized_size()); 23 | TestResult::from_bool(decoded_t == t) 24 | } 25 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/testing/verifiers/mod.rs: -------------------------------------------------------------------------------- 1 | mod ledger_verifier; 2 | 3 | pub use ledger_verifier::LedgerStateVerifier; 4 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/tokens/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod identifier; 2 | pub mod minting_policy; 3 | pub mod name; 4 | pub mod policy_hash; 5 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/tokens/policy_hash.rs: -------------------------------------------------------------------------------- 1 | use chain_core::{ 2 | packer::Codec, 3 | property::{Deserialize, ReadError}, 4 | }; 5 | 6 | pub const POLICY_HASH_SIZE: usize = 28; 7 | 8 | /// blake2b_224 hash of a serialized minting policy 9 | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] 10 | #[cfg_attr( 11 | any(test, feature = "property-test-api"), 12 | derive(test_strategy::Arbitrary) 13 | )] 14 | pub struct PolicyHash([u8; POLICY_HASH_SIZE]); 15 | 16 | impl AsRef<[u8]> for PolicyHash { 17 | fn as_ref(&self) -> &[u8] { 18 | self.0.as_ref() 19 | } 20 | } 21 | 22 | impl From<[u8; POLICY_HASH_SIZE]> for PolicyHash { 23 | fn from(bytes: [u8; POLICY_HASH_SIZE]) -> Self { 24 | Self(bytes) 25 | } 26 | } 27 | 28 | impl TryFrom<&[u8]> for PolicyHash { 29 | type Error = ReadError; 30 | 31 | fn try_from(value: &[u8]) -> Result { 32 | Self::deserialize(&mut Codec::new(value)) 33 | } 34 | } 35 | 36 | impl Deserialize for PolicyHash { 37 | fn deserialize(codec: &mut Codec) -> Result { 38 | let bytes = codec 39 | .get_bytes(POLICY_HASH_SIZE)? 40 | .try_into() 41 | .unwrap_or_else(|_| panic!("already read {} bytes", POLICY_HASH_SIZE)); 42 | Ok(Self(bytes)) 43 | } 44 | } 45 | 46 | #[cfg(any(test, feature = "property-test-api"))] 47 | mod tests { 48 | use super::*; 49 | #[allow(unused_imports)] 50 | use quickcheck::TestResult; 51 | use quickcheck::{Arbitrary, Gen}; 52 | 53 | impl Arbitrary for PolicyHash { 54 | fn arbitrary(g: &mut G) -> Self { 55 | let mut bytes = [0u8; POLICY_HASH_SIZE]; 56 | for i in &mut bytes { 57 | *i = Arbitrary::arbitrary(g); 58 | } 59 | Self(bytes) 60 | } 61 | } 62 | 63 | #[quickcheck_macros::quickcheck] 64 | fn policy_hash_serialization_bijection(ph: PolicyHash) -> TestResult { 65 | let ph_got = ph.as_ref(); 66 | let mut codec = Codec::new(ph_got); 67 | let result = PolicyHash::deserialize(&mut codec).unwrap(); 68 | TestResult::from_bool(ph == result) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/transaction/mod.rs: -------------------------------------------------------------------------------- 1 | mod builder; 2 | mod element; 3 | mod input; 4 | mod io; 5 | mod payload; 6 | #[allow(clippy::module_inception)] 7 | mod transaction; 8 | mod transfer; 9 | mod utxo; 10 | mod witness; 11 | 12 | #[cfg(any(test, feature = "property-test-api"))] 13 | pub mod test; 14 | 15 | use chain_core::{ 16 | packer::Codec, 17 | property::{Deserialize, ReadError, Serialize, WriteError}, 18 | }; 19 | 20 | // to remove.. 21 | pub use builder::{ 22 | SetAuthData, SetIOs, SetPayload, SetTtl, SetWitnesses, TxBuilder, TxBuilderState, 23 | }; 24 | pub use element::*; 25 | pub use input::*; 26 | pub use io::{Error, InputOutput, InputOutputBuilder, OutputPolicy}; 27 | pub use payload::{NoExtra, Payload, PayloadAuthData, PayloadAuthSlice, PayloadData, PayloadSlice}; 28 | pub use transaction::*; 29 | pub use transfer::*; 30 | pub use utxo::*; 31 | pub use witness::*; 32 | 33 | impl Serialize for Transaction { 34 | fn serialized_size(&self) -> usize { 35 | self.as_ref().len() 36 | } 37 | 38 | fn serialize(&self, codec: &mut Codec) -> Result<(), WriteError> { 39 | codec.put_bytes(self.as_ref()) 40 | } 41 | } 42 | 43 | impl Deserialize for Transaction { 44 | fn deserialize(codec: &mut Codec) -> Result { 45 | let mut buf = Vec::new(); 46 | // TODO: implicitly define size of the Transaction object in the deserialize function, do not use read_to_end, 47 | // it narrows the usage of the deserialize trait for the Transaction struct, 48 | // which is not obvious from the Deserialze trait description, so leads to mistakes 49 | codec.read_to_end(&mut buf)?; 50 | let utx = UnverifiedTransactionSlice::from(buf.as_slice()); 51 | match utx.check() { 52 | Ok(tx) => Ok(tx.to_owned()), 53 | Err(e) => Err(ReadError::StructureInvalid(e.to_string())), 54 | } 55 | } 56 | } 57 | 58 | // TEMPORARY 59 | pub type AuthenticatedTransaction

= Transaction

; 60 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/transaction/transfer.rs: -------------------------------------------------------------------------------- 1 | use crate::legacy::OldAddress; 2 | use crate::value::*; 3 | use chain_core::{ 4 | packer::Codec, 5 | property::{Deserialize, ReadError}, 6 | }; 7 | 8 | /// Information how tokens are spent. 9 | /// A value of tokens is sent to the address. 10 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] 11 | pub struct Output

{ 12 | pub address: Address, 13 | pub value: Value, 14 | } 15 | 16 | impl
Output
{ 17 | pub fn from_address(address: Address, value: Value) -> Self { 18 | Output { address, value } 19 | } 20 | } 21 | 22 | impl Deserialize for Output
{ 23 | fn deserialize(codec: &mut Codec) -> Result { 24 | let address = Address::deserialize(codec)?; 25 | let value = Value::deserialize(codec)?; 26 | Ok(Output { address, value }) 27 | } 28 | } 29 | 30 | impl std::fmt::Display for Output { 31 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 32 | write!(f, "{}.{}", self.address.base32(), self.value) 33 | } 34 | } 35 | 36 | impl std::fmt::Display for Output { 37 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 38 | write!(f, "{}.{}", self.address, self.value) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/transaction/utxo.rs: -------------------------------------------------------------------------------- 1 | use crate::fragment::FragmentId; 2 | use crate::value::*; 3 | 4 | pub type TransactionIndex = u8; 5 | 6 | /// Unspent transaction pointer. 7 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 8 | pub struct UtxoPointer { 9 | /// the transaction identifier where the unspent output is 10 | pub transaction_id: FragmentId, 11 | /// the output index within the pointed transaction's outputs 12 | pub output_index: TransactionIndex, 13 | /// the value we expect to read from this output 14 | /// 15 | /// This setting is added in order to protect undesired withdrawal 16 | /// and to set the actual fee in the transaction. 17 | pub value: Value, 18 | } 19 | 20 | impl UtxoPointer { 21 | pub fn new(transaction_id: FragmentId, output_index: TransactionIndex, value: Value) -> Self { 22 | UtxoPointer { 23 | transaction_id, 24 | output_index, 25 | value, 26 | } 27 | } 28 | } 29 | 30 | impl std::fmt::Display for UtxoPointer { 31 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 32 | write!( 33 | f, 34 | "{}@{}.{}", 35 | self.transaction_id, self.output_index, self.value 36 | ) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/treasury.rs: -------------------------------------------------------------------------------- 1 | use crate::ledger::Error; 2 | use crate::value::Value; 3 | 4 | /// An amount of value owned by the treasury. 5 | /// 6 | /// Right now, it doesn't have any mechanism to 7 | /// withdraw money from, so it serves just to 8 | /// record a monotically increasing special account. 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 10 | pub struct Treasury(Value); 11 | 12 | impl Treasury { 13 | /// Create a treasury with an initial value 14 | pub fn initial(v: Value) -> Self { 15 | Self(v) 16 | } 17 | 18 | pub fn draw(&mut self, value: Value) -> Value { 19 | let to_draw = std::cmp::min(value, self.0); 20 | (self.0).0 -= to_draw.0; 21 | to_draw 22 | } 23 | 24 | /// Add some value in the treasury 25 | pub fn add(&mut self, v: Value) -> Result<(), Error> { 26 | self.0 = (self.0 + v).map_err(|error| Error::PotValueInvalid { error })?; 27 | Ok(()) 28 | } 29 | 30 | /// remove some values from the treasury 31 | pub fn sub(&mut self, value: Value) -> Result<(), Error> { 32 | self.0 = self 33 | .0 34 | .checked_sub(value) 35 | .map_err(|error| Error::PotValueInvalid { error })?; 36 | Ok(()) 37 | } 38 | 39 | /// Get value in the treasury 40 | pub fn value(self) -> Value { 41 | self.0 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use super::Treasury; 48 | use crate::ledger::Error; 49 | use crate::value::{Value, ValueError}; 50 | use quickcheck::{Arbitrary, Gen}; 51 | 52 | impl Arbitrary for Treasury { 53 | fn arbitrary(g: &mut G) -> Self { 54 | Treasury::initial(Arbitrary::arbitrary(g)) 55 | } 56 | } 57 | 58 | #[test] 59 | pub fn draw_from_treasure() { 60 | let mut treasury = Treasury::initial(Value(100)); 61 | assert_eq!(treasury.draw(Value(60)), Value(60)); 62 | assert_eq!(treasury.value(), Value(40)); 63 | 64 | assert_eq!(treasury.draw(Value(50)), Value(40)); 65 | assert_eq!(treasury.value(), Value::zero()); 66 | } 67 | 68 | #[test] 69 | pub fn sub_from_treasure() { 70 | let mut treasury = Treasury::initial(Value(100)); 71 | assert!(treasury.sub(Value(60)).is_ok()); 72 | assert_eq!(treasury.value(), Value(40)); 73 | 74 | assert_eq!(treasury.value(), Value(40)); 75 | 76 | assert_eq!( 77 | treasury.sub(Value(50)), 78 | Err(Error::PotValueInvalid { 79 | error: ValueError::NegativeAmount 80 | }) 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/vote/mod.rs: -------------------------------------------------------------------------------- 1 | //! cover all that relate to the voting part of things 2 | //! (except for the certificate that are in the certificate 3 | //! module). 4 | //! 5 | 6 | mod choice; 7 | mod committee; 8 | mod ledger; 9 | mod manager; 10 | mod payload; 11 | mod privacy; 12 | mod status; 13 | mod tally; 14 | 15 | pub use self::{ 16 | choice::{Choice, Options}, 17 | committee::CommitteeId, 18 | ledger::{VotePlanLedger, VotePlanLedgerError}, 19 | manager::{ValidatedPayload, VoteError, VotePlanManager}, 20 | payload::{EncryptedVote, Payload, PayloadType, ProofOfCorrectVote, TryFromIntError}, 21 | privacy::encrypt_vote, 22 | status::{VotePlanStatus, VoteProposalStatus}, 23 | tally::{PrivateTallyState, Tally, TallyError, TallyResult, Weight}, 24 | }; 25 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/vote/privacy.rs: -------------------------------------------------------------------------------- 1 | use crate::vote::{EncryptedVote, ProofOfCorrectVote}; 2 | use chain_vote::{Crs, ElectionPublicKey, Vote}; 3 | use rand_core::{CryptoRng, RngCore}; 4 | 5 | #[allow(dead_code)] 6 | pub fn encrypt_vote( 7 | rng: &mut R, 8 | crs: &Crs, 9 | public_key: &ElectionPublicKey, 10 | vote: Vote, 11 | ) -> (EncryptedVote, ProofOfCorrectVote) { 12 | let (ev, proof) = public_key.encrypt_and_prove_vote(rng, crs, vote); 13 | ( 14 | EncryptedVote::from_inner(ev), 15 | ProofOfCorrectVote::from_inner(proof), 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /chain-impl-mockchain/src/vote/status.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | account, 3 | certificate::{ExternalProposalId, VotePlanId}, 4 | date::BlockDate, 5 | tokens::identifier::TokenIdentifier, 6 | vote::{Options, PayloadType, Tally}, 7 | }; 8 | use chain_vote::MemberPublicKey; 9 | use imhamt::Hamt; 10 | use std::collections::hash_map::DefaultHasher; 11 | 12 | pub struct VotePlanStatus { 13 | pub id: VotePlanId, 14 | pub payload: PayloadType, 15 | pub vote_start: BlockDate, 16 | pub vote_end: BlockDate, 17 | pub committee_end: BlockDate, 18 | pub committee_public_keys: Vec, 19 | pub proposals: Vec, 20 | pub voting_token: TokenIdentifier, 21 | } 22 | 23 | pub struct VoteProposalStatus { 24 | pub index: u8, 25 | pub proposal_id: ExternalProposalId, 26 | pub options: Options, 27 | pub tally: Tally, 28 | pub votes: Hamt, 29 | } 30 | -------------------------------------------------------------------------------- /chain-network/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-network" 3 | version = "0.1.0" 4 | authors = ["Mikhail Zabaluev "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [dependencies] 9 | chain-crypto = { path = "../chain-crypto" } 10 | async-trait = "0.1" 11 | futures = "0.3" 12 | http-body = "0.4" 13 | pin-project = "1.0" 14 | prost = "0.9" 15 | rand_core = "0.6" 16 | thiserror = "1.0" 17 | 18 | [dependencies.tonic] 19 | version = "0.6" 20 | default-features = false 21 | features = ["codegen", "prost"] 22 | 23 | [dev-dependencies] 24 | rand = "0.8" 25 | 26 | [build-dependencies.tonic-build] 27 | version = "0.6" 28 | default-features = false 29 | features = ["prost"] 30 | 31 | [features] 32 | default = ["transport", "legacy"] 33 | transport = ["tonic/transport", "tonic-build/transport"] 34 | legacy = [] 35 | codegen-rustfmt = ["tonic-build/rustfmt"] 36 | -------------------------------------------------------------------------------- /chain-network/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tonic_build::compile_protos("proto/node.proto").unwrap(); 3 | tonic_build::compile_protos("proto/watch.proto").unwrap(); 4 | } 5 | -------------------------------------------------------------------------------- /chain-network/proto/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | // Message types shared by protocol definitions 4 | package iohk.chain.types; 5 | 6 | // Representation of a block. 7 | message Block { 8 | // The serialized content of the block. 9 | bytes content = 1; 10 | } 11 | 12 | // Representation of a block header. 13 | message Header { 14 | // The serialized content of the block header. 15 | bytes content = 1; 16 | } 17 | 18 | // Representation of a block fragment, that is, a transaction or other 19 | // content item. 20 | message Fragment { 21 | // The serialized content of the fragment. 22 | bytes content = 1; 23 | } 24 | 25 | // A sequence of block identifiers used in fetch requests and solicitation. 26 | message BlockIds { 27 | // The identifiers of blocks for loading. 28 | repeated bytes ids = 1; 29 | } 30 | 31 | // A sequence of fragment identifiers used in fetch requests. 32 | message FragmentIds { 33 | // The identifiers of fragments. 34 | repeated bytes ids = 1; 35 | } 36 | -------------------------------------------------------------------------------- /chain-network/proto/watch.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "types.proto"; 4 | 5 | package iohk.chain.watch; 6 | 7 | message BlockSubscriptionRequest {} 8 | 9 | message TipSubscriptionRequest {} 10 | 11 | message MempoolSubscriptionRequest {} 12 | 13 | message SyncMultiverseRequest { 14 | // Block IDs to start synchronization from. 15 | // 16 | // The client should examine previously synchronized blockchain storage, 17 | // and include the ID of the latest block at or earlier than 18 | // the stability depth in that saved state, as well as tips of all valid 19 | // branches existing in that state. 20 | // 21 | // The server should prune the block IDs in the list, discarding any block IDs 22 | // not found in the blockchain storage (which might be on discarded branches), 23 | // as well as any blocks that are ancestors of other blocks identified in 24 | // the list. The server responds with a stream sending any 25 | // valid blocks on the (possibly forked) chain that are descendants of 26 | // the remaining blocks in the list, unless the list is empty, in which case 27 | // the server sends all blocks starting from genesis. 28 | // The stream should uphold the parent-to-child order of the blocks, 29 | // i.e. no block gets sent before its parent. 30 | repeated bytes from = 1; 31 | } 32 | 33 | service Watch { 34 | // get a stream of blocks succesfully processed by the node, this means they 35 | // are already validated. 36 | // the parent of a block will always be streamed before the block itself. 37 | rpc BlockSubscription(BlockSubscriptionRequest) returns (stream types.Block); 38 | 39 | // get tip updates 40 | rpc TipSubscription(TipSubscriptionRequest) returns (stream types.Header); 41 | 42 | // Fetch all blocks from the blockchain storage that are descendant from 43 | // the given checkpoints, The blocks are fetched from all possible branches, 44 | // observing parent-to-child order. The order in which any two blocks from 45 | // divergent branches are sent is not specified and 46 | // should not be relied upon. 47 | rpc SyncMultiverse(SyncMultiverseRequest) returns (stream types.Block); 48 | } 49 | -------------------------------------------------------------------------------- /chain-network/src/core/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod server; 2 | pub mod watch; 3 | -------------------------------------------------------------------------------- /chain-network/src/core/server/fragment.rs: -------------------------------------------------------------------------------- 1 | use super::PushStream; 2 | use crate::data::{Fragment, FragmentIds, Peer}; 3 | use crate::error::Error; 4 | use async_trait::async_trait; 5 | use futures::prelude::*; 6 | 7 | /// Interface for the blockchain node service implementation responsible for 8 | /// exchanging fragments that make up a future block. 9 | #[async_trait] 10 | pub trait FragmentService { 11 | /// The type of an asynchronous stream that provides blocks in 12 | /// response to `get_fragments` method. 13 | type GetFragmentsStream: Stream> + Send + Sync; 14 | 15 | /// Serves a request to retrieve blocks identified by the list of `ids` 16 | /// Resloves to a stream of blocks to send to the remote client peer. 17 | async fn get_fragments(&self, ids: FragmentIds) -> Result; 18 | 19 | /// The type of outbound asynchronous streams returned by the 20 | /// `subscription` method. 21 | type SubscriptionStream: Stream> + Send + Sync; 22 | 23 | /// Called by the protocol implementation to establish a 24 | /// bidirectional subscription stream. 25 | /// The inbound stream is passed to the asynchronous method, 26 | /// which resolves to the outbound stream. 27 | async fn fragment_subscription( 28 | &self, 29 | subscriber: Peer, 30 | stream: PushStream, 31 | ) -> Result; 32 | } 33 | -------------------------------------------------------------------------------- /chain-network/src/core/server/gossip.rs: -------------------------------------------------------------------------------- 1 | use super::PushStream; 2 | use crate::data::{Gossip, Peer}; 3 | use crate::error::Error; 4 | use async_trait::async_trait; 5 | use futures::stream::Stream; 6 | 7 | /// Interface for the blockchain node service implementation responsible for 8 | /// exchanging P2P network gossip. 9 | #[async_trait] 10 | pub trait GossipService { 11 | async fn peers(&self, limit: u32) -> Result; 12 | 13 | /// The type of outbound asynchronous streams returned by the 14 | /// `subscription` method. 15 | type SubscriptionStream: Stream> + Send + Sync; 16 | 17 | /// Called by the protocol implementation to establish a 18 | /// bidirectional subscription stream. 19 | /// The inbound stream is passed to the asynchronous method, 20 | /// which resolves to the outbound stream. 21 | async fn gossip_subscription( 22 | &self, 23 | subscriber: Peer, 24 | stream: PushStream, 25 | ) -> Result; 26 | } 27 | -------------------------------------------------------------------------------- /chain-network/src/core/server/mod.rs: -------------------------------------------------------------------------------- 1 | mod block; 2 | mod fragment; 3 | mod gossip; 4 | mod node; 5 | mod push; 6 | 7 | pub use block::BlockService; 8 | pub use fragment::FragmentService; 9 | pub use gossip::GossipService; 10 | 11 | pub use node::Node; 12 | 13 | pub use push::PushStream; 14 | -------------------------------------------------------------------------------- /chain-network/src/core/server/node.rs: -------------------------------------------------------------------------------- 1 | use super::{BlockService, FragmentService, GossipService}; 2 | use crate::data::p2p::{AuthenticatedNodeId, Peer}; 3 | use crate::data::HandshakeResponse; 4 | use crate::error::Error; 5 | use async_trait::async_trait; 6 | 7 | /// Interface to application logic of the blockchain node server. 8 | /// 9 | /// An implementation of a blockchain node implements this trait to 10 | /// serve the network protocols using node's subsystems such as 11 | /// block storage and fragment processor. 12 | #[async_trait] 13 | pub trait Node: Send + Sync + 'static { 14 | /// The implementation of the block service. 15 | type BlockService: BlockService + Send + Sync; 16 | 17 | /// The implementation of the fragment service. 18 | type FragmentService: FragmentService + Send + Sync; 19 | 20 | /// The implementation of the gossip service. 21 | type GossipService: GossipService + Send + Sync; 22 | 23 | /// Implements node handshake. The server returns the ID of the genesis 24 | /// block and its own node ID, authenticated with the signature of `nonce`. 25 | async fn handshake(&self, peer: Peer, nonce: &[u8]) -> Result; 26 | 27 | /// Handles client ID authentication. 28 | async fn client_auth(&self, peer: Peer, auth: AuthenticatedNodeId) -> Result<(), Error>; 29 | 30 | /// Instantiates the block service, 31 | /// if supported by this node. 32 | fn block_service(&self) -> Option<&Self::BlockService>; 33 | 34 | /// Instantiates the fragment service, 35 | /// if supported by this node. 36 | fn fragment_service(&self) -> Option<&Self::FragmentService>; 37 | 38 | /// Instantiates the gossip service, 39 | /// if supported by this node. 40 | fn gossip_service(&self) -> Option<&Self::GossipService>; 41 | } 42 | -------------------------------------------------------------------------------- /chain-network/src/core/server/push.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | 3 | /// Type alias for inbound stream objects passed to the application. 4 | pub type PushStream = futures::stream::BoxStream<'static, Result>; 5 | -------------------------------------------------------------------------------- /chain-network/src/core/watch/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod server; 2 | -------------------------------------------------------------------------------- /chain-network/src/core/watch/server.rs: -------------------------------------------------------------------------------- 1 | use crate::data::{Block, BlockIds, Header}; 2 | use crate::error::Error; 3 | 4 | use async_trait::async_trait; 5 | use futures::prelude::*; 6 | 7 | #[async_trait] 8 | pub trait Watch: Send + Sync + 'static { 9 | type BlockSubscriptionStream: Stream> + Send + Sync; 10 | 11 | async fn block_subscription(&self) -> Result; 12 | 13 | type TipSubscriptionStream: Stream> + Send + Sync; 14 | 15 | async fn tip_subscription(&self) -> Result; 16 | 17 | type SyncMultiverseStream: Stream> + Send + Sync; 18 | 19 | async fn sync_multiverse(&self, from: BlockIds) -> Result; 20 | } 21 | -------------------------------------------------------------------------------- /chain-network/src/data/block/block.rs: -------------------------------------------------------------------------------- 1 | /// A block in the byte array representation. 2 | #[derive(Clone)] 3 | pub struct Block(Box<[u8]>); 4 | 5 | impl Block { 6 | #[inline] 7 | pub fn from_bytes>>(bytes: B) -> Self { 8 | Block(bytes.into()) 9 | } 10 | 11 | #[inline] 12 | pub fn as_bytes(&self) -> &[u8] { 13 | &self.0 14 | } 15 | 16 | #[inline] 17 | pub fn into_bytes(self) -> Vec { 18 | self.0.into() 19 | } 20 | } 21 | 22 | impl AsRef<[u8]> for Block { 23 | #[inline] 24 | fn as_ref(&self) -> &[u8] { 25 | self.as_bytes() 26 | } 27 | } 28 | 29 | impl From for Vec { 30 | #[inline] 31 | fn from(block: Block) -> Self { 32 | block.into_bytes() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chain-network/src/data/block/header.rs: -------------------------------------------------------------------------------- 1 | /// A block header in the byte array representation. 2 | #[derive(Clone, Debug)] 3 | pub struct Header(Box<[u8]>); 4 | 5 | impl Header { 6 | #[inline] 7 | pub fn from_bytes>>(bytes: B) -> Self { 8 | Header(bytes.into()) 9 | } 10 | 11 | #[inline] 12 | pub fn as_bytes(&self) -> &[u8] { 13 | &self.0 14 | } 15 | 16 | #[inline] 17 | pub fn into_bytes(self) -> Vec { 18 | self.0.into() 19 | } 20 | } 21 | 22 | impl AsRef<[u8]> for Header { 23 | #[inline] 24 | fn as_ref(&self) -> &[u8] { 25 | self.as_bytes() 26 | } 27 | } 28 | 29 | impl From
for Vec { 30 | #[inline] 31 | fn from(header: Header) -> Self { 32 | header.into_bytes() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chain-network/src/data/block/id.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Code, Error}; 2 | 3 | use std::fmt; 4 | 5 | const BLOCK_ID_LEN: usize = 32; 6 | 7 | /// Network representation of a block ID. 8 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 9 | pub struct BlockId([u8; BLOCK_ID_LEN]); 10 | 11 | pub type BlockIds = Box<[BlockId]>; 12 | 13 | impl fmt::Debug for BlockId { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 15 | f.write_str("BlockId(0x")?; 16 | for byte in self.0.iter() { 17 | write!(f, "{:02x}", byte)?; 18 | } 19 | f.write_str(")") 20 | } 21 | } 22 | 23 | impl BlockId { 24 | #[inline] 25 | pub fn as_bytes(&self) -> &[u8] { 26 | &self.0 27 | } 28 | } 29 | 30 | impl AsRef<[u8]> for BlockId { 31 | #[inline] 32 | fn as_ref(&self) -> &[u8] { 33 | self.as_bytes() 34 | } 35 | } 36 | 37 | impl TryFrom<&[u8]> for BlockId { 38 | type Error = Error; 39 | 40 | fn try_from(src: &[u8]) -> Result { 41 | match TryFrom::try_from(src) { 42 | Ok(data) => Ok(BlockId(data)), 43 | Err(_) => Err(Error::new( 44 | Code::InvalidArgument, 45 | format!("block identifier must be {} bytes long", BLOCK_ID_LEN), 46 | )), 47 | } 48 | } 49 | } 50 | 51 | pub fn try_ids_from_iter(iter: I) -> Result 52 | where 53 | I: IntoIterator, 54 | I::Item: AsRef<[u8]>, 55 | { 56 | try_ids_from_iter_desugared(iter.into_iter()) 57 | } 58 | 59 | fn try_ids_from_iter_desugared(iter: I) -> Result 60 | where 61 | I: Iterator, 62 | I::Item: AsRef<[u8]>, 63 | { 64 | let ids = iter 65 | .map(|item| BlockId::try_from(item.as_ref())) 66 | .collect::, _>>()?; 67 | Ok(ids.into()) 68 | } 69 | -------------------------------------------------------------------------------- /chain-network/src/data/block/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::module_inception)] 2 | mod block; 3 | mod header; 4 | mod id; 5 | mod subscription; 6 | 7 | pub use block::Block; 8 | pub use header::Header; 9 | pub use id::{try_ids_from_iter, BlockId, BlockIds}; 10 | pub use subscription::{BlockEvent, ChainPullRequest}; 11 | -------------------------------------------------------------------------------- /chain-network/src/data/block/subscription.rs: -------------------------------------------------------------------------------- 1 | use super::{BlockId, BlockIds, Header}; 2 | 3 | /// An event sent from client to server over the block subscription stream. 4 | #[derive(Debug)] 5 | pub enum BlockEvent { 6 | /// Announcement of a new block in the chain. 7 | Announce(Header), 8 | /// Request to upload the identified blocks. 9 | Solicit(BlockIds), 10 | /// Request to push a chain of headers. 11 | Missing(ChainPullRequest), 12 | } 13 | 14 | /// A request to send headers in the block chain sequence. 15 | #[derive(Debug)] 16 | pub struct ChainPullRequest { 17 | /// A list of starting points known by the requester. 18 | /// The sender should pick the latest one. 19 | pub from: BlockIds, 20 | /// The identifier of the last block to send the header for. 21 | pub to: BlockId, 22 | } 23 | -------------------------------------------------------------------------------- /chain-network/src/data/fragment/fragment.rs: -------------------------------------------------------------------------------- 1 | /// A block fragment in the byte array representation. 2 | #[derive(Clone)] 3 | pub struct Fragment(Box<[u8]>); 4 | 5 | impl Fragment { 6 | #[inline] 7 | pub fn from_bytes>>(bytes: B) -> Self { 8 | Fragment(bytes.into()) 9 | } 10 | 11 | #[inline] 12 | pub fn as_bytes(&self) -> &[u8] { 13 | &self.0 14 | } 15 | 16 | #[inline] 17 | pub fn into_bytes(self) -> Vec { 18 | self.0.into() 19 | } 20 | } 21 | 22 | impl AsRef<[u8]> for Fragment { 23 | #[inline] 24 | fn as_ref(&self) -> &[u8] { 25 | self.as_bytes() 26 | } 27 | } 28 | 29 | impl From for Vec { 30 | #[inline] 31 | fn from(block: Fragment) -> Self { 32 | block.into_bytes() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chain-network/src/data/fragment/id.rs: -------------------------------------------------------------------------------- 1 | use crate::error::{Code, Error}; 2 | 3 | use std::fmt; 4 | 5 | const FRAGMENT_ID_LEN: usize = 32; 6 | 7 | /// Network representation of a fragment ID. 8 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 9 | pub struct FragmentId([u8; FRAGMENT_ID_LEN]); 10 | 11 | pub type FragmentIds = Box<[FragmentId]>; 12 | 13 | impl fmt::Debug for FragmentId { 14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 15 | f.write_str("FragmentId(0x")?; 16 | for byte in self.0.iter() { 17 | write!(f, "{:02x}", byte)?; 18 | } 19 | f.write_str(")") 20 | } 21 | } 22 | 23 | impl FragmentId { 24 | #[inline] 25 | pub fn as_bytes(&self) -> &[u8] { 26 | &self.0 27 | } 28 | } 29 | 30 | impl AsRef<[u8]> for FragmentId { 31 | #[inline] 32 | fn as_ref(&self) -> &[u8] { 33 | self.as_bytes() 34 | } 35 | } 36 | 37 | impl TryFrom<&[u8]> for FragmentId { 38 | type Error = Error; 39 | 40 | fn try_from(src: &[u8]) -> Result { 41 | match TryFrom::try_from(src) { 42 | Ok(data) => Ok(FragmentId(data)), 43 | Err(_) => Err(Error::new( 44 | Code::InvalidArgument, 45 | format!("fragment identifier must be {} bytes long", FRAGMENT_ID_LEN), 46 | )), 47 | } 48 | } 49 | } 50 | 51 | pub fn try_ids_from_iter(iter: I) -> Result, Error> 52 | where 53 | I: IntoIterator, 54 | I::Item: AsRef<[u8]>, 55 | { 56 | try_ids_from_iter_desugared(iter.into_iter()) 57 | } 58 | 59 | fn try_ids_from_iter_desugared(iter: I) -> Result 60 | where 61 | I: Iterator, 62 | I::Item: AsRef<[u8]>, 63 | { 64 | let ids = iter 65 | .map(|item| FragmentId::try_from(item.as_ref())) 66 | .collect::, _>>()?; 67 | Ok(ids.into()) 68 | } 69 | -------------------------------------------------------------------------------- /chain-network/src/data/fragment/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(clippy::module_inception)] 2 | mod fragment; 3 | mod id; 4 | 5 | pub use fragment::Fragment; 6 | pub use id::{try_ids_from_iter, FragmentId, FragmentIds}; 7 | -------------------------------------------------------------------------------- /chain-network/src/data/gossip/mod.rs: -------------------------------------------------------------------------------- 1 | mod node; 2 | 3 | pub use node::{Gossip, Node, Nodes}; 4 | -------------------------------------------------------------------------------- /chain-network/src/data/gossip/node.rs: -------------------------------------------------------------------------------- 1 | /// A gossip node in an opaque byte array representation. 2 | #[derive(Clone)] 3 | pub struct Node(Box<[u8]>); 4 | 5 | pub type Nodes = Box<[Node]>; 6 | 7 | impl Node { 8 | #[inline] 9 | pub fn from_bytes>>(bytes: B) -> Self { 10 | Node(bytes.into()) 11 | } 12 | 13 | #[inline] 14 | pub fn as_bytes(&self) -> &[u8] { 15 | &self.0 16 | } 17 | 18 | #[inline] 19 | pub fn into_bytes(self) -> Vec { 20 | self.0.into() 21 | } 22 | } 23 | 24 | impl AsRef<[u8]> for Node { 25 | #[inline] 26 | fn as_ref(&self) -> &[u8] { 27 | self.as_bytes() 28 | } 29 | } 30 | 31 | impl From for Vec { 32 | #[inline] 33 | fn from(block: Node) -> Self { 34 | block.into_bytes() 35 | } 36 | } 37 | 38 | pub struct Gossip { 39 | pub nodes: Nodes, 40 | } 41 | -------------------------------------------------------------------------------- /chain-network/src/data/handshake.rs: -------------------------------------------------------------------------------- 1 | use super::block::BlockId; 2 | use super::p2p::AuthenticatedNodeId; 3 | 4 | pub struct HandshakeResponse { 5 | pub block0_id: BlockId, 6 | pub auth: AuthenticatedNodeId, 7 | pub nonce: Box<[u8]>, 8 | } 9 | -------------------------------------------------------------------------------- /chain-network/src/data/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block; 2 | pub mod fragment; 3 | pub mod gossip; 4 | mod handshake; 5 | pub mod p2p; 6 | 7 | pub use block::{Block, BlockEvent, BlockId, BlockIds, Header}; 8 | pub use fragment::{Fragment, FragmentId, FragmentIds}; 9 | pub use gossip::Gossip; 10 | pub use handshake::HandshakeResponse; 11 | pub use p2p::{AuthenticatedNodeId, NodeId, NodeKeyPair, Peer}; 12 | -------------------------------------------------------------------------------- /chain-network/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fmt}; 2 | 3 | /// Common error codes for network protocol requests. 4 | /// 5 | /// These codes mimic the status codes used in gRPC and map one to one to 6 | /// those in the gRPC protocol implementation. 7 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 8 | pub enum Code { 9 | Canceled, 10 | Unknown, 11 | InvalidArgument, 12 | NotFound, 13 | FailedPrecondition, 14 | Aborted, 15 | Unimplemented, 16 | Internal, 17 | Unavailable, 18 | } 19 | 20 | /// Represents errors that can be returned by the node protocol implementation. 21 | #[derive(Debug)] 22 | pub struct Error { 23 | code: Code, 24 | source: Box, 25 | } 26 | 27 | impl Error { 28 | pub fn new(code: Code, source: E) -> Self 29 | where 30 | E: Into>, 31 | { 32 | Error { 33 | code, 34 | source: source.into(), 35 | } 36 | } 37 | 38 | pub fn unimplemented() -> Self { 39 | Error::new(Code::Unimplemented, "not yet implemented") 40 | } 41 | 42 | pub fn code(&self) -> Code { 43 | self.code 44 | } 45 | } 46 | 47 | impl error::Error for Error { 48 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { 49 | Some(self.source.as_ref()) 50 | } 51 | } 52 | 53 | impl fmt::Display for Error { 54 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 55 | let msg = match self.code { 56 | Code::Canceled => "processing canceled", 57 | Code::Unknown => "unknown error", 58 | Code::InvalidArgument => "invalid request data", 59 | Code::NotFound => "not found", 60 | Code::FailedPrecondition => "system state does not permit the operation", 61 | Code::Aborted => "the operation was aborted", 62 | Code::Unimplemented => "not implemented", 63 | Code::Internal => "internal processing error", 64 | Code::Unavailable => "the service is unavailable", 65 | }; 66 | write!(f, "{} ({})", msg, self.source) 67 | } 68 | } 69 | 70 | /// An error that the future returned by the `handshake` method can 71 | /// resolve to. 72 | #[derive(Debug, thiserror::Error)] 73 | pub enum HandshakeError { 74 | /// Error occurred with the protocol request. 75 | #[error("{0}")] 76 | Rpc(#[source] Error), 77 | /// The protocol version reported by the server is not supported. 78 | /// Carries the reported version in a human-readable form. 79 | #[error("unsupported protocol version {0}")] 80 | UnsupportedVersion(Box), 81 | #[error("invalid genesis block payload")] 82 | InvalidBlock0(#[source] Error), 83 | #[error("invalid node ID")] 84 | InvalidNodeId(#[source] Error), 85 | #[error("invalid node signature format")] 86 | MalformedSignature(#[source] Error), 87 | } 88 | -------------------------------------------------------------------------------- /chain-network/src/grpc/legacy.rs: -------------------------------------------------------------------------------- 1 | //! Support for legacy features. 2 | 3 | use rand_core::RngCore; 4 | use std::array::TryFromSliceError; 5 | use std::fmt; 6 | 7 | const NODE_ID_LEN: usize = 24; 8 | 9 | /// Represents a randomly generated node ID such as was present in subscription 10 | /// requests and responses in Jormungandr versions prior to 0.9 11 | /// (as implemented in the old `network-grpc` crate). 12 | #[derive(Copy, Clone, PartialEq, Eq, Hash)] 13 | pub struct NodeId([u8; NODE_ID_LEN]); 14 | 15 | impl NodeId { 16 | pub fn generate(rng: &mut impl RngCore) -> Result { 17 | let mut bytes = [0; NODE_ID_LEN]; 18 | rng.try_fill_bytes(&mut bytes)?; 19 | Ok(NodeId(bytes)) 20 | } 21 | 22 | /// Get the node ID as a byte slice. 23 | /// 24 | /// This is mostly useful for display purposes such as logging. 25 | /// To get the wire format representation, use `encode`. 26 | pub fn as_bytes(&self) -> &[u8] { 27 | &self.0 28 | } 29 | 30 | /// Get the wire format representation that was used (sigh) by 31 | /// jormungandr releases prior to 0.9. 32 | pub fn encode(&self) -> Vec { 33 | let mut vec = Vec::with_capacity(NODE_ID_LEN + 8); 34 | vec.extend_from_slice(&(NODE_ID_LEN as u64).to_le_bytes()); 35 | vec.extend_from_slice(&self.0); 36 | vec 37 | } 38 | } 39 | 40 | #[derive(Debug, thiserror::Error)] 41 | #[error("invalid slice length for legacy node ID")] 42 | pub struct TryFromBytesError(); 43 | 44 | impl From for TryFromBytesError { 45 | fn from(_: TryFromSliceError) -> Self { 46 | TryFromBytesError() 47 | } 48 | } 49 | 50 | impl<'a> TryFrom<&'a [u8]> for NodeId { 51 | type Error = TryFromBytesError; 52 | fn try_from(bytes: &'a [u8]) -> Result { 53 | let bytes = bytes.try_into()?; 54 | Ok(NodeId(bytes)) 55 | } 56 | } 57 | 58 | struct HexWrap<'a>(&'a [u8]); 59 | 60 | impl<'a> fmt::Debug for HexWrap<'a> { 61 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 62 | f.write_str("0x")?; 63 | for b in self.0 { 64 | write!(f, "{:x}", *b)?; 65 | } 66 | Ok(()) 67 | } 68 | } 69 | 70 | impl fmt::Debug for NodeId { 71 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 72 | f.debug_tuple("NodeId").field(&HexWrap(&self.0)).finish() 73 | } 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use super::*; 79 | 80 | #[test] 81 | fn encode_works() { 82 | let id = NodeId::generate(&mut rand::thread_rng()).unwrap(); 83 | let v = id.encode(); 84 | assert_eq!(v.len(), NODE_ID_LEN + 8); 85 | assert_eq!(v[..8], (NODE_ID_LEN as u64).to_le_bytes()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /chain-network/src/grpc/mod.rs: -------------------------------------------------------------------------------- 1 | mod proto; 2 | 3 | pub mod client; 4 | pub mod server; 5 | 6 | #[cfg(feature = "legacy")] 7 | pub mod legacy; 8 | 9 | pub mod watch; 10 | 11 | mod convert; 12 | mod streaming; 13 | 14 | pub use client::Client; 15 | pub use server::{NodeService, Server}; 16 | -------------------------------------------------------------------------------- /chain-network/src/grpc/proto.rs: -------------------------------------------------------------------------------- 1 | // landing for gRPC code generated by tonic-build 2 | 3 | pub(crate) mod types { 4 | tonic::include_proto!("iohk.chain.types"); 5 | } 6 | 7 | pub(crate) mod node { 8 | tonic::include_proto!("iohk.chain.node"); 9 | } 10 | 11 | pub(crate) mod watch { 12 | tonic::include_proto!("iohk.chain.watch"); 13 | } 14 | -------------------------------------------------------------------------------- /chain-network/src/grpc/streaming/inbound.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::grpc::convert::{error_from_grpc, FromProtobuf}; 3 | use futures::prelude::*; 4 | use pin_project::pin_project; 5 | use tonic::Streaming; 6 | 7 | use std::marker::PhantomData; 8 | use std::pin::Pin; 9 | use std::task::{Context, Poll}; 10 | 11 | #[must_use = "streams do nothing unless polled"] 12 | #[pin_project] 13 | pub struct InboundStream { 14 | #[pin] 15 | inner: Streaming

, 16 | _phantom: PhantomData, 17 | } 18 | 19 | impl InboundStream { 20 | pub(crate) fn new(inner: Streaming

) -> Self { 21 | InboundStream { 22 | inner, 23 | _phantom: PhantomData, 24 | } 25 | } 26 | } 27 | 28 | impl Stream for InboundStream 29 | where 30 | T: FromProtobuf

, 31 | { 32 | type Item = Result; 33 | 34 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { 35 | let this = self.project(); 36 | this.inner.poll_next(cx).map(|opt| { 37 | opt.map(|item| match item { 38 | Ok(msg) => { 39 | let item = T::from_message(msg)?; 40 | Ok(item) 41 | } 42 | Err(e) => Err(error_from_grpc(e)), 43 | }) 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /chain-network/src/grpc/streaming/mod.rs: -------------------------------------------------------------------------------- 1 | mod inbound; 2 | mod outbound; 3 | 4 | pub use inbound::InboundStream; 5 | pub(super) use outbound::{OutboundStream, OutboundTryStream}; 6 | -------------------------------------------------------------------------------- /chain-network/src/grpc/streaming/outbound.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use crate::grpc::convert::{error_into_grpc, IntoProtobuf}; 3 | use futures::prelude::*; 4 | use pin_project::pin_project; 5 | use tonic::Status; 6 | 7 | use std::pin::Pin; 8 | use std::task::{Context, Poll}; 9 | 10 | #[must_use = "streams do nothing unless polled"] 11 | #[pin_project] 12 | pub struct OutboundStream { 13 | #[pin] 14 | inner: S, 15 | } 16 | 17 | impl OutboundStream { 18 | pub(crate) fn new(inner: S) -> Self { 19 | OutboundStream { inner } 20 | } 21 | } 22 | 23 | impl Stream for OutboundStream 24 | where 25 | S: Stream, 26 | S::Item: IntoProtobuf, 27 | { 28 | type Item = ::Message; 29 | 30 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 31 | let this = self.project(); 32 | this.inner 33 | .poll_next(cx) 34 | .map(|maybe_item| maybe_item.map(|item| item.into_message())) 35 | } 36 | } 37 | 38 | #[must_use = "streams do nothing unless polled"] 39 | #[pin_project] 40 | pub struct OutboundTryStream { 41 | #[pin] 42 | inner: S, 43 | } 44 | 45 | impl OutboundTryStream { 46 | pub(crate) fn new(inner: S) -> Self { 47 | OutboundTryStream { inner } 48 | } 49 | } 50 | 51 | impl Stream for OutboundTryStream 52 | where 53 | S: TryStream, 54 | S::Ok: IntoProtobuf, 55 | { 56 | type Item = Result<::Message, Status>; 57 | 58 | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 59 | let this = self.project(); 60 | this.inner.try_poll_next(cx).map(|maybe_item| { 61 | maybe_item.map(|item| match item { 62 | Ok(data) => Ok(data.into_message()), 63 | Err(e) => Err(error_into_grpc(e)), 64 | }) 65 | }) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /chain-network/src/grpc/watch/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod server; 3 | 4 | pub use client::Client; 5 | pub use server::Server; 6 | -------------------------------------------------------------------------------- /chain-network/src/grpc/watch/server.rs: -------------------------------------------------------------------------------- 1 | use crate::core::watch::server::Watch; 2 | use crate::data::block; 3 | use crate::grpc::proto; 4 | use crate::grpc::streaming::OutboundTryStream; 5 | 6 | pub type Server = proto::watch::watch_server::WatchServer>; 7 | 8 | #[derive(Debug)] 9 | pub struct WatchService { 10 | inner: T, 11 | } 12 | 13 | impl WatchService { 14 | pub fn new(inner: T) -> Self { 15 | WatchService { inner } 16 | } 17 | } 18 | 19 | #[tonic::async_trait] 20 | impl proto::watch::watch_server::Watch for WatchService 21 | where 22 | T: Watch, 23 | { 24 | type BlockSubscriptionStream = OutboundTryStream; 25 | 26 | async fn block_subscription( 27 | &self, 28 | request: tonic::Request, 29 | ) -> Result, tonic::Status> { 30 | let proto::watch::BlockSubscriptionRequest {} = request.into_inner(); 31 | let stream = self.inner.block_subscription().await?; 32 | Ok(tonic::Response::new(OutboundTryStream::new(stream))) 33 | } 34 | 35 | type TipSubscriptionStream = OutboundTryStream; 36 | 37 | async fn tip_subscription( 38 | &self, 39 | request: tonic::Request, 40 | ) -> Result, tonic::Status> { 41 | let proto::watch::TipSubscriptionRequest {} = request.into_inner(); 42 | let stream = self.inner.tip_subscription().await?; 43 | Ok(tonic::Response::new(OutboundTryStream::new(stream))) 44 | } 45 | 46 | type SyncMultiverseStream = OutboundTryStream; 47 | 48 | async fn sync_multiverse( 49 | &self, 50 | request: tonic::Request, 51 | ) -> Result, tonic::Status> { 52 | let proto::watch::SyncMultiverseRequest { from } = request.into_inner(); 53 | let from = block::try_ids_from_iter(from)?; 54 | let stream = self.inner.sync_multiverse(from).await?; 55 | Ok(tonic::Response::new(OutboundTryStream::new(stream))) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /chain-network/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::all)] 2 | 3 | pub mod core; 4 | pub mod data; 5 | pub mod error; 6 | pub mod grpc; 7 | 8 | /// Version of the protocol implemented by this crate. 9 | /// 10 | /// Note that until the protocol is stabilized, breaking changes may still 11 | /// occur without changing this version number. 12 | pub const PROTOCOL_VERSION: u32 = 1; 13 | -------------------------------------------------------------------------------- /chain-ser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-ser" 3 | version = "0.1.0" 4 | authors = ["Vincent Hanquez "] 5 | edition = "2021" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | thiserror = "1.0" 11 | -------------------------------------------------------------------------------- /chain-ser/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod abor; 2 | pub mod deser; 3 | pub mod packer; 4 | -------------------------------------------------------------------------------- /chain-storage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-storage" 3 | version = "0.1.0" 4 | authors = ["dev@iohk.io"] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [features] 9 | with-bench = ["criterion", "tempfile", "rand_core"] 10 | 11 | [dependencies] 12 | sled = "0.34.0" 13 | thiserror = "1.0" 14 | data-pile = "0.6.1" 15 | 16 | criterion = { version = "0.3.0", optional = true } 17 | rand_core = { version = "0.6", features = ["getrandom"], optional = true } 18 | tempfile = { version = "3.1.0", optional = true } 19 | 20 | [dev-dependencies] 21 | rand_core = { version = "0.6", features = ["getrandom"] } 22 | tempfile = "3.1.0" 23 | 24 | [[bench]] 25 | harness = false 26 | name = "storage" 27 | required-features = ["with-bench"] 28 | -------------------------------------------------------------------------------- /chain-storage/README.md: -------------------------------------------------------------------------------- 1 | # Blockchain storage 2 | 3 | Documentation is available in the crate and can be generated with `cargo doc`. 4 | 5 | ## Running benchmarks 6 | 7 | `cargo bench --features with-bench` 8 | -------------------------------------------------------------------------------- /chain-storage/src/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Debug, Error)] 4 | pub enum Error { 5 | #[error("failed to open the database directory")] 6 | Open(#[source] std::io::Error), 7 | #[error("block not found")] 8 | BlockNotFound, 9 | #[error("volatile store error")] 10 | VolatileBackendError(#[from] sled::Error), 11 | #[error("permanent store error")] 12 | PermanentBackendError(#[from] data_pile::Error), 13 | #[error("Block already present in DB")] 14 | BlockAlreadyPresent, 15 | #[error("the parent block is missing for the required write")] 16 | MissingParent, 17 | #[error("branch with the requested tip does not exist")] 18 | BranchNotFound, 19 | #[error("failed to serialize block metadata")] 20 | BlockInfoSerialize(#[source] std::io::Error), 21 | #[error("failed to deserialize block metadata")] 22 | BlockInfoDeserialize(#[source] std::io::Error), 23 | #[error("the database is consistent")] 24 | Inconsistent(#[from] ConsistencyFailure), 25 | #[error( 26 | "cannot iterate over blocks because the provided distance is bigger than the chain length" 27 | )] 28 | CannotIterate, 29 | } 30 | 31 | #[derive(Debug, Error)] 32 | pub enum ConsistencyFailure { 33 | #[error("stored block is missing its parent")] 34 | MissingParentBlock, 35 | #[error("block is listed in the chain length index but is not stored")] 36 | ChainLength, 37 | #[error("BlockInfo is present but the corresponding block is not stored")] 38 | BlockInfo, 39 | #[error("tagged block is not stored")] 40 | TaggedBlock, 41 | #[error("expected to see a block in the permanent storage")] 42 | MissingPermanentBlock, 43 | } 44 | -------------------------------------------------------------------------------- /chain-storage/src/test_utils.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for testing the storage. 2 | 3 | use crate::Value; 4 | use std::sync::atomic::{AtomicU64, Ordering}; 5 | 6 | #[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Copy)] 7 | pub struct BlockId(pub u64); 8 | 9 | /// Used to generate block ids 10 | static GLOBAL_ID_COUNTER: AtomicU64 = AtomicU64::new(1); 11 | 12 | impl BlockId { 13 | pub fn generate() -> Self { 14 | Self(GLOBAL_ID_COUNTER.fetch_add(1, Ordering::SeqCst)) 15 | } 16 | 17 | pub fn serialize(&self, mut writer: W) -> Result<(), std::io::Error> { 18 | writer.write_all(&self.0.to_le_bytes()) 19 | } 20 | 21 | pub fn serialize_as_vec(&self) -> Vec { 22 | let mut v = Vec::new(); 23 | self.serialize(&mut v).unwrap(); 24 | v 25 | } 26 | 27 | pub fn serialize_as_value(&self) -> Value { 28 | Value::owned(self.serialize_as_vec().into_boxed_slice()) 29 | } 30 | } 31 | 32 | #[derive(Debug, Clone, Eq, PartialEq)] 33 | pub struct Block { 34 | pub id: BlockId, 35 | pub parent: BlockId, 36 | pub chain_length: u32, 37 | pub data: Box<[u8]>, 38 | } 39 | 40 | impl Block { 41 | pub fn genesis(data: Option>) -> Self { 42 | Self { 43 | id: BlockId::generate(), 44 | parent: BlockId(0), 45 | chain_length: 0, 46 | data: data.unwrap_or_default(), 47 | } 48 | } 49 | 50 | pub fn make_child(&self, data: Option>) -> Self { 51 | Self { 52 | id: BlockId::generate(), 53 | parent: self.id, 54 | chain_length: self.chain_length + 1, 55 | data: data.unwrap_or_default(), 56 | } 57 | } 58 | 59 | pub fn serialize(&self, mut writer: W) -> Result<(), std::io::Error> { 60 | writer.write_all(&self.id.0.to_le_bytes())?; 61 | writer.write_all(&self.parent.0.to_le_bytes())?; 62 | writer.write_all(&self.chain_length.to_le_bytes())?; 63 | writer.write_all(&(self.data.len() as u64).to_le_bytes())?; 64 | writer.write_all(&self.data)?; 65 | Ok(()) 66 | } 67 | 68 | pub fn serialize_as_vec(&self) -> Vec { 69 | let mut v = Vec::new(); 70 | self.serialize(&mut v).unwrap(); 71 | v 72 | } 73 | 74 | pub fn serialize_as_value(&self) -> Value { 75 | Value::owned(self.serialize_as_vec().into_boxed_slice()) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /chain-storage/src/value.rs: -------------------------------------------------------------------------------- 1 | use data_pile::SharedMmap; 2 | use sled::IVec; 3 | use std::hash::{Hash, Hasher}; 4 | 5 | #[derive(Debug, Clone)] 6 | enum ValueImpl { 7 | Volatile(IVec), 8 | Owned(Box<[u8]>), 9 | Permanent(SharedMmap), 10 | } 11 | 12 | /// Wrapper for data held by the database. This wrapper holds structs returned 13 | /// by both volatile and permanent storage to ensure we don't have needless 14 | /// copying on return. Data should be accessed through the `AsRef` trait. 15 | #[derive(Debug, Clone)] 16 | pub struct Value { 17 | inner: ValueImpl, 18 | } 19 | 20 | impl Value { 21 | pub(crate) fn volatile(value: IVec) -> Self { 22 | Self { 23 | inner: ValueImpl::Volatile(value), 24 | } 25 | } 26 | 27 | pub(crate) fn owned(value: Box<[u8]>) -> Self { 28 | Self { 29 | inner: ValueImpl::Owned(value), 30 | } 31 | } 32 | 33 | pub(crate) fn permanent(value: SharedMmap) -> Self { 34 | Self { 35 | inner: ValueImpl::Permanent(value), 36 | } 37 | } 38 | } 39 | 40 | impl PartialEq for Value { 41 | fn eq(&self, other: &Value) -> bool { 42 | self.as_ref() == other.as_ref() 43 | } 44 | } 45 | 46 | impl Eq for Value {} 47 | 48 | impl Hash for Value { 49 | fn hash(&self, state: &mut H) { 50 | self.as_ref().hash(state) 51 | } 52 | } 53 | 54 | impl AsRef<[u8]> for Value { 55 | fn as_ref(&self) -> &[u8] { 56 | match &self.inner { 57 | ValueImpl::Volatile(value) => value.as_ref(), 58 | ValueImpl::Owned(value) => value.as_ref(), 59 | ValueImpl::Permanent(value) => value.as_ref(), 60 | } 61 | } 62 | } 63 | 64 | impl From> for Value { 65 | fn from(value: Box<[u8]>) -> Self { 66 | Self::owned(value) 67 | } 68 | } 69 | 70 | impl From> for Value { 71 | fn from(value: Vec) -> Self { 72 | Self::owned(value.into_boxed_slice()) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /chain-time/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-time" 3 | version = "0.1.0" 4 | authors = ["Vincent Hanquez "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [dependencies] 9 | chain-ser = { path = "../chain-ser" } 10 | chain-core = { path = "../chain-core" } 11 | 12 | quickcheck = { version = "0.9", optional = true } 13 | proptest = { git = "https://github.com/input-output-hk/proptest.git", optional = true } 14 | test-strategy = { version = "0.1", optional = true } 15 | 16 | [features] 17 | property-test-api = ["quickcheck", "proptest", "test-strategy"] 18 | 19 | [dev-dependencies] 20 | quickcheck = "0.9" 21 | proptest = { git = "https://github.com/input-output-hk/proptest.git" } 22 | test-strategy = "0.1" 23 | -------------------------------------------------------------------------------- /chain-time/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod era; 2 | pub mod timeframe; 3 | pub mod timeline; 4 | pub mod units; 5 | 6 | pub use era::{Epoch, EpochPosition, EpochSlotOffset, TimeEra}; 7 | pub use timeframe::{Slot, SlotDuration, TimeFrame}; 8 | pub use timeline::{TimeOffsetSeconds, Timeline}; 9 | pub use units::DurationSeconds; 10 | 11 | #[cfg(any(test, feature = "property-test-api"))] 12 | pub mod testing; 13 | -------------------------------------------------------------------------------- /chain-time/src/testing.rs: -------------------------------------------------------------------------------- 1 | use crate::{Epoch, EpochPosition, EpochSlotOffset, Slot, TimeEra}; 2 | use quickcheck::{Arbitrary, Gen}; 3 | 4 | impl Arbitrary for TimeEra { 5 | fn arbitrary(g: &mut G) -> Self { 6 | TimeEra::new( 7 | Arbitrary::arbitrary(g), 8 | Arbitrary::arbitrary(g), 9 | u32::arbitrary(g) % 127 + 1, 10 | ) 11 | } 12 | } 13 | 14 | impl Arbitrary for Slot { 15 | fn arbitrary(g: &mut G) -> Self { 16 | Slot(Arbitrary::arbitrary(g)) 17 | } 18 | } 19 | 20 | impl Arbitrary for Epoch { 21 | fn arbitrary(g: &mut G) -> Self { 22 | Epoch(Arbitrary::arbitrary(g)) 23 | } 24 | } 25 | 26 | impl Arbitrary for EpochPosition { 27 | fn arbitrary(g: &mut G) -> Self { 28 | EpochPosition { 29 | epoch: Epoch(Arbitrary::arbitrary(g)), 30 | slot: EpochSlotOffset(u32::arbitrary(g)), 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chain-time/src/timeline.rs: -------------------------------------------------------------------------------- 1 | use crate::units::DurationSeconds; 2 | use std::time::{Duration, SystemTime}; 3 | 4 | /// Represent a timeline with a specific start point rooted on earth time. 5 | #[derive(Debug, Clone)] 6 | pub struct Timeline(pub(crate) SystemTime); 7 | 8 | /// Represent an offset in time units in the timeline 9 | #[derive(Debug, Clone)] 10 | pub struct TimeOffset(pub(crate) Duration); 11 | 12 | /// Represent an offset in seconds in the timeline 13 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 14 | #[cfg_attr( 15 | any(test, feature = "property-test-api"), 16 | derive(test_strategy::Arbitrary) 17 | )] 18 | pub struct TimeOffsetSeconds(pub(crate) DurationSeconds); 19 | 20 | impl From for Timeline { 21 | fn from(s: SystemTime) -> Self { 22 | Timeline(s) 23 | } 24 | } 25 | 26 | impl From for TimeOffsetSeconds { 27 | fn from(v: DurationSeconds) -> Self { 28 | TimeOffsetSeconds(v) 29 | } 30 | } 31 | 32 | impl From for TimeOffset { 33 | fn from(v: TimeOffsetSeconds) -> TimeOffset { 34 | TimeOffset(v.0.into()) 35 | } 36 | } 37 | 38 | impl From for u64 { 39 | fn from(v: TimeOffsetSeconds) -> Self { 40 | v.0.into() 41 | } 42 | } 43 | 44 | impl Timeline { 45 | /// Create a new timeline, which is a time starting point 46 | pub fn new(start_time: SystemTime) -> Self { 47 | Timeline(start_time) 48 | } 49 | 50 | /// Return the duration since the creation of the timeline 51 | /// 52 | /// If the time is earlier than the start of this timeline, 53 | /// then None is returned. 54 | pub fn differential(&self, t: &SystemTime) -> Option { 55 | match t.duration_since(self.0) { 56 | Ok(duration) => Some(TimeOffset(duration)), 57 | Err(_) => None, 58 | } 59 | } 60 | 61 | /// Advance a timeline, and create a new timeline starting at 62 | /// timeline + duration 63 | pub fn advance(&self, d: Duration) -> Self { 64 | Timeline(self.0 + d) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /chain-time/src/units.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | /// Represent a Duration where the maximum precision is in the second 4 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 5 | #[cfg_attr( 6 | any(test, feature = "property-test-api"), 7 | derive(test_strategy::Arbitrary) 8 | )] 9 | pub struct DurationSeconds(pub u64); 10 | 11 | impl From for DurationSeconds { 12 | fn from(v: u64) -> Self { 13 | Self(v) 14 | } 15 | } 16 | 17 | impl From for u64 { 18 | fn from(v: DurationSeconds) -> Self { 19 | v.0 20 | } 21 | } 22 | 23 | impl From for Duration { 24 | fn from(v: DurationSeconds) -> Self { 25 | Duration::from_secs(v.0) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chain-vote/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "chain-vote" 3 | version = "0.1.0" 4 | authors = ["Vincent Hanquez "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | chain-core = { path = "../chain-core" } 9 | chain-crypto = { path = "../chain-crypto"} 10 | rand = "0.8" 11 | rand_core = "0.6" 12 | rayon = "1.5" 13 | thiserror = "1.0" 14 | cryptoxide = "^0.4.2" 15 | const_format = "0.2" 16 | 17 | [dev-dependencies] 18 | rand_chacha = "0.3" 19 | smoke = "^0.2.1" 20 | criterion = "0.3" 21 | 22 | [build-dependencies] 23 | cfg-if = "*" 24 | 25 | 26 | [[bench]] 27 | harness = false 28 | name = "shvzk" 29 | 30 | [features] 31 | default = ["ristretto255"] 32 | ristretto255 = [] 33 | p256k1 = ["chain-crypto/p256k1"] 34 | -------------------------------------------------------------------------------- /chain-vote/benches/shvzk.rs: -------------------------------------------------------------------------------- 1 | use chain_vote::*; 2 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 3 | use rand_chacha::ChaCha20Rng; 4 | use rand_core::SeedableRng; 5 | 6 | fn common(rng: &mut ChaCha20Rng) -> ElectionPublicKey { 7 | let h = Crs::from_hash(&[0u8; 32]); 8 | 9 | let mc1 = MemberCommunicationKey::new(rng); 10 | let mc = [mc1.to_public()]; 11 | 12 | let threshold = 1; 13 | 14 | let m1 = MemberState::new(rng, threshold, &h, &mc, 0); 15 | 16 | let participants = vec![m1.public_key()]; 17 | ElectionPublicKey::from_participants(&participants) 18 | } 19 | 20 | fn encrypt_and_prove(c: &mut Criterion) { 21 | let mut rng = ChaCha20Rng::from_seed([0u8; 32]); 22 | let mut group = c.benchmark_group("Encrypt and prove"); 23 | let crs = Crs::from_hash(&[0u8; 32]); 24 | let ek = common(&mut rng); 25 | 26 | for &number_candidates in [2usize, 4, 8].iter() { 27 | let parameter_string = format!("{} candidates", number_candidates); 28 | group.bench_with_input( 29 | BenchmarkId::new("Encrypt and Prove", parameter_string), 30 | &number_candidates, 31 | |b, &nr| b.iter(|| ek.encrypt_and_prove_vote(&mut rng, &crs, Vote::new(nr, 0))), 32 | ); 33 | } 34 | 35 | group.finish(); 36 | } 37 | 38 | fn verify(c: &mut Criterion) { 39 | let mut rng = ChaCha20Rng::from_seed([0u8; 32]); 40 | let mut group = c.benchmark_group("Verify vote proof"); 41 | let crs = Crs::from_hash(&[0u8; 32]); 42 | let ek = common(&mut rng); 43 | 44 | for &number_candidates in [2usize, 4, 8].iter() { 45 | let (vote, proof) = 46 | ek.encrypt_and_prove_vote(&mut rng, &crs, Vote::new(number_candidates, 0)); 47 | let parameter_string = format!("{} candidates", number_candidates); 48 | group.bench_with_input( 49 | BenchmarkId::new("Verify with", parameter_string), 50 | &number_candidates, 51 | |b, _| b.iter(|| proof.verify(&crs, ek.as_raw(), &vote)), 52 | ); 53 | } 54 | 55 | group.finish(); 56 | } 57 | 58 | criterion_group!( 59 | name = shvzk; 60 | config = Criterion::default().sample_size(500); 61 | targets = 62 | encrypt_and_prove, 63 | verify, 64 | ); 65 | 66 | criterion_main!(shvzk); 67 | -------------------------------------------------------------------------------- /chain-vote/build.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | /// This build script serves as a way to select a single crypto backend to use 3 | /// without polluting the code with chains of if-else cfg everywhere a mutually 4 | /// exclusive choice about the backend has to be made. 5 | /// 6 | /// The backend is selected with the following rules: 7 | /// * if a non-default feature is selected, activate the corresponding backend 8 | /// * If no non-default feature is selected, activate the default backend 9 | /// 10 | /// Flags exported by this module are guaranteed to be mutually exclusive. 11 | 12 | const BACKEND_FLAG_P256K1: &str = "__internal_ex_backend_p256k1"; 13 | const BACKEND_FLAG_RISTRETTO255: &str = "__internal_ex_backend_ristretto255"; 14 | 15 | fn main() { 16 | cfg_if::cfg_if! { 17 | if #[cfg(feature = "p256k1")] { 18 | println!("cargo:rustc-cfg=crypto_backend=\"{}\"", BACKEND_FLAG_P256K1); 19 | } else if #[cfg(feature = "ristretto255")] { 20 | println!("cargo:rustc-cfg=crypto_backend=\"{}\"", BACKEND_FLAG_RISTRETTO255); 21 | } else { 22 | compile_error!("one of the features \"p256k1\", \"ristretto255\' has to be selected as the backend"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/mod.rs: -------------------------------------------------------------------------------- 1 | mod commitment; 2 | mod elgamal; 3 | mod zkps; 4 | 5 | pub(crate) use self::{ 6 | commitment::CommitmentKey, 7 | elgamal::{HybridCiphertext, PublicKey, SecretKey}, 8 | zkps::{CorrectShareGenerationZkp, UnitVectorZkp}, 9 | }; 10 | 11 | #[cfg(test)] 12 | pub(crate) use self::elgamal::Keypair; 13 | 14 | pub use self::elgamal::Ciphertext; 15 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/zkps/correct_decryption/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | mod zkp; 3 | 4 | pub use zkp::Zkp as CorrectElGamalDecrZkp; 5 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/zkps/correct_hybrid_decryption_key/mod.rs: -------------------------------------------------------------------------------- 1 | #[allow(dead_code)] 2 | mod zkp; 3 | 4 | pub use zkp::Zkp as CorrectHybridDecrKeyZkp; 5 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/zkps/correct_share_generation/mod.rs: -------------------------------------------------------------------------------- 1 | mod zkp; 2 | 3 | pub use zkp::Zkp as CorrectShareGenerationZkp; 4 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/zkps/dl_equality/challenge_context.rs: -------------------------------------------------------------------------------- 1 | use crate::{GroupElement, Scalar}; 2 | use cryptoxide::blake2b::Blake2b; 3 | use cryptoxide::digest::Digest; 4 | 5 | /// Challenge context for Discrete Logarithm Equality proof. The common reference string 6 | /// are two EC bases, and the statement consists of two EC points. 7 | /// The challenge computation takes as input the two announcements 8 | /// computed in the sigma protocol, `a1` and `a2`, and the full 9 | /// statement. 10 | pub struct ChallengeContext(Blake2b); 11 | 12 | impl ChallengeContext { 13 | /// Initialise the challenge context, by including the common reference string and the full statement 14 | pub(crate) fn new( 15 | base_1: &GroupElement, 16 | base_2: &GroupElement, 17 | point_1: &GroupElement, 18 | point_2: &GroupElement, 19 | ) -> Self { 20 | let mut ctx = Blake2b::new(64); 21 | ctx.input(&base_1.to_bytes()); 22 | ctx.input(&base_2.to_bytes()); 23 | ctx.input(&point_1.to_bytes()); 24 | ctx.input(&point_2.to_bytes()); 25 | 26 | ChallengeContext(ctx) 27 | } 28 | 29 | /// Generation of the `first_challenge`. This challenge is generated after the `Announcement` is 30 | /// "sent". Hence, we include the latter to the challenge context and generate its 31 | /// corresponding scalar. 32 | pub(crate) fn first_challenge(&mut self, a1: &GroupElement, a2: &GroupElement) -> Scalar { 33 | self.0.input(&a1.to_bytes()); 34 | self.0.input(&a2.to_bytes()); 35 | 36 | Scalar::hash_to_scalar(&self.0) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/zkps/dl_equality/mod.rs: -------------------------------------------------------------------------------- 1 | mod challenge_context; 2 | mod zkp; 3 | 4 | pub use zkp::Zkp as DleqZkp; 5 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/zkps/mod.rs: -------------------------------------------------------------------------------- 1 | mod correct_decryption; 2 | mod correct_hybrid_decryption_key; 3 | mod correct_share_generation; 4 | mod dl_equality; 5 | mod unit_vector; 6 | 7 | pub use correct_decryption::CorrectElGamalDecrZkp; 8 | pub use correct_hybrid_decryption_key::CorrectHybridDecrKeyZkp; 9 | pub use correct_share_generation::CorrectShareGenerationZkp; 10 | pub use unit_vector::UnitVectorZkp; 11 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/zkps/unit_vector/challenge_context.rs: -------------------------------------------------------------------------------- 1 | use super::messages::Announcement; 2 | use crate::cryptography::{Ciphertext, CommitmentKey, PublicKey}; 3 | use crate::Scalar; 4 | use cryptoxide::blake2b::Blake2b; 5 | use cryptoxide::digest::Digest; 6 | 7 | /// Challenge context for the Unit Vector Zero Knowledge Proof. The common reference string 8 | /// is a commitment key, and the statement consists of a public key, and the encryption of each 9 | /// entry of the vector. 10 | pub struct ChallengeContext(Blake2b); 11 | 12 | impl ChallengeContext { 13 | /// Initialise the challenge context, by including the common reference string and the full statement 14 | pub(crate) fn new( 15 | commitment_key: &CommitmentKey, 16 | public_key: &PublicKey, 17 | ciphers: &[Ciphertext], 18 | ) -> Self { 19 | let mut ctx = Blake2b::new(64); 20 | ctx.input(&commitment_key.to_bytes()); 21 | ctx.input(&public_key.to_bytes()); 22 | for c in ciphers { 23 | ctx.input(&c.to_bytes()); 24 | } 25 | 26 | ChallengeContext(ctx) 27 | } 28 | 29 | /// Generation of the `first_challenge`. This challenge is generated after the `Announcement` is "sent". Hence, 30 | /// we include the latter to the challenge context and generate its corresponding scalar. 31 | pub(crate) fn first_challenge(&mut self, ibas: &[Announcement]) -> Scalar { 32 | for iba in ibas { 33 | self.0.input(&iba.i.to_bytes()); 34 | self.0.input(&iba.b.to_bytes()); 35 | self.0.input(&iba.a.to_bytes()); 36 | } 37 | 38 | Scalar::hash_to_scalar(&self.0) 39 | } 40 | 41 | /// Generation of the `second_challenge`. This challenge is generated after the encrypted polynomial 42 | /// coefficients are "sent". Hence, we include the list of ciphertexts to the challenge context and 43 | /// generate its corresponding scalar. 44 | pub(crate) fn second_challenge(&mut self, ds: &[Ciphertext]) -> Scalar { 45 | for d in ds { 46 | self.0.input(&d.to_bytes()) 47 | } 48 | Scalar::hash_to_scalar(&self.0) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /chain-vote/src/cryptography/zkps/unit_vector/mod.rs: -------------------------------------------------------------------------------- 1 | mod challenge_context; 2 | mod messages; 3 | mod zkp; 4 | 5 | pub use zkp::Zkp as UnitVectorZkp; 6 | -------------------------------------------------------------------------------- /chain-vote/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | mod macros; 3 | pub mod committee; 4 | mod cryptography; 5 | mod encrypted_vote; 6 | mod math; 7 | pub mod tally; 8 | 9 | // re-export under a debug module 10 | #[doc(hidden)] 11 | pub mod debug { 12 | pub mod cryptography { 13 | pub use crate::cryptography::*; 14 | } 15 | } 16 | 17 | #[cfg(crypto_backend = "__internal_ex_backend_p256k1")] 18 | pub(crate) use chain_crypto::ec::p256k1::*; 19 | #[cfg(crypto_backend = "__internal_ex_backend_ristretto255")] 20 | pub(crate) use chain_crypto::ec::ristretto255::*; 21 | 22 | #[cfg(crypto_backend = "__internal_ex_backend_p256k1")] 23 | const CURVE_HRP: &str = "p256k1"; 24 | #[cfg(crypto_backend = "__internal_ex_backend_ristretto255")] 25 | const CURVE_HRP: &str = "ristretto255"; 26 | 27 | pub use math::babystep::BabyStepsTable as TallyOptimizationTable; 28 | 29 | pub use crate::{ 30 | committee::{ElectionPublicKey, MemberCommunicationKey, MemberPublicKey, MemberState}, 31 | cryptography::Ciphertext, //todo: why this? 32 | encrypted_vote::{Ballot, BallotVerificationError, EncryptedVote, ProofOfCorrectVote, Vote}, 33 | tally::{Crs, EncryptedTally, Tally, TallyDecryptShare}, 34 | }; 35 | -------------------------------------------------------------------------------- /chain-vote/src/macros.rs: -------------------------------------------------------------------------------- 1 | //! Internal macros 2 | 3 | macro_rules! std_ops_gen { 4 | ($lty: ident, $class: ident, $rty: ident, $out: ident, $f: ident) => { 5 | impl<'a> $class<$rty> for &'a $lty { 6 | type Output = $out; 7 | 8 | fn $f(self, other: $rty) -> Self::Output { 9 | self.$f(&other) 10 | } 11 | } 12 | 13 | impl<'b> $class<&'b $rty> for $lty { 14 | type Output = $out; 15 | 16 | fn $f(self, other: &'b $rty) -> Self::Output { 17 | (&self).$f(other) 18 | } 19 | } 20 | 21 | impl $class<$rty> for $lty { 22 | type Output = $out; 23 | 24 | fn $f(self, other: $rty) -> Self::Output { 25 | (&self).$f(&other) 26 | } 27 | } 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /chain-vote/src/math/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod babystep; 2 | pub mod polynomial; 3 | -------------------------------------------------------------------------------- /imhamt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "imhamt" 3 | version = "0.1.0" 4 | authors = ["Vincent Hanquez "] 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [dependencies] 9 | thiserror = "1.0" 10 | proptest = { git = "https://github.com/input-output-hk/proptest.git", optional = true } 11 | test-strategy = { version = "0.1", optional = true } 12 | 13 | [dev-dependencies] 14 | criterion = "0.3.0" 15 | proptest = { git = "https://github.com/input-output-hk/proptest.git" } 16 | test-strategy = "0.1" 17 | trybuild = "1" 18 | 19 | [target.'cfg(unix)'.dev-dependencies] 20 | jemalloc-ctl = "0.3" 21 | jemallocator = "*" 22 | 23 | [[example]] 24 | name = "memdump" 25 | path = "examples/memdump/main.rs" 26 | 27 | [[bench]] 28 | harness = false 29 | name = "imhamt" 30 | 31 | [features] 32 | property-test-api = ["proptest", "test-strategy"] 33 | 34 | [build-dependencies] 35 | rustc_version = "0.4" 36 | -------------------------------------------------------------------------------- /imhamt/benches/imhamt.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | 3 | use imhamt::*; 4 | 5 | use std::collections::hash_map::DefaultHasher; 6 | use std::collections::BTreeMap; 7 | 8 | type Key = String; 9 | 10 | const NB: usize = 1000; 11 | 12 | fn keys() -> Vec { 13 | let mut v = Vec::with_capacity(NB); 14 | for i in 0..NB { 15 | v.push(format!("key {}", i)) 16 | } 17 | v 18 | } 19 | 20 | fn bench_btreemap_insert(c: &mut Criterion) { 21 | c.bench_function("bench_btreemap_insert", |b| { 22 | b.iter(|| { 23 | let mut h: BTreeMap = BTreeMap::new(); 24 | for k in keys() { 25 | h.insert(k, 2); 26 | } 27 | }); 28 | }); 29 | } 30 | 31 | fn bench_hamt_insert(c: &mut Criterion) { 32 | c.bench_function("bench_hamt_insert", |b| { 33 | b.iter(|| { 34 | let mut h: Hamt = Hamt::new(); 35 | for k in keys() { 36 | h = h.insert(k, 2).unwrap() 37 | } 38 | }) 39 | }); 40 | } 41 | 42 | fn bench_btreemap_remove(c: &mut Criterion) { 43 | let mut h: BTreeMap = BTreeMap::new(); 44 | for k in keys() { 45 | h.insert(k, 2); 46 | } 47 | c.bench_function("bench_btreemap_remove", |b| { 48 | b.iter(|| { 49 | let mut h2 = h.clone(); 50 | for k in keys() { 51 | h2.remove(&k); 52 | } 53 | }) 54 | }); 55 | } 56 | 57 | fn bench_hamt_remove(c: &mut Criterion) { 58 | let mut h: Hamt = Hamt::new(); 59 | for k in keys() { 60 | h = h.insert(k, 2).unwrap() 61 | } 62 | c.bench_function("bench_hamt_remove", |b| { 63 | b.iter(|| { 64 | let mut h2 = h.clone(); 65 | for k in keys() { 66 | h2 = h2.remove_match(&k, &2).unwrap() 67 | } 68 | }) 69 | }); 70 | } 71 | 72 | criterion_group!( 73 | reference_btree, 74 | bench_btreemap_insert, 75 | bench_btreemap_remove, 76 | ); 77 | criterion_group!(hamt, bench_hamt_insert, bench_hamt_remove); 78 | criterion_main!(reference_btree, hamt); 79 | -------------------------------------------------------------------------------- /imhamt/build.rs: -------------------------------------------------------------------------------- 1 | use rustc_version::{version_meta, Channel}; 2 | 3 | fn main() { 4 | // if compiling with a nightly compiler, enable the "nightly" feature 5 | if version_meta().unwrap().channel == Channel::Nightly { 6 | println!("cargo:rustc-cfg=feature=\"nightly\""); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /imhamt/examples/memdump/main.rs: -------------------------------------------------------------------------------- 1 | #[cfg(unix)] 2 | mod unix; 3 | 4 | #[cfg(unix)] 5 | use unix::run; 6 | 7 | #[cfg(not(unix))] 8 | fn run() { 9 | println!("example not supported on this platform") 10 | } 11 | 12 | fn main() { 13 | run() 14 | } 15 | -------------------------------------------------------------------------------- /imhamt/examples/memdump/unix.rs: -------------------------------------------------------------------------------- 1 | extern crate imhamt; 2 | extern crate jemalloc_ctl; 3 | extern crate jemallocator; 4 | 5 | use jemalloc_ctl::{epoch, stats}; 6 | use std::collections::hash_map::DefaultHasher; 7 | use std::thread; 8 | use std::time::Duration; 9 | 10 | #[global_allocator] 11 | static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; 12 | 13 | use imhamt::Hamt; 14 | 15 | fn statprint() { 16 | let allocated = stats::allocated::read().unwrap(); 17 | let resident = stats::resident::read().unwrap(); 18 | println!("{} bytes allocated/{} bytes resident", allocated, resident); 19 | } 20 | 21 | pub fn run() { 22 | let mut h: Hamt = Hamt::new(); 23 | 24 | println!("adding 100000 entries"); 25 | 26 | for i in 0..100_000 { 27 | h = h.insert(i, i).unwrap(); 28 | } 29 | 30 | let mut h2 = h; 31 | 32 | epoch::advance().unwrap(); 33 | 34 | statprint(); 35 | thread::sleep(Duration::from_secs(10)); 36 | 37 | println!("adding 100000 entries"); 38 | 39 | for i in 100_000..200_000 { 40 | h2 = h2.insert(i, i).unwrap(); 41 | } 42 | 43 | epoch::advance().unwrap(); 44 | 45 | statprint(); 46 | thread::sleep(Duration::from_secs(10)); 47 | 48 | epoch::advance().unwrap(); 49 | 50 | statprint(); 51 | } 52 | -------------------------------------------------------------------------------- /imhamt/src/hash.rs: -------------------------------------------------------------------------------- 1 | pub use std::hash::{Hash, Hasher}; 2 | use std::marker::PhantomData; 3 | 4 | const SIZE: usize = 32; 5 | 6 | /// Hash of a key 7 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 8 | pub struct HashedKey(pub u64); 9 | 10 | impl HashedKey { 11 | /// Compute the HashedKey from a key 'k' 12 | pub fn compute(_: PhantomData, k: &K) -> Self { 13 | let mut hs = H::default(); 14 | k.hash(&mut hs); 15 | HashedKey(hs.finish()) 16 | } 17 | } 18 | 19 | impl HashedKey { 20 | /// get the index associated with a specific level 21 | #[inline] 22 | #[must_use = "operation does not modify the state"] 23 | pub fn level_index(self, level: usize) -> LevelIndex { 24 | // group of 5 bits 25 | let shift = level as u32 * 5; 26 | let idx = (self.0.wrapping_shr(shift) & 0b11111) as usize; 27 | assert!(idx < SIZE); 28 | LevelIndex(idx) 29 | } 30 | } 31 | 32 | /// Hash value subgroup per level used as Index for children 33 | /// 34 | /// A number that is between 0 and the maximum number of children 35 | /// elements in a node. 36 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 37 | pub struct LevelIndex(pub usize); 38 | 39 | impl LevelIndex { 40 | #[inline] 41 | #[must_use = "operation does not modify the state"] 42 | pub const fn mask(self) -> u32 { 43 | 1u32.wrapping_shl(self.0 as u32) 44 | } 45 | } 46 | 47 | #[cfg(test)] 48 | mod tests { 49 | use super::*; 50 | use std::collections::hash_map::DefaultHasher; 51 | 52 | #[test] 53 | fn hash_is_deterministic() { 54 | let h1 = HashedKey::compute(PhantomData::, &100u32); 55 | let h2 = HashedKey::compute(PhantomData::, &100u32); 56 | assert_eq!(h1, h2) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /imhamt/src/helper.rs: -------------------------------------------------------------------------------- 1 | #[inline] 2 | pub fn clone_array_and_insert_at_pos(v: &[A], a: A, pos: usize) -> Box<[A]> { 3 | // copy all elements but insert a new elements at position pos 4 | let mut new_array: Vec = Vec::with_capacity(v.len() + 1); 5 | new_array.extend_from_slice(&v[0..pos]); 6 | new_array.push(a); 7 | new_array.extend_from_slice(&v[pos..]); 8 | new_array.into() 9 | } 10 | 11 | #[inline] 12 | pub fn clone_array_and_set_at_pos(v: &[A], a: A, pos: usize) -> Box<[A]> { 13 | // copy all elements except at pos where a replaces it. 14 | let mut new_array: Vec = Vec::with_capacity(v.len()); 15 | if pos > 0 { 16 | new_array.extend_from_slice(&v[0..pos]); 17 | } 18 | new_array.push(a); 19 | if pos + 1 < v.len() { 20 | new_array.extend_from_slice(&v[(pos + 1)..]); 21 | } 22 | new_array.into() 23 | } 24 | 25 | #[inline] 26 | pub fn clone_array_and_remove_at_pos(v: &[A], pos: usize) -> Box<[A]> { 27 | let mut v: Vec<_> = v.to_vec(); 28 | v.remove(pos); 29 | v.into() 30 | } 31 | 32 | pub fn clone_array_and_extend(v: &[A], end: A) -> Box<[A]> { 33 | let mut new_array: Vec = Vec::with_capacity(v.len() + 1); 34 | new_array.extend_from_slice(v); 35 | new_array.push(end); 36 | new_array.into() 37 | } 38 | -------------------------------------------------------------------------------- /imhamt/src/node/mod.rs: -------------------------------------------------------------------------------- 1 | mod reference; 2 | pub use reference::*; 3 | -------------------------------------------------------------------------------- /imhamt/src/operation.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | #[derive(Debug, Error, Clone, Copy, PartialEq, Eq)] 4 | pub enum InsertError { 5 | #[error("entry with the provided key already exists")] 6 | EntryExists, 7 | } 8 | 9 | #[derive(Debug, Error, Clone, Copy, PartialEq, Eq)] 10 | pub enum RemoveError { 11 | #[error("could not find the requested key")] 12 | KeyNotFound, 13 | #[error("the removed value does not match the expected one")] 14 | ValueNotMatching, 15 | } 16 | 17 | #[derive(Debug, Error, Clone, Copy, PartialEq, Eq)] 18 | pub enum UpdateError 19 | where 20 | T: std::error::Error + std::fmt::Debug + 'static, 21 | { 22 | #[error("could not find the requested key")] 23 | KeyNotFound, 24 | #[error("error while updating the value")] 25 | ValueCallbackError(#[source] T), 26 | } 27 | 28 | #[derive(Debug, Error, Clone, Copy, PartialEq, Eq)] 29 | pub enum ReplaceError { 30 | #[error("could not find the requested key")] 31 | KeyNotFound, 32 | } 33 | -------------------------------------------------------------------------------- /imhamt/src/sharedref.rs: -------------------------------------------------------------------------------- 1 | //use std::rc; 2 | use std::sync::Arc; 3 | 4 | // pub type SharedRef = rc::Rc; 5 | pub type SharedRef = Arc; 6 | -------------------------------------------------------------------------------- /imhamt/testing/ui/fail/ignored_must_use.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![deny(warnings)] 3 | 4 | use imhamt::*; 5 | use std::collections::hash_map::DefaultHasher; 6 | 7 | fn main() {} 8 | 9 | fn check_insert(map: Hamt) { 10 | map.insert(1, 2).unwrap(); 11 | } 12 | fn check_remove(map: Hamt) { 13 | map.remove(&1).unwrap(); 14 | } 15 | fn check_remove_match(map: Hamt) { 16 | map.remove_match(&1, &2).unwrap(); 17 | } 18 | fn check_replace(map: Hamt) { 19 | map.replace(&1, 2).unwrap(); 20 | } 21 | fn check_update(map: Hamt) { 22 | map.update(&1, update).unwrap(); 23 | } 24 | fn check_insert_or_update(map: Hamt) { 25 | map.insert_or_update(1, 2, update).unwrap(); 26 | } 27 | // fn check_insert_or_update_simple(map: Hamt) { 28 | // map.insert_or_update_simple(1, 2, update_simple); 29 | // } 30 | 31 | fn update(x: &i32) -> Result, std::io::Error> { 32 | Ok(Some(*x)) 33 | } 34 | 35 | fn update_simple(x: &i32) -> Option { 36 | Some(*x) 37 | } 38 | -------------------------------------------------------------------------------- /imhamt/testing/ui/fail/ignored_must_use.stderr: -------------------------------------------------------------------------------- 1 | error: unused `imhamt::Hamt` that must be used 2 | --> testing/ui/fail/ignored_must_use.rs:10:5 3 | | 4 | 10 | map.insert(1, 2).unwrap(); 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | 7 | note: the lint level is defined here 8 | --> testing/ui/fail/ignored_must_use.rs:2:9 9 | | 10 | 2 | #![deny(warnings)] 11 | | ^^^^^^^^ 12 | = note: `#[deny(unused_must_use)]` implied by `#[deny(warnings)]` 13 | = note: `Hamt`s are not modified in place, instead modified copies are returned` 14 | 15 | error: unused `imhamt::Hamt` that must be used 16 | --> testing/ui/fail/ignored_must_use.rs:13:5 17 | | 18 | 13 | map.remove(&1).unwrap(); 19 | | ^^^^^^^^^^^^^^^^^^^^^^^^ 20 | | 21 | = note: `Hamt`s are not modified in place, instead modified copies are returned` 22 | 23 | error: unused `imhamt::Hamt` that must be used 24 | --> testing/ui/fail/ignored_must_use.rs:16:5 25 | | 26 | 16 | map.remove_match(&1, &2).unwrap(); 27 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 28 | | 29 | = note: `Hamt`s are not modified in place, instead modified copies are returned` 30 | 31 | error: unused `imhamt::Hamt` in tuple element 0 that must be used 32 | --> testing/ui/fail/ignored_must_use.rs:19:5 33 | | 34 | 19 | map.replace(&1, 2).unwrap(); 35 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ 36 | | 37 | = note: `Hamt`s are not modified in place, instead modified copies are returned` 38 | 39 | error: unused `imhamt::Hamt` that must be used 40 | --> testing/ui/fail/ignored_must_use.rs:22:5 41 | | 42 | 22 | map.update(&1, update).unwrap(); 43 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 44 | | 45 | = note: `Hamt`s are not modified in place, instead modified copies are returned` 46 | 47 | error: unused `imhamt::Hamt` that must be used 48 | --> testing/ui/fail/ignored_must_use.rs:25:5 49 | | 50 | 25 | map.insert_or_update(1, 2, update).unwrap(); 51 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 52 | | 53 | = note: `Hamt`s are not modified in place, instead modified copies are returned` 54 | -------------------------------------------------------------------------------- /sparse-array/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sparse-array" 3 | version = "0.1.0" 4 | authors = ["Yevhenii Babichenko "] 5 | edition = "2021" 6 | 7 | [dev-dependencies] 8 | proptest = { git = "https://github.com/input-output-hk/proptest.git" } 9 | test-strategy = "0.1" 10 | -------------------------------------------------------------------------------- /sparse-array/src/lib.rs: -------------------------------------------------------------------------------- 1 | ///! Implementation of a sparse array storing maximum of 256 elements 2 | mod bitmap; 3 | mod fast; 4 | mod sparse_array; 5 | 6 | #[cfg(test)] 7 | mod testing; 8 | 9 | pub use crate::{ 10 | fast::{FastSparseArray, FastSparseArrayBuilder, FastSparseArrayIter}, 11 | sparse_array::{SparseArray, SparseArrayBuilder, SparseArrayIter}, 12 | }; 13 | -------------------------------------------------------------------------------- /sparse-array/src/testing.rs: -------------------------------------------------------------------------------- 1 | use proptest::prelude::*; 2 | 3 | const MAX_INDEX: usize = 255; 4 | 5 | /// Generates a list of indexes for testing sparse arrays and bitmaps. 6 | pub(crate) fn test_indexes() -> impl Strategy> { 7 | // generate the list of free & occupied positions in a sparse aray 8 | prop::bits::bool_vec::between(0, MAX_INDEX + 1).prop_map(|occupied_entries| { 9 | occupied_entries 10 | .into_iter() 11 | .enumerate() 12 | .filter(|(_, occupied)| *occupied) // take occupied positions... 13 | .map(|(i, _)| i as u8) // ...and get their indexes 14 | .collect() 15 | }) 16 | } 17 | 18 | /// Generates an ordered list of entries for sparse arrays. There are no entries with repeating 19 | /// numbers. 20 | pub(crate) fn sparse_array_test_data() -> impl Strategy> { 21 | test_indexes().prop_flat_map(|indexes: Vec| { 22 | // populate an array with the given indexes 23 | prop::collection::vec(prop::arbitrary::any::(), indexes.len()).prop_map(move |values| { 24 | let indexes = indexes.clone(); 25 | indexes.into_iter().zip(values.into_iter()).collect() 26 | }) 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /typed-bytes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "typed-bytes" 3 | version = "0.1.0" 4 | authors = ["Vincent Hanquez "] 5 | edition = "2021" 6 | keywords = [ "Bytes", "Typed", "Types", "CStruct" ] 7 | license = "MIT OR Apache-2.0" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [dependencies] 12 | -------------------------------------------------------------------------------- /typed-bytes/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018-2019 Input Output HK 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 | --------------------------------------------------------------------------------