├── .coveralls.yml ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .gitlab-ci.yml ├── .travis.yml ├── Cargo.toml ├── Dockerfile ├── LICENSE.md ├── README.md ├── accounts ├── Cargo.toml ├── src │ ├── accounts.rs │ ├── lib.rs │ └── tree │ │ └── mod.rs ├── tests │ ├── accounts.rs │ ├── mod.rs │ └── tree.rs └── tree-primitives │ ├── Cargo.toml │ └── src │ ├── accounts_proof.rs │ ├── accounts_tree_chunk.rs │ ├── accounts_tree_node.rs │ ├── address_nibbles.rs │ └── lib.rs ├── beserial ├── Cargo.toml ├── beserial_derive │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ ├── enums.rs │ │ └── option_vec.rs ├── src │ ├── lib.rs │ └── types.rs └── tests │ ├── hashsets.rs │ └── types.rs ├── block-production ├── Cargo.toml ├── src │ └── lib.rs └── tests │ └── mod.rs ├── blockchain ├── Cargo.toml ├── src │ ├── blockchain │ │ ├── error.rs │ │ ├── mod.rs │ │ └── transaction_proofs.rs │ ├── chain_info.rs │ ├── chain_metrics.rs │ ├── chain_store.rs │ ├── lib.rs │ ├── nipopow.rs │ ├── super_block_counts.rs │ ├── transaction_cache.rs │ └── transaction_store │ │ ├── blockchain.rs │ │ └── mod.rs └── tests │ ├── blockchain │ ├── mod.rs │ ├── nipopow.rs │ └── transaction_proofs.rs │ ├── chain_info.rs │ ├── chain_store.rs │ ├── mod.rs │ ├── super_block_counts.rs │ ├── transaction_cache.rs │ └── transaction_store │ └── mod.rs ├── client ├── Cargo.toml ├── client.example.toml └── src │ ├── cmdline.rs │ ├── deadlock.rs │ ├── files.rs │ ├── logging.rs │ ├── main.rs │ ├── serialization.rs │ ├── settings.rs │ └── static_env.rs ├── collections ├── Cargo.toml ├── src │ ├── lib.rs │ ├── limit_hash_set.rs │ ├── linked_list.rs │ ├── queue.rs │ └── unique_linked_list.rs └── tests │ ├── tests.rs │ └── unique_queue.rs ├── consensus ├── Cargo.toml ├── src │ ├── accounts_chunk_cache.rs │ ├── consensus.rs │ ├── consensus_agent │ │ ├── mod.rs │ │ └── requests.rs │ ├── error.rs │ ├── inventory.rs │ ├── lib.rs │ └── processing_queue.rs └── tests │ └── mod.rs ├── database ├── Cargo.toml └── src │ ├── lib.rs │ ├── lmdb.rs │ ├── traits │ ├── accounts.rs │ ├── block.rs │ ├── hash.rs │ ├── keys.rs │ └── mod.rs │ └── volatile.rs ├── docs ├── concurrency.md └── nimiq_logo_rgb_horizontal.png ├── fixed-unsigned ├── Cargo.toml ├── benches │ └── fixed_unsigned.rs ├── src │ ├── lib.rs │ └── types.rs └── tests │ └── mod.rs ├── hash ├── Cargo.toml ├── src │ ├── hmac.rs │ ├── lib.rs │ ├── pbkdf2.rs │ └── sha512.rs └── tests │ ├── hmac.rs │ ├── mod.rs │ └── pbkdf2.rs ├── key-derivation ├── Cargo.toml ├── src │ └── lib.rs └── tests │ └── mod.rs ├── keys ├── Cargo.toml ├── src │ ├── address.rs │ ├── errors.rs │ ├── key_pair.rs │ ├── lib.rs │ ├── multisig.rs │ ├── private_key.rs │ ├── public_key.rs │ └── signature.rs └── tests │ ├── mod.rs │ └── multisig.rs ├── lib ├── Cargo.toml └── src │ ├── client.rs │ ├── error.rs │ ├── lib.rs │ └── prelude.rs ├── libargon2-sys ├── Cargo.toml ├── build.rs ├── native │ ├── argon2.c │ ├── argon2.h │ ├── blake2 │ │ ├── blake2-impl.h │ │ ├── blake2.h │ │ ├── blake2b.c │ │ ├── blamka-round-opt.h │ │ └── blamka-round-ref.h │ ├── core.c │ ├── core.h │ └── opt.c └── src │ └── lib.rs ├── macros ├── Cargo.toml └── src │ └── lib.rs ├── mempool ├── Cargo.toml ├── src │ ├── filter.rs │ └── lib.rs └── tests │ ├── filter.rs │ └── mod.rs ├── messages ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── message │ └── mod.rs │ └── mod.rs ├── metrics-server ├── Cargo.toml └── src │ ├── error.rs │ ├── lib.rs │ ├── metrics │ ├── chain.rs │ ├── mempool.rs │ ├── mod.rs │ └── network.rs │ └── server │ ├── attributes.rs │ └── mod.rs ├── mnemonic ├── Cargo.toml ├── src │ ├── key_derivation.rs │ └── lib.rs └── tests │ └── mod.rs ├── network-primitives ├── Cargo.toml ├── src │ ├── address │ │ ├── mod.rs │ │ ├── net_address.rs │ │ ├── peer_address.rs │ │ ├── peer_uri.rs │ │ └── seed_list.rs │ ├── lib.rs │ ├── networks.rs │ ├── protocol.rs │ ├── services.rs │ ├── subscription.rs │ ├── time.rs │ └── version.rs └── tests │ ├── address │ ├── mod.rs │ ├── peer_address.rs │ └── peer_uri.rs │ ├── mod.rs │ ├── networks │ └── mod.rs │ └── subscription │ └── mod.rs ├── network ├── Cargo.toml ├── src │ ├── address │ │ ├── mod.rs │ │ ├── peer_address_book.rs │ │ ├── peer_address_seeder.rs │ │ └── peer_address_state.rs │ ├── connection │ │ ├── close_type.rs │ │ ├── connection_info.rs │ │ ├── connection_pool.rs │ │ ├── mod.rs │ │ ├── network_agent.rs │ │ ├── network_connection.rs │ │ └── signal_processor.rs │ ├── error.rs │ ├── lib.rs │ ├── network.rs │ ├── network_config.rs │ ├── network_metrics.rs │ ├── peer.rs │ ├── peer_channel │ │ ├── channel.rs │ │ ├── mod.rs │ │ ├── sink.rs │ │ └── stream.rs │ ├── peer_scorer.rs │ └── websocket │ │ ├── client.rs │ │ ├── error.rs │ │ ├── mod.rs │ │ ├── public_state.rs │ │ ├── reverse_proxy.rs │ │ ├── server.rs │ │ ├── shared_stream.rs │ │ ├── stream.rs │ │ └── websocket_connector.rs └── tests │ └── mod.rs ├── primitives ├── Cargo.toml ├── account │ ├── Cargo.toml │ ├── src │ │ ├── basic_account.rs │ │ ├── htlc_contract.rs │ │ ├── lib.rs │ │ └── vesting_contract.rs │ └── tests │ │ ├── account │ │ ├── htlc_contract.rs │ │ ├── mod.rs │ │ └── vesting_contract.rs │ │ └── mod.rs ├── block │ ├── Cargo.toml │ ├── src │ │ ├── block.rs │ │ ├── body.rs │ │ ├── header.rs │ │ ├── interlink.rs │ │ ├── lib.rs │ │ ├── proof.rs │ │ └── target.rs │ └── tests │ │ ├── block │ │ ├── block.rs │ │ ├── body.rs │ │ ├── header.rs │ │ ├── mod.rs │ │ └── target.rs │ │ └── mod.rs ├── src │ ├── account.rs │ ├── coin.rs │ ├── lib.rs │ ├── networks.rs │ └── policy.rs ├── tests │ ├── coin │ │ └── mod.rs │ └── mod.rs └── transaction │ ├── Cargo.toml │ ├── src │ ├── account │ │ └── mod.rs │ └── lib.rs │ └── tests │ └── mod.rs ├── rpc-server ├── Cargo.toml └── src │ ├── error.rs │ ├── jsonrpc.rs │ └── lib.rs ├── scripts ├── coverage.sh ├── deps.py ├── docker_config.sh ├── docker_run.sh └── publish.py └── utils ├── Cargo.toml ├── src ├── bit_vec.rs ├── crc.rs ├── iterators.rs ├── lib.rs ├── locking.rs ├── merkle.rs ├── mutable_once.rs ├── observer.rs ├── rate_limit.rs ├── throttled_queue.rs ├── time.rs ├── timers.rs ├── unique_id.rs └── unique_ptr.rs └── tests ├── crc └── mod.rs ├── iterators └── mod.rs ├── merkle └── mod.rs ├── mod.rs ├── observer └── mod.rs ├── rate_limit └── mod.rs ├── throttled_queue └── mod.rs └── unique_id └── mod.rs /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-pro 2 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at hello@nimiq.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | For anyone looking to get involved to this project, we are glad to hear from you. Here are a few of the kind of contributions 4 | that we would be interested. 5 | 6 | * Bug fixes 7 | - If you find a bug, please first report it using GitHub issues. 8 | - Issues that have already been identified as a bug will be labeled `bug`. 9 | - If you'd like to submit a fix for a bug, create a Pull Request from your own fork and mention the issue number. 10 | + Include a test that isolates the bug and verifies that it was fixed. 11 | * New Features 12 | - If you'd like to accomplish something in the library that it doesn't already do, describe the problem in a new 13 |       GitHub issue. 14 | - Issues that have been identified as a feature request will be labeled `enhancement`. 15 | - If you'd like to implement the new feature, please wait for feedback from the project maintainers before spending 16 | too much time writing the code. In some cases, `enhancement`s may not align well with the project objectives at 17 | the time. 18 | * Tests, Documentation, Miscellaneous 19 | - If you think the test coverage could be improved, the documentation could be clearer, you've got an alternative 20 |       implementation of something that may have more advantages, or any other change we would be glad hear about 21 | it. 22 | + If it's a trivial change, go ahead and create a Pull Request with the changes you have in mind. 23 | + If not, open a GitHub issue to discuss the idea first. 24 | 25 | ## Requirements 26 | 27 | For a contribution to be accepted: 28 | 29 | * The test suite must be complete and pass 30 | * Code must follow existing styling conventions 31 | * Commit messages must be descriptive. Related issues should be mentioned by number. 32 | 33 | If the contribution doesn't meet these criteria, a maintainer will discuss with you on the issue. You can still 34 | continue to add more commits to the branch you have sent the Pull Request from. 35 | 36 | ## How To Start 37 | 38 | 1. Fork this repository on GitHub. 39 | 1. Clone/fetch your fork to your local development machine. 40 | 1. Create a new branch (e.g. `issue-12`, `feat.add_foo`, etc) and check it out. 41 | 1. Make your changes and commit them. (Did the tests pass?) 42 | 1. Push your new branch to your fork. (e.g. `git push myname issue-12`) 43 | 1. Open a Pull Request from your new branch to the original fork's `master` branch. 44 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## New issue checklist 2 | 3 | 4 | - [ ] I have read all of the [`README`](https://github.com/nimiq-network/core/blob/master/README.md) 5 | - [ ] I have searched [existing issues](https://github.com/nimiq-network/core/issues?q=is%3Aissue+sort%3Acreated-desc) and **this is not a duplicate**. 6 | 7 | ### General information 8 | 9 | - Library version(s): 10 | - Browser version(s): 11 | - Devices/Simulators/Machine affected: 12 | - Reproducible in the testnet? (Yes/No): 13 | - Related issues: 14 | 15 | ## Bug report 16 | 17 | #### Expected behavior 18 | 19 | > ... 20 | 21 | #### Actual behavior 22 | 23 | > ... 24 | 25 | #### Steps to reproduce 26 | 27 | > ... 28 | 29 | #### Crash log? Screenshots? Videos? Sample project? 30 | 31 | >... 32 | 33 | ## Question or Feature Request 34 | 35 | > ... 36 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Pull request checklist 2 | 3 | - [ ] All tests pass. Demo project builds and runs. 4 | - [ ] I have resolved any merge conflicts. 5 | 6 | #### This fixes issue #___. 7 | 8 | ## What's in this pull request? 9 | 10 | >... 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /cov 2 | /target 3 | **/*.rs.bk 4 | /.idea 5 | *.iml 6 | .DS_Store 7 | /client/db 8 | /client/peer_key.dat 9 | Cargo.lock 10 | /fixed-unsigned/target 11 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: 'rust:latest' 2 | variables: 3 | CARGO_HOME: $CI_PROJECT_DIR/cargo 4 | APT_CACHE_DIR: $CI_PROJECT_DIR/apt 5 | 6 | stages: 7 | - build 8 | - image 9 | - test 10 | - coverage 11 | - deploy 12 | 13 | .build: 14 | stage: build 15 | script: 16 | - rustc --version 17 | - cargo --version 18 | - cargo build --all 19 | - cargo build --all --release 20 | artifacts: 21 | paths: 22 | - target/debug/nimiq-client 23 | - target/release/nimiq-client 24 | 25 | .test: 26 | stage: test 27 | script: 28 | - rustc --version 29 | - cargo --version 30 | - cargo test --all 31 | 32 | .coverage: 33 | stage: coverage 34 | script: 35 | - rustc --version 36 | - cargo --version 37 | - apt-get update -yq 38 | - apt-get install -y cmake libelf-dev libdw-dev binutils-dev libiberty-dev jq 39 | - ./scripts/coverage.sh --all --tests 40 | artifacts: 41 | paths: 42 | - cov/cov 43 | 44 | .clippy: 45 | stage: coverage 46 | script: 47 | - set +e 48 | - rustup component add clippy 49 | - cargo clippy --all 50 | 51 | build-nightly: 52 | extends: .build 53 | image: 'rustlang/rust:nightly' 54 | 55 | test-nightly: 56 | extends: .test 57 | image: 'rustlang/rust:nightly' 58 | # dependencies: 59 | # - build-nightly 60 | 61 | coverage-nightly: 62 | extends: .coverage 63 | image: 'rustlang/rust:nightly' 64 | 65 | clippy-nightly: 66 | extends: .clippy 67 | image: 'rustlang/rust:nightly' 68 | 69 | #build-stable: 70 | # extends: .build 71 | 72 | #test-stable: 73 | # extends: .test 74 | # dependencies: 75 | # - build-stable 76 | 77 | #coverage-stable: 78 | # extends: .coverage 79 | # dependencies: 80 | # - build-stable 81 | 82 | .pages: 83 | stage: deploy 84 | script: 85 | - cargo doc --no-deps 86 | - mv target/doc public 87 | - echo '' > public/index.html 88 | artifacts: 89 | paths: 90 | - public 91 | only: 92 | - master 93 | 94 | # stable 95 | #pages: 96 | # extends: .pages 97 | 98 | # nightly 99 | #pages: 100 | # extends: .pages 101 | # image: 'rustlang/rust:nightly' 102 | 103 | docker: 104 | stage: image 105 | image: docker:18 106 | only: 107 | - branches 108 | services: 109 | - docker:18-dind 110 | dependencies: 111 | - build-nightly 112 | before_script: 113 | - docker info 114 | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY 115 | script: 116 | - > 117 | case "$CI_COMMIT_REF_NAME" in 118 | master) TAG=latest ;; 119 | *) TAG=$(echo $CI_COMMIT_REF_NAME | sed -e 's/\//-/g') ;; 120 | esac 121 | - docker pull $CI_REGISTRY_IMAGE:$TAG || true 122 | - docker build --tag $CI_REGISTRY_IMAGE:$TAG . 123 | - docker push $CI_REGISTRY_IMAGE:$TAG 124 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | # See travis-ci/travis-ci#9061 4 | sudo: required 5 | 6 | # Cache cargo for faster build 7 | cache: 8 | directories: 9 | - /home/travis/.cargo 10 | 11 | # Dependencies of kcov, used by coverage 12 | addons: 13 | apt: 14 | packages: 15 | - libcurl4-openssl-dev 16 | - libelf-dev 17 | - libdw-dev 18 | - binutils-dev 19 | - cmake # also required for cargo-update 20 | sources: 21 | - kalakris-cmake 22 | 23 | # run builds for all the trains (and more) 24 | rust: 25 | - stable 26 | - beta 27 | - nightly 28 | 29 | matrix: 30 | allow_failures: 31 | - rust: stable 32 | - rust: beta 33 | fast_finish: true 34 | 35 | before_script: 36 | - export PATH=$HOME/.cargo/bin:$PATH 37 | - rustup component add clippy || export NO_CLIPPY=1 38 | - cargo install cargo-update || echo "cargo-update already installed" 39 | - cargo install cargo-travis || echo "cargo-travis already installed" 40 | - cargo install-update -a # update outdated cached binaries 41 | - pip install --user python-coveralls 42 | 43 | script: 44 | - if [ -z ${NO_CLIPPY+x} ]; then cargo clippy --all-features; fi 45 | - cargo test --verbose --all 46 | 47 | # Don't cache registry as it accumulates dead weight as dependencies are updated 48 | # Also downloading this from crates.io is probably faster as downloading from travis cache 49 | before_cache: 50 | - rm -rf $HOME/.cargo/registry 51 | 52 | # temporarily disabled, plan is to move to codecov 53 | #after_success: 54 | # measure code coverage and upload to coveralls.io 55 | # - cargo coveralls --exclude-pattern /libargon2-sys,/tests,/beserial -p nimiq 56 | # - coveralls --merge=target/kcov/merged-kcov-output/coveralls.out 57 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "libargon2-sys", 4 | "beserial", 5 | "beserial/beserial_derive", 6 | "collections", 7 | "database", 8 | "hash", 9 | "macros", 10 | "keys", 11 | "key-derivation", 12 | "mnemonic", 13 | "utils", 14 | "primitives", 15 | "primitives/account", 16 | "primitives/block", 17 | "primitives/transaction", 18 | "blockchain", 19 | "accounts", 20 | "accounts/tree-primitives", 21 | "mempool", 22 | "network-primitives", 23 | "consensus", 24 | "network", 25 | "client", 26 | "rpc-server", 27 | "metrics-server", 28 | "lib", 29 | "messages", 30 | "fixed-unsigned" 31 | ] 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | RUN apt-get update \ 3 | && apt-get install -y libssl1.1 \ 4 | && rm -rf /var/lib/apt 5 | 6 | COPY ./scripts/docker_*.sh /root/ 7 | COPY ./target/debug/nimiq-client /bin/ 8 | WORKDIR /root 9 | 10 | ENV NIMIQ_HOST=localhost.localdomain \ 11 | NIMIQ_NETWORK=dev-albatross \ 12 | NIMIQ_LOG_LEVEL=debug \ 13 | NIMIQ_VALIDATOR=none \ 14 | VALIDATOR_BLOCK_DELAY=250 \ 15 | RPC_ENABLED=false 16 | 17 | EXPOSE 8443/tcp 8648/tcp 18 | 19 | VOLUME [ "/root/database" ] 20 | 21 | ENTRYPOINT [ "/bin/bash" ] 22 | CMD [ "/root/docker_run.sh" ] 23 | -------------------------------------------------------------------------------- /accounts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-accounts" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Persistent accounts storage for Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | hex = "0.3" 21 | beserial = { path = "../beserial", version = "0.2" } 22 | nimiq-keys = { path = "../keys", version = "0.2" } 23 | nimiq-primitives = { path = "../primitives", features = ["coin", "networks", "policy"], version = "0.2" } 24 | nimiq-hash = { path = "../hash", version = "0.2" } 25 | nimiq-account = { path = "../primitives/account", version = "0.2" } 26 | nimiq-block = { path = "../primitives/block", version = "0.2" } 27 | nimiq-transaction = { path = "../primitives/transaction", version = "0.2" } 28 | nimiq-database = { path = "../database", features = ["full-nimiq"], version = "0.2" } 29 | nimiq-tree-primitives = { path = "./tree-primitives", version = "0.2" } 30 | nimiq-network-primitives = { path = "../network-primitives", features = ["networks"], version = "0.2" } 31 | -------------------------------------------------------------------------------- /accounts/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate nimiq_primitives as primitives; 2 | extern crate nimiq_hash as hash; 3 | extern crate nimiq_database as database; 4 | extern crate nimiq_keys as keys; 5 | extern crate nimiq_account as account; 6 | extern crate nimiq_block as block; 7 | extern crate nimiq_transaction as transaction; 8 | extern crate nimiq_network_primitives as network_primitives; 9 | extern crate nimiq_tree_primitives as tree_primitives; 10 | 11 | pub mod tree; 12 | pub mod accounts; 13 | 14 | pub use self::accounts::Accounts; 15 | -------------------------------------------------------------------------------- /accounts/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod tree; 2 | mod accounts; -------------------------------------------------------------------------------- /accounts/tree-primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-tree-primitives" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Accounts tree primitives for Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | hex = "0.3" 21 | beserial = { path = "../../beserial", version = "0.2" } 22 | beserial_derive = { path = "../../beserial/beserial_derive", version = "0.2" } 23 | nimiq-keys = { path = "../../keys", version = "0.2" } 24 | nimiq-hash = { path = "../../hash", version = "0.2" } 25 | nimiq-account = { path = "../../primitives/account", version = "0.2" } 26 | 27 | [dev-dependencies] 28 | nimiq-primitives = { path = "../../primitives", version = "0.2", features = ["coin"] } 29 | -------------------------------------------------------------------------------- /accounts/tree-primitives/src/accounts_tree_chunk.rs: -------------------------------------------------------------------------------- 1 | use beserial::{Deserialize, Serialize}; 2 | use hash::Blake2bHash; 3 | 4 | use crate::accounts_proof::AccountsProof; 5 | use crate::accounts_tree_node::AccountsTreeNode; 6 | 7 | #[derive(Clone, Debug, Serialize, Deserialize)] 8 | pub struct AccountsTreeChunk { 9 | #[beserial(len_type(u16))] 10 | pub nodes: Vec, 11 | pub proof: AccountsProof, 12 | } 13 | 14 | impl AccountsTreeChunk { 15 | pub fn new(nodes: Vec, proof: AccountsProof) -> AccountsTreeChunk { 16 | AccountsTreeChunk { nodes, proof } 17 | } 18 | 19 | pub fn verify(&mut self) -> bool { 20 | if !self.proof.verify() { 21 | return false; 22 | } 23 | let mut last_prefix = Option::None; 24 | for node in &self.nodes { 25 | if last_prefix > Option::Some(node.prefix()) { 26 | return false; 27 | } 28 | last_prefix = Option::Some(node.prefix()); 29 | } 30 | if last_prefix > Option::Some(self.tail().prefix()) { 31 | return false; 32 | } 33 | true 34 | } 35 | 36 | #[inline] 37 | pub fn len(&self) -> usize { self.nodes.len() + 1 } 38 | 39 | #[inline] 40 | pub fn is_empty(&self) -> bool { false } 41 | 42 | #[inline] 43 | pub fn head(&self) -> &AccountsTreeNode { self.nodes.get(0).unwrap_or_else(|| self.tail()) } 44 | 45 | #[inline] 46 | pub fn terminal_nodes(&self) -> Vec<&AccountsTreeNode> { 47 | let mut vec = Vec::with_capacity(self.len()); 48 | for node in &self.nodes { 49 | vec.push(node) 50 | } 51 | vec.push(self.tail()); 52 | vec 53 | } 54 | 55 | #[inline] 56 | pub fn tail(&self) -> &AccountsTreeNode { self.proof.nodes().get(0).unwrap() } 57 | 58 | pub fn root(&self) -> Blake2bHash { self.proof.root_hash() } 59 | 60 | pub fn last_terminal_string(&self) -> Option { 61 | Some(self.tail().prefix().to_string()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /accounts/tree-primitives/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate beserial_derive; 3 | extern crate nimiq_hash as hash; 4 | extern crate nimiq_keys as keys; 5 | extern crate nimiq_account as account; 6 | 7 | pub mod accounts_proof; 8 | pub mod accounts_tree_chunk; 9 | pub mod accounts_tree_node; 10 | pub mod address_nibbles; 11 | -------------------------------------------------------------------------------- /beserial/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "beserial" 3 | version = "0.2.0" 4 | authors = ["Marvin W "] 5 | edition = "2018" 6 | description = "A serializer that uses BigEndian encoding that also allows to specify types for length fields" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain", "serialization"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | byteorder = "1.2" 21 | num = "0.2" 22 | failure = "0.1" 23 | -------------------------------------------------------------------------------- /beserial/beserial_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "beserial_derive" 3 | version = "0.2.0" 4 | authors = ["Marvin W "] 5 | edition = "2018" 6 | description = "Derive macros for beserial" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain", "serialization"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [lib] 20 | proc-macro = true 21 | 22 | [dependencies] 23 | proc-macro2 = "0.4" 24 | syn = { version = "0.15", features = ["extra-traits"] } 25 | quote = "0.6" 26 | beserial = { path = "..", version = "0.2" } 27 | -------------------------------------------------------------------------------- /beserial/beserial_derive/tests/enums.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate beserial_derive; 3 | 4 | use beserial::{Deserialize, Serialize, uvar}; 5 | 6 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)] 7 | #[repr(u8)] 8 | enum TestU8 { 9 | A = 1, 10 | B, 11 | C = 5, 12 | D, 13 | } 14 | 15 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)] 16 | #[repr(u64)] 17 | enum TestU64 { 18 | A = 1, 19 | B, 20 | C = 9223372036854775807, 21 | D, 22 | } 23 | 24 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)] 25 | #[repr(u64)] 26 | #[beserial(uvar)] 27 | enum TestUVar { 28 | A = 1, 29 | B = 2164392960, 30 | C = 9223372036854775807, 31 | D, 32 | } 33 | 34 | #[test] 35 | fn it_can_handle_value_enums_with_repr_u8() { 36 | fn reserialize(test: TestU8) -> TestU8 { 37 | let v = Serialize::serialize_to_vec(&test); 38 | return Deserialize::deserialize(&mut &v[..]).unwrap(); 39 | } 40 | assert_eq!(reserialize(TestU8::A), TestU8::A); 41 | assert_eq!(reserialize(TestU8::B), TestU8::B); 42 | assert_eq!(reserialize(TestU8::C), TestU8::C); 43 | assert_eq!(reserialize(TestU8::D), TestU8::D); 44 | assert_eq!(Serialize::serialize_to_vec(&TestU8::A)[0], 1); 45 | assert_eq!(Serialize::serialize_to_vec(&TestU8::B)[0], 2); 46 | assert_eq!(Serialize::serialize_to_vec(&TestU8::C)[0], 5); 47 | assert_eq!(Serialize::serialize_to_vec(&TestU8::D)[0], 6); 48 | } 49 | 50 | #[test] 51 | fn it_can_handle_value_enums_with_repr_u64() { 52 | fn reserialize(test: TestU64) -> TestU64 { 53 | let v = Serialize::serialize_to_vec(&test); 54 | return Deserialize::deserialize(&mut &v[..]).unwrap(); 55 | } 56 | fn reserialize_to_num(test: TestU64) -> u64 { 57 | let v = Serialize::serialize_to_vec(&test); 58 | return Deserialize::deserialize(&mut &v[..]).unwrap(); 59 | } 60 | assert_eq!(reserialize(TestU64::A), TestU64::A); 61 | assert_eq!(reserialize(TestU64::B), TestU64::B); 62 | assert_eq!(reserialize(TestU64::C), TestU64::C); 63 | assert_eq!(reserialize(TestU64::D), TestU64::D); 64 | assert_eq!(reserialize_to_num(TestU64::A), 1); 65 | assert_eq!(reserialize_to_num(TestU64::B), 2); 66 | assert_eq!(reserialize_to_num(TestU64::C), 9223372036854775807); 67 | assert_eq!(reserialize_to_num(TestU64::D), 9223372036854775808); 68 | } 69 | 70 | #[test] 71 | fn it_can_handle_value_enums_with_repr_uvar() { 72 | fn reserialize(test: TestUVar) -> TestUVar { 73 | let v = Serialize::serialize_to_vec(&test); 74 | return Deserialize::deserialize(&mut &v[..]).unwrap(); 75 | } 76 | fn reserialize_to_num(test: TestUVar) -> uvar { 77 | let v = Serialize::serialize_to_vec(&test); 78 | return Deserialize::deserialize(&mut &v[..]).unwrap(); 79 | } 80 | assert_eq!(reserialize(TestUVar::A), TestUVar::A); 81 | assert_eq!(reserialize(TestUVar::B), TestUVar::B); 82 | assert_eq!(reserialize(TestUVar::C), TestUVar::C); 83 | assert_eq!(reserialize(TestUVar::D), TestUVar::D); 84 | assert_eq!(reserialize_to_num(TestUVar::A), 1.into()); 85 | assert_eq!(reserialize_to_num(TestUVar::B), 2164392960.into()); 86 | assert_eq!(reserialize_to_num(TestUVar::C), 9223372036854775807.into()); 87 | assert_eq!(reserialize_to_num(TestUVar::D), 9223372036854775808.into()); 88 | } 89 | 90 | -------------------------------------------------------------------------------- /beserial/beserial_derive/tests/option_vec.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate beserial_derive; 3 | 4 | use beserial::{Deserialize, Serialize}; 5 | 6 | #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] 7 | struct TestStruct { 8 | #[beserial(len_type(u16))] 9 | test: Option> 10 | } 11 | 12 | #[test] 13 | fn it_correctly_serializes_and_deserializes_option_of_vec() { 14 | fn reserialize(s: &TestStruct) -> TestStruct { 15 | let mut v = Vec::with_capacity(s.serialized_size()); 16 | Serialize::serialize(&s, &mut v).unwrap(); 17 | let s2: TestStruct = Deserialize::deserialize(&mut &v[..]).unwrap(); 18 | return s2; 19 | } 20 | let mut test = TestStruct { test: None }; 21 | assert_eq!(test.serialized_size(), 1); 22 | assert_eq!(reserialize(&test), test); 23 | test = TestStruct { test: Some(Vec::new()) }; 24 | assert_eq!(test.serialized_size(), 3); 25 | assert_eq!(reserialize(&test), test); 26 | test = TestStruct { test: Some(vec![1, 2, 3]) }; 27 | assert_eq!(test.serialized_size(), 6); 28 | assert_eq!(reserialize(&test), test); 29 | } 30 | -------------------------------------------------------------------------------- /beserial/tests/hashsets.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use beserial::{Deserialize, DeserializeWithLength, Serialize, SerializeWithLength}; 3 | 4 | #[test] 5 | fn it_correctly_serializes_and_deserializes_hashsets() { 6 | fn reserialize(s: HashSet) -> HashSet 7 | where T: Serialize + Deserialize + std::cmp::Eq + std::hash::Hash 8 | { 9 | let mut v = Vec::with_capacity(64); 10 | SerializeWithLength::serialize::>(&s, &mut v).unwrap(); 11 | let s2: HashSet = DeserializeWithLength::deserialize::(&mut &v[..]).unwrap(); 12 | return s2; 13 | } 14 | 15 | let mut hashset = HashSet::new(); 16 | hashset.insert(18446744073709551615u64); 17 | hashset.insert(9223372036854775807); 18 | hashset.insert(381073568934759237); 19 | hashset.insert(2131907967678687); 20 | hashset.insert(255); 21 | hashset.insert(16); 22 | 23 | let reference_hashset = hashset.clone(); 24 | let processed_hashset = reserialize(hashset); 25 | 26 | assert_eq!(reference_hashset, processed_hashset); 27 | 28 | assert!(processed_hashset.contains(&18446744073709551615)); 29 | assert!(processed_hashset.contains(&9223372036854775807)); 30 | assert!(processed_hashset.contains(&381073568934759237)); 31 | assert!(processed_hashset.contains(&2131907967678687)); 32 | assert!(processed_hashset.contains(&255)); 33 | assert!(processed_hashset.contains(&16)); 34 | 35 | assert!(!processed_hashset.contains(&0)); 36 | } 37 | -------------------------------------------------------------------------------- /beserial/tests/types.rs: -------------------------------------------------------------------------------- 1 | use beserial::{Deserialize, Serialize, DeserializeWithLength, SerializeWithLength, uvar}; 2 | 3 | 4 | 5 | #[test] 6 | fn it_correctly_serializes_and_deserializes_uvar() { 7 | fn reserialize(u: u64) -> u64 { 8 | let uu: uvar = u.into(); 9 | let mut v = Vec::with_capacity(9); 10 | Serialize::serialize(&uu, &mut v).unwrap(); 11 | let uv: uvar = Deserialize::deserialize(&mut &v[..]).unwrap(); 12 | return uv.into(); 13 | } 14 | assert_eq!(reserialize(0), 0); 15 | assert_eq!(reserialize(1), 1); 16 | assert_eq!(reserialize(127), 127); 17 | assert_eq!(reserialize(128), 128); 18 | assert_eq!(reserialize(4223), 4223); 19 | assert_eq!(reserialize(4224), 4224); 20 | assert_eq!(reserialize(16511), 16511); 21 | assert_eq!(reserialize(16512), 16512); 22 | assert_eq!(reserialize(16513), 16513); 23 | assert_eq!(reserialize(2113663), 2113663); 24 | assert_eq!(reserialize(2113664), 2113664); 25 | assert_eq!(reserialize(2113665), 2113665); 26 | assert_eq!(reserialize(270549119), 270549119); 27 | assert_eq!(reserialize(270549120), 270549120); 28 | assert_eq!(reserialize(270549121), 270549121); 29 | assert_eq!(reserialize(2164392959), 2164392959); 30 | assert_eq!(reserialize(2164392960), 2164392960); 31 | assert_eq!(reserialize(2164392961), 2164392961); 32 | assert_eq!(reserialize(10), 10); 33 | assert_eq!(reserialize(100), 100); 34 | assert_eq!(reserialize(1000), 1000); 35 | assert_eq!(reserialize(10000), 10000); 36 | assert_eq!(reserialize(100000), 100000); 37 | assert_eq!(reserialize(1000000), 1000000); 38 | assert_eq!(reserialize(10000000), 10000000); 39 | assert_eq!(reserialize(100000000), 100000000); 40 | assert_eq!(reserialize(1000000000), 1000000000); 41 | assert_eq!(reserialize(10000000000), 10000000000); 42 | assert_eq!(reserialize(100000000000), 100000000000); 43 | assert_eq!(reserialize(1000000000000), 1000000000000); 44 | assert_eq!(reserialize(10000000000000), 10000000000000); 45 | assert_eq!(reserialize(100000000000000), 100000000000000); 46 | assert_eq!(reserialize(1000000000000000), 1000000000000000); 47 | assert_eq!(reserialize(10000000000000000), 10000000000000000); 48 | assert_eq!(reserialize(100000000000000000), 100000000000000000); 49 | assert_eq!(reserialize(9223372036854775807), 9223372036854775807); 50 | assert_eq!(reserialize(18446744073709551615), 18446744073709551615); 51 | } 52 | 53 | #[test] 54 | fn it_serializes_and_deserializes_box() { 55 | let b = Box::new(1337); 56 | let serialized = Serialize::serialize_to_vec(&b); 57 | let deserialized: Box = Deserialize::deserialize_from_vec(&serialized).unwrap(); 58 | assert_eq!(deserialized, b); 59 | } 60 | 61 | #[test] 62 | fn it_serializes_and_deserializes_vec() { 63 | let vec = vec![1,4,7,4,3,6,9,9,4]; 64 | let serialized = SerializeWithLength::serialize_to_vec::(&vec); 65 | let deserialized: Vec = DeserializeWithLength::deserialize_from_vec::(&serialized).unwrap(); 66 | assert_eq!(deserialized, vec); 67 | } 68 | 69 | #[test] 70 | fn it_correctly_serializes_and_deserializes_string() { 71 | fn reserialize(s: String) -> String { 72 | let mut v = Vec::with_capacity(50); 73 | SerializeWithLength::serialize::>(&s, &mut v).unwrap(); 74 | let s2: String = DeserializeWithLength::deserialize::(&mut &v[..]).unwrap(); 75 | return s2; 76 | } 77 | assert!(reserialize("a".into()) == "a"); 78 | assert!(reserialize("kjsSDFsdf345SD@$%^&".into()) == "kjsSDFsdf345SD@$%^&"); 79 | assert!(reserialize("a".into()) != "b"); 80 | } 81 | -------------------------------------------------------------------------------- /block-production/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-block-production" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | license = "Apache-2.0" 6 | edition = "2018" 7 | description = "Block Production logic for the Nimiq Rust implementation" 8 | homepage = "https://nimiq.com" 9 | repository = "https://github.com/nimiq/core-rs" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | beserial = { path = "../beserial", version = "0.2" } 21 | nimiq-block = { path = "../primitives/block", version = "0.2" } 22 | nimiq-blockchain = { path = "../blockchain", version = "0.2" } 23 | nimiq-hash = { path = "../hash", version = "0.2" } 24 | nimiq-keys = { path = "../keys", version = "0.2" } 25 | nimiq-mempool = { path = "../mempool", version = "0.2" } 26 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2", features = ["networks"] } 27 | 28 | [dev-dependencies] 29 | nimiq-account = { path = "../primitives/account", version = "0.2" } 30 | nimiq-database = { path = "../database", version = "0.2" } 31 | nimiq-primitives = { path = "../primitives", version = "0.2" } 32 | nimiq-transaction = { path = "../primitives/transaction", version = "0.2" } 33 | -------------------------------------------------------------------------------- /blockchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-blockchain" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Persistent block storage for Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | parking_lot = "0.7" 21 | log = "0.4" 22 | hex = "0.3" 23 | failure = "0.1" 24 | beserial = { path = "../beserial", version = "0.2" } 25 | beserial_derive = { path = "../beserial/beserial_derive", version = "0.2" } 26 | nimiq-keys = { path = "../keys", version = "0.2" } 27 | nimiq-primitives = { path = "../primitives", version = "0.2" } 28 | nimiq-account = { path = "../primitives/account", version = "0.2" } 29 | nimiq-block = { path = "../primitives/block", version = "0.2" } 30 | nimiq-transaction = { path = "../primitives/transaction", version = "0.2" } 31 | nimiq-hash = { path = "../hash", version = "0.2" } 32 | nimiq-accounts = { path = "../accounts", version = "0.2" } 33 | nimiq-database = { path = "../database", version = "0.2", features = ["full-nimiq"] } 34 | nimiq-tree-primitives = { path = "../accounts/tree-primitives", version = "0.2" } 35 | fixed-unsigned = { path = "../fixed-unsigned", version = "0.2" } 36 | nimiq-utils = { path = "../utils", version = "0.2", features = ["observer", "unique-ptr", "iterators"] } 37 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2", features = ["networks", "time"] } 38 | 39 | [dev-dependencies] 40 | atomic = "0.4" 41 | 42 | [features] 43 | default = ["transaction-store"] 44 | metrics = [] 45 | transaction-store = [] 46 | -------------------------------------------------------------------------------- /blockchain/src/blockchain/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | 3 | use network_primitives::networks::NetworkId; 4 | 5 | #[derive(Debug, Fail, Clone, PartialEq, Eq)] 6 | pub enum BlockchainError { 7 | #[fail(display = "Invalid genesis block stored. Are you on the right network?")] 8 | InvalidGenesisBlock, 9 | #[fail(display = "Failed to load the main chain. Reset your consensus database.")] 10 | FailedLoadingMainChain, 11 | #[fail(display = "Inconsistent chain/accounts state. Reset your consensus database.")] 12 | InconsistentState, 13 | #[fail(display = "No network for: {:?}", _0)] 14 | NoNetwork(NetworkId), 15 | } 16 | -------------------------------------------------------------------------------- /blockchain/src/blockchain/transaction_proofs.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use database::ReadTransaction; 4 | use hash::Blake2bHash; 5 | use hash::Hash; 6 | use keys::Address; 7 | use tree_primitives::accounts_proof::AccountsProof; 8 | use transaction::TransactionsProof; 9 | use utils::merkle::Blake2bMerkleProof; 10 | 11 | use crate::Blockchain; 12 | 13 | impl<'env> Blockchain<'env> { 14 | pub fn get_transactions_proof(&self, block_hash: &Blake2bHash, addresses: &HashSet
) -> Option { 15 | let block = self.get_block(block_hash, /*include_forks*/ false, /*include_body*/ true)?; 16 | let body = block.body.as_ref()?; 17 | 18 | let mut matches = Vec::new(); 19 | for transaction in body.transactions.iter() { 20 | if addresses.contains(&transaction.sender) || addresses.contains(&transaction.recipient) { 21 | matches.push(transaction.clone()); 22 | } 23 | } 24 | 25 | let merkle_leaves = body.get_merkle_leaves::(); 26 | let matching_hashes: Vec = matches.iter().map(Hash::hash).collect(); 27 | let proof = Blake2bMerkleProof::new(merkle_leaves, matching_hashes); 28 | Some(TransactionsProof { 29 | transactions: matches, 30 | proof, 31 | }) 32 | } 33 | 34 | pub fn get_accounts_proof(&self, block_hash: &Blake2bHash, addresses: &[Address]) -> Option { 35 | let state = self.state.read(); 36 | // We only support accounts proofs for the head hash. 37 | if block_hash != &state.head_hash { 38 | return None; 39 | } 40 | let txn = ReadTransaction::new(self.env); 41 | Some(state.accounts.get_accounts_proof(&txn, addresses)) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /blockchain/src/chain_metrics.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | use crate::PushResult; 4 | 5 | #[derive(Default)] 6 | pub struct BlockchainMetrics { 7 | block_invalid_count: AtomicUsize, 8 | block_orphan_count: AtomicUsize, 9 | block_known_count: AtomicUsize, 10 | block_extended_count: AtomicUsize, 11 | block_rebranched_count: AtomicUsize, 12 | block_forked_count: AtomicUsize, 13 | } 14 | 15 | impl BlockchainMetrics { 16 | #[inline] 17 | pub fn note(&self, push_result: PushResult) { 18 | match push_result { 19 | PushResult::Invalid(_) => self.note_invalid_block(), 20 | PushResult::Orphan => self.note_orphan_block(), 21 | PushResult::Known => self.note_known_block(), 22 | PushResult::Extended => self.note_extended_block(), 23 | PushResult::Rebranched => self.note_rebranched_block(), 24 | PushResult::Forked => self.note_forked_block(), 25 | } 26 | } 27 | 28 | #[inline] 29 | pub fn note_invalid_block(&self) { 30 | self.block_invalid_count.fetch_add(1, Ordering::Release); 31 | } 32 | 33 | #[inline] 34 | pub fn block_invalid_count(&self) -> usize { 35 | self.block_invalid_count.load(Ordering::Acquire) 36 | } 37 | 38 | #[inline] 39 | pub fn note_orphan_block(&self) { 40 | self.block_orphan_count.fetch_add(1, Ordering::Release); 41 | } 42 | 43 | #[inline] 44 | pub fn block_orphan_count(&self) -> usize { 45 | self.block_orphan_count.load(Ordering::Acquire) 46 | } 47 | 48 | #[inline] 49 | pub fn note_known_block(&self) { 50 | self.block_known_count.fetch_add(1, Ordering::Release); 51 | } 52 | 53 | #[inline] 54 | pub fn block_known_count(&self) -> usize { 55 | self.block_known_count.load(Ordering::Acquire) 56 | } 57 | 58 | #[inline] 59 | pub fn note_extended_block(&self) { 60 | self.block_extended_count.fetch_add(1, Ordering::Release); 61 | } 62 | 63 | #[inline] 64 | pub fn block_extended_count(&self) -> usize { 65 | self.block_extended_count.load(Ordering::Acquire) 66 | } 67 | 68 | #[inline] 69 | pub fn note_rebranched_block(&self) { 70 | self.block_rebranched_count.fetch_add(1, Ordering::Release); 71 | } 72 | 73 | #[inline] 74 | pub fn block_rebranched_count(&self) -> usize { 75 | self.block_rebranched_count.load(Ordering::Acquire) 76 | } 77 | 78 | #[inline] 79 | pub fn note_forked_block(&self) { 80 | self.block_forked_count.fetch_add(1, Ordering::Release); 81 | } 82 | 83 | #[inline] 84 | pub fn block_forked_count(&self) -> usize { 85 | self.block_forked_count.load(Ordering::Acquire) 86 | } 87 | } -------------------------------------------------------------------------------- /blockchain/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate beserial_derive; 3 | #[macro_use] 4 | extern crate log; 5 | 6 | extern crate nimiq_accounts as accounts; 7 | extern crate nimiq_primitives as primitives; 8 | extern crate nimiq_hash as hash; 9 | extern crate nimiq_database as database; 10 | extern crate nimiq_network_primitives as network_primitives; 11 | extern crate nimiq_utils as utils; 12 | extern crate nimiq_keys as keys; 13 | extern crate nimiq_account as account; 14 | extern crate nimiq_block as block; 15 | extern crate nimiq_transaction as transaction; 16 | extern crate nimiq_tree_primitives as tree_primitives; 17 | 18 | pub mod chain_info; 19 | pub mod chain_store; 20 | pub mod blockchain; 21 | pub mod super_block_counts; 22 | pub mod transaction_cache; 23 | pub mod nipopow; 24 | #[cfg(feature = "metrics")] 25 | pub mod chain_metrics; 26 | #[cfg(feature = "transaction-store")] 27 | pub mod transaction_store; 28 | 29 | pub use self::blockchain::{Blockchain, BlockchainEvent, PushResult, PushError}; 30 | pub use self::blockchain::error::BlockchainError; 31 | pub use self::chain_store::Direction; 32 | -------------------------------------------------------------------------------- /blockchain/src/transaction_store/blockchain.rs: -------------------------------------------------------------------------------- 1 | use hash::Blake2bHash; 2 | use keys::Address; 3 | use transaction::TransactionReceipt; 4 | use database::ReadTransaction; 5 | 6 | use crate::blockchain::Blockchain; 7 | use crate::transaction_store::TransactionInfo; 8 | 9 | impl From for TransactionReceipt { 10 | fn from(info: TransactionInfo) -> Self { 11 | TransactionReceipt { 12 | transaction_hash: info.transaction_hash, 13 | block_hash: info.block_hash, 14 | block_height: info.block_height, 15 | } 16 | } 17 | } 18 | 19 | impl<'env> Blockchain<'env> { 20 | pub fn get_transaction_receipts_by_address(&self, address: &Address, sender_limit: usize, recipient_limit: usize) -> Vec { 21 | let mut receipts; 22 | 23 | let txn = ReadTransaction::new(self.env); 24 | receipts = self.transaction_store.get_by_sender(address, sender_limit, Some(&txn)); 25 | receipts.extend(self.transaction_store.get_by_recipient(address, recipient_limit, Some(&txn))); 26 | 27 | receipts.drain(..).map(TransactionReceipt::from).collect() 28 | } 29 | 30 | pub fn get_transaction_info_by_hash(&self, transaction_hash: &Blake2bHash) -> Option { 31 | self.transaction_store.get_by_hash(transaction_hash, None) 32 | } 33 | } -------------------------------------------------------------------------------- /blockchain/tests/blockchain/transaction_proofs.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::sync::Arc; 3 | 4 | use beserial::Serialize; 5 | use nimiq_blockchain::{Blockchain, PushResult}; 6 | use nimiq_database::volatile::VolatileEnvironment; 7 | use nimiq_hash::Hash; 8 | use nimiq_keys::{Address, KeyPair, PrivateKey}; 9 | use nimiq_network_primitives::time::NetworkTime; 10 | use nimiq_primitives::coin::Coin; 11 | use nimiq_primitives::networks::NetworkId; 12 | use nimiq_transaction::{SignatureProof, Transaction}; 13 | 14 | #[test] 15 | fn it_can_compute_trivial_transactions_proof() { 16 | let keypair: KeyPair = PrivateKey::from([1u8; PrivateKey::SIZE]).into(); 17 | 18 | let env = VolatileEnvironment::new(10).unwrap(); 19 | let blockchain = Blockchain::new(&env, NetworkId::Main, Arc::new(NetworkTime::new())).unwrap(); 20 | 21 | let miner = Address::from(&keypair.public); 22 | let block2 = crate::next_block(&blockchain) 23 | .with_miner(miner.clone()) 24 | .with_nonce(34932) 25 | .build(); 26 | 27 | let mut status = blockchain.push(block2); 28 | assert_eq!(status, PushResult::Extended); 29 | 30 | // Push block 3 containing a tx. 31 | let mut tx = Transaction::new_basic( 32 | miner.clone(), 33 | [2u8; Address::SIZE].into(), 34 | Coin::from_u64(10).unwrap(), 35 | Coin::from_u64(0).unwrap(), 36 | 1, 37 | NetworkId::Main 38 | ); 39 | tx.proof = SignatureProof::from(keypair.public.clone(), keypair.sign(&tx.serialize_content())).serialize_to_vec(); 40 | 41 | let block3 = crate::next_block(&blockchain) 42 | .with_miner(miner.clone()) 43 | .with_transactions(vec![tx.clone()]) 44 | .with_nonce(23026) 45 | .build(); 46 | let body_hash = block3.header.body_hash.clone(); 47 | let block_hash = block3.header.hash(); 48 | status = blockchain.push(block3); 49 | assert_eq!(status, PushResult::Extended); 50 | 51 | // Generate transactions proof. 52 | let mut addresses = HashSet::new(); 53 | addresses.insert(miner.clone()); 54 | let transactions_proof = blockchain.get_transactions_proof( 55 | &block_hash, 56 | &addresses, 57 | ).unwrap(); 58 | 59 | let root_hash = transactions_proof.proof 60 | .compute_root_from_values(&transactions_proof.transactions[..]).unwrap(); 61 | assert_eq!(root_hash, body_hash); 62 | } 63 | -------------------------------------------------------------------------------- /blockchain/tests/chain_info.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | use fixed_unsigned::types::FixedUnsigned10; 3 | 4 | use beserial::{Deserialize, Serialize}; 5 | use nimiq_blockchain::{super_block_counts::SuperBlockCounts, chain_info::ChainInfo}; 6 | use nimiq_network_primitives::networks::get_network_info; 7 | use nimiq_block::Difficulty; 8 | use nimiq_primitives::networks::NetworkId; 9 | 10 | #[test] 11 | fn it_is_correctly_initialized() { 12 | let genesis_block = get_network_info(NetworkId::Main).unwrap().genesis_block.clone(); 13 | let chain_info = ChainInfo::initial(genesis_block.clone()); 14 | let mut super_block_counts = SuperBlockCounts::default(); 15 | super_block_counts.add(0); // Depth for target is 0 16 | assert_eq!(chain_info.head, genesis_block); 17 | assert_eq!(chain_info.total_difficulty, Difficulty::from(1)); 18 | assert_eq!(FixedUnsigned10::from(chain_info.total_work), FixedUnsigned10::from_str("1.8842573476").unwrap()); 19 | assert_eq!(chain_info.on_main_chain, true); 20 | assert_eq!(chain_info.main_chain_successor, None); 21 | assert_eq!(chain_info.super_block_counts, super_block_counts); 22 | } 23 | 24 | #[test] 25 | fn it_can_be_serialized_and_deserialized() { 26 | let mut genesis_block = get_network_info(NetworkId::Main).unwrap().genesis_block.clone(); 27 | genesis_block.body = None; 28 | let chain_info = ChainInfo::initial(genesis_block); 29 | 30 | let mut v: Vec = Vec::with_capacity(chain_info.serialized_size()); 31 | chain_info.serialize(&mut v).unwrap(); 32 | let chain_info2: ChainInfo = Deserialize::deserialize(&mut &v[..]).unwrap(); 33 | assert_eq!(chain_info, chain_info2); 34 | } 35 | 36 | #[test] 37 | fn serialize_strips_body() { 38 | let genesis_block = get_network_info(NetworkId::Main).unwrap().genesis_block.clone(); 39 | let mut chain_info = ChainInfo::initial(genesis_block.clone()); 40 | 41 | let mut v: Vec = Vec::with_capacity(chain_info.serialized_size()); 42 | chain_info.serialize(&mut v).unwrap(); 43 | let chain_info2: ChainInfo = Deserialize::deserialize(&mut &v[..]).unwrap(); 44 | 45 | chain_info.head.body = None; 46 | assert_eq!(chain_info, chain_info2); 47 | } 48 | 49 | #[test] 50 | fn it_calculates_successor_correctly() { 51 | let genesis_block = get_network_info(NetworkId::Main).unwrap().genesis_block.clone(); 52 | let chain_info = ChainInfo::initial(genesis_block.clone()); 53 | let next_info = chain_info.next(genesis_block.clone()); 54 | let mut super_block_counts = SuperBlockCounts::default(); 55 | super_block_counts.add(0); // Depth for target is 0 56 | super_block_counts.add(0); // Two genesis blocks means two superblocks at depth 0 57 | assert_eq!(next_info.head, genesis_block); 58 | assert_eq!(next_info.total_difficulty, Difficulty::from(2)); 59 | assert_eq!(FixedUnsigned10::from(next_info.total_work), FixedUnsigned10::from_str("3.7685146952").unwrap()); 60 | assert_eq!(next_info.on_main_chain, false); 61 | assert_eq!(next_info.main_chain_successor, None); 62 | assert_eq!(next_info.super_block_counts, super_block_counts); 63 | } 64 | -------------------------------------------------------------------------------- /client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-client" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Nimiq's Rust client" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | exclude = ["db", "peer_key.dat"] 13 | 14 | [badges] 15 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 16 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 17 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 18 | maintenance = { status = "experimental" } 19 | 20 | [dependencies] 21 | serde = "1.0" 22 | serde_derive = "1.0" 23 | toml = "0.5" 24 | log = "0.4" 25 | fern = { version = "0.5", features = ["colored"] } 26 | futures = "0.1" 27 | tokio = "0.1" 28 | lazy_static = "1.2" 29 | parking_lot = { version = "0.7", optional = true, features = ["deadlock_detection"] } 30 | clap = "2.32" 31 | colored = "1.7" 32 | chrono = "0.4" 33 | failure = "0.1" 34 | url = "1.7" 35 | hex = "0.3" 36 | directories = "1.0" 37 | human-panic = { version = "1.0", optional = true } 38 | log-panics = { version = "2.0", features = ["with-backtrace"] } 39 | nimiq-database = { path = "../database", version = "0.2" } 40 | nimiq-network = { path = "../network", version = "0.2" } 41 | nimiq-primitives = { path = "../primitives", version = "0.2", features = ["networks", "coin"] } 42 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2" } 43 | nimiq-rpc-server = { path = "../rpc-server", version = "0.2", optional = true } 44 | nimiq-metrics-server = { path = "../metrics-server", version = "0.2", optional = true } 45 | nimiq-mempool = { path = "../mempool", version = "0.2" } 46 | nimiq-lib = { path = "../lib", version = "0.2" } 47 | nimiq-keys = { path = "../keys", version = "0.2" } 48 | 49 | [features] 50 | default = ["all"] 51 | all = ["rpc-server", "metrics-server", "deadlock-detection", "human-panic"] 52 | rpc-server = ["nimiq-rpc-server"] 53 | metrics-server = ["nimiq-metrics-server"] 54 | deadlock-detection = ["parking_lot"] 55 | system-install = [] -------------------------------------------------------------------------------- /client/src/deadlock.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | use std::time::Duration; 3 | use parking_lot::deadlock; 4 | 5 | pub fn deadlock_detection() { 6 | // Create a background thread which checks for deadlocks every 10s 7 | thread::spawn(move || { 8 | loop { 9 | thread::sleep(Duration::from_secs(10)); 10 | let deadlocks = deadlock::check_deadlock(); 11 | if deadlocks.is_empty() { 12 | continue; 13 | } 14 | 15 | error!("{} deadlocks detected", deadlocks.len()); 16 | for (i, threads) in deadlocks.iter().enumerate() { 17 | error!("Deadlock #{}", i); 18 | for t in threads { 19 | error!("Thread Id {:#?}", t.thread_id()); 20 | error!("{:#?}", t.backtrace()); 21 | } 22 | } 23 | } 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /client/src/static_env.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | use std::mem; 3 | use parking_lot::Mutex; 4 | 5 | use database::Environment; 6 | 7 | 8 | /// A wrapper for static variables that can be initialized at run-time 9 | /// Invariants are checked dynamically, i.e. it will panic if you try to get a static reference 10 | /// if the variable wasn't initialized yet or if you try to initialize it twice. 11 | pub struct InitializedStatic { 12 | inner: Mutex>>, 13 | } 14 | 15 | impl InitializedStatic { 16 | pub fn new() -> InitializedStatic { 17 | InitializedStatic{ inner: Mutex::new(UnsafeCell::new(None)) } 18 | } 19 | 20 | /// Initialize the static variable 21 | pub fn initialize(&self, x: T) { 22 | let inner = unsafe { self.inner.lock().get().as_ref() }.unwrap(); 23 | if inner.is_some() { 24 | panic!("InitializedStatic was already initialized"); 25 | } 26 | unsafe { mem::replace(&mut *self.inner.lock().get(), Some(x) ) }; 27 | } 28 | 29 | pub fn get(&self) -> &T { 30 | let inner = unsafe { self.inner.lock().get().as_ref() }.unwrap().as_ref(); 31 | inner.expect("Static wasn't initialized yet") 32 | } 33 | } 34 | 35 | // This is needed for lazy_static 36 | // 37 | // Not sure if this is safe. We should probably synchronize initialize or set it to unsafe. 38 | unsafe impl std::marker::Sync for InitializedStatic {} 39 | 40 | 41 | pub type StaticEnvironment = InitializedStatic; 42 | lazy_static! { 43 | pub static ref ENV: StaticEnvironment = InitializedStatic::new(); 44 | } 45 | -------------------------------------------------------------------------------- /collections/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-collections" 3 | version = "0.2.0" 4 | authors = ["Pascal B "] 5 | edition = "2018" 6 | description = "A set of advanced collections for use in the Nimiq Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dev-dependencies] 20 | rand = "0.6" 21 | -------------------------------------------------------------------------------- /collections/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Required for LinkedList. 2 | #![feature(box_into_raw_non_null)] 3 | #![feature(specialization)] 4 | 5 | pub mod linked_list; 6 | pub mod unique_linked_list; 7 | pub mod queue; 8 | pub mod limit_hash_set; 9 | 10 | pub use self::linked_list::LinkedList; 11 | pub use self::unique_linked_list::UniqueLinkedList; 12 | pub use self::queue::Queue; 13 | pub use self::limit_hash_set::LimitHashSet; -------------------------------------------------------------------------------- /collections/tests/tests.rs: -------------------------------------------------------------------------------- 1 | mod unique_queue; -------------------------------------------------------------------------------- /consensus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-consensus" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Consensus implementation of Nimiq in Rust" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | parking_lot = "0.7" 21 | rand = "0.6" 22 | log = "0.4" 23 | weak-table = "0.2" 24 | failure = "0.1" 25 | futures = "0.1" 26 | tokio = "0.1" 27 | beserial = { path = "../beserial", version = "0.2" } 28 | nimiq-hash = { path = "../hash", version = "0.2" } 29 | nimiq-macros = { path = "../macros", version = "0.2" } 30 | nimiq-block = { path = "../primitives/block", version = "0.2" } 31 | nimiq-transaction = { path = "../primitives/transaction", version = "0.2" } 32 | nimiq-mempool = { path = "../mempool", version = "0.2" } 33 | nimiq-collections = { path = "../collections", version = "0.2" } 34 | nimiq-messages = { path = "../messages", version = "0.2" } 35 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2", features = ["networks", "time"] } 36 | nimiq-network = { path = "../network", version = "0.2" } 37 | nimiq-database = { path = "../database", version = "0.2", features = ["full-nimiq"] } 38 | nimiq-utils = { path = "../utils", version = "0.2", features = ["observer", "timers", "mutable-once", "throttled-queue", "rate-limit"] } 39 | nimiq-blockchain = { path = "../blockchain", version = "0.2", features = ["transaction-store"] } 40 | -------------------------------------------------------------------------------- /consensus/src/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | 3 | use network::error::Error as NetworkError; 4 | use blockchain::BlockchainError; 5 | 6 | #[derive(Fail, Debug)] 7 | pub enum Error { 8 | #[fail(display = "{}", _0)] 9 | NetworkError(#[cause] NetworkError), 10 | #[fail(display = "{}", _0)] 11 | BlockchainError(#[cause] BlockchainError), 12 | } 13 | 14 | impl From for Error { 15 | fn from(e: NetworkError) -> Self { 16 | Error::NetworkError(e) 17 | } 18 | } 19 | 20 | impl From for Error { 21 | fn from(e: BlockchainError) -> Self { 22 | Error::BlockchainError(e) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /consensus/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | #[macro_use] 4 | extern crate nimiq_macros as macros; 5 | 6 | extern crate nimiq_mempool as mempool; 7 | extern crate nimiq_blockchain as blockchain; 8 | extern crate nimiq_messages as network_messages; 9 | extern crate nimiq_network_primitives as network_primitives; 10 | extern crate nimiq_utils as utils; 11 | extern crate nimiq_network as network; 12 | extern crate nimiq_database as database; 13 | extern crate nimiq_hash as hash; 14 | extern crate nimiq_block as block; 15 | extern crate nimiq_transaction as transaction; 16 | extern crate nimiq_collections as collections; 17 | 18 | pub mod consensus; 19 | pub mod consensus_agent; 20 | pub mod inventory; 21 | pub mod error; 22 | mod accounts_chunk_cache; 23 | -------------------------------------------------------------------------------- /consensus/src/processing_queue.rs: -------------------------------------------------------------------------------- 1 | use futures::sync::mpsc::{unbounded, UnboundedSender, UnboundedReceiver}; 2 | use futures::stream::Stream; 3 | use futures::{future, Future}; 4 | 5 | pub trait Forward { 6 | type ItemType; 7 | fn forward(value: Self::ItemType); 8 | } 9 | 10 | pub struct ProcessingQueue { 11 | consumer: Option>, 12 | producer: UnboundedSender, 13 | } 14 | 15 | impl ProcessingQueue { 16 | pub fn new(f: F) { 17 | let consumer = Consumer { f }; 18 | let (tx, rx) = unbounded(); 19 | ProcessingQueue { 20 | consumer: Some(rx.for_each(move |item| consumer.consume(item))), 21 | producer: tx, 22 | } 23 | } 24 | } 25 | 26 | struct Consumer { 27 | f: F, 28 | } 29 | 30 | impl Consumer { 31 | fn consume(&self, item: F::ItemType) -> impl Future { 32 | self.f.forward(item); 33 | future::ok(()) 34 | } 35 | } -------------------------------------------------------------------------------- /consensus/tests/mod.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimiq/core-rs/24a91874d50ea052cd7a5e1b603b57bb0596a4f0/consensus/tests/mod.rs -------------------------------------------------------------------------------- /database/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-database" 3 | version = "0.2.0" 4 | authors = ["Pascal B ", "The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "A LMDB database wrapper with support for volatile storage" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | log = "0.4" 21 | lmdb-zero = "0.4" 22 | fs2 = "0.4" 23 | parking_lot = "0.7" 24 | tempdir = "0.3" 25 | rand = "0.6" 26 | bitflags = "1.0" 27 | beserial = { path = "../beserial", version = "0.2" } 28 | nimiq-hash = { path = "../hash", version = "0.2", optional = true } 29 | nimiq-keys = { path = "../keys", version = "0.2", optional = true } 30 | nimiq-block = { path = "../primitives/block", version = "0.2", optional = true } 31 | nimiq-tree-primitives = { path = "../accounts/tree-primitives", version = "0.2", optional = true } 32 | 33 | [features] 34 | # Compiles this package with all features needed for the nimiq client. 35 | full-nimiq = ["hash", "block", "account", "keys"] 36 | hash = ["nimiq-hash"] 37 | block = ["nimiq-block"] 38 | account = ["nimiq-tree-primitives"] 39 | keys = ["nimiq-keys"] 40 | -------------------------------------------------------------------------------- /database/src/traits/accounts.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::io; 3 | 4 | use beserial::{Deserialize, Serialize}; 5 | use nimiq_tree_primitives::accounts_tree_node::AccountsTreeNode; 6 | use nimiq_tree_primitives::address_nibbles::AddressNibbles; 7 | 8 | use crate::{AsDatabaseBytes, FromDatabaseValue, IntoDatabaseValue}; 9 | 10 | impl AsDatabaseBytes for AddressNibbles { 11 | fn as_database_bytes(&self) -> Cow<[u8]> { 12 | // TODO: Improve AddressNibbles, so that no serialization is needed. 13 | let v = Serialize::serialize_to_vec(&self); 14 | Cow::Owned(v) 15 | } 16 | } 17 | 18 | impl IntoDatabaseValue for AccountsTreeNode { 19 | fn database_byte_size(&self) -> usize { 20 | self.serialized_size() 21 | } 22 | 23 | fn copy_into_database(&self, mut bytes: &mut [u8]) { 24 | Serialize::serialize(&self, &mut bytes).unwrap(); 25 | } 26 | } 27 | 28 | impl FromDatabaseValue for AccountsTreeNode { 29 | fn copy_from_database(bytes: &[u8]) -> io::Result where Self: Sized { 30 | let mut cursor = io::Cursor::new(bytes); 31 | Ok(Deserialize::deserialize(&mut cursor)?) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /database/src/traits/block.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use beserial::{Deserialize, Serialize}; 4 | use nimiq_block::Block; 5 | 6 | use crate::{FromDatabaseValue, IntoDatabaseValue}; 7 | 8 | impl IntoDatabaseValue for Block { 9 | fn database_byte_size(&self) -> usize { 10 | self.serialized_size() 11 | } 12 | 13 | fn copy_into_database(&self, mut bytes: &mut [u8]) { 14 | Serialize::serialize(&self, &mut bytes).unwrap(); 15 | } 16 | } 17 | 18 | impl FromDatabaseValue for Block { 19 | fn copy_from_database(bytes: &[u8]) -> io::Result where Self: Sized { 20 | let mut cursor = io::Cursor::new(bytes); 21 | Ok(Deserialize::deserialize(&mut cursor)?) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /database/src/traits/hash.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::io; 3 | 4 | use nimiq_hash::Blake2bHash; 5 | 6 | use crate::{AsDatabaseBytes, FromDatabaseValue}; 7 | 8 | impl AsDatabaseBytes for Blake2bHash { 9 | fn as_database_bytes(&self) -> Cow<[u8]> { 10 | Cow::Borrowed(self.as_bytes()) 11 | } 12 | } 13 | 14 | impl FromDatabaseValue for Blake2bHash { 15 | fn copy_from_database(bytes: &[u8]) -> io::Result where Self: Sized { 16 | Ok(bytes.into()) 17 | } 18 | } -------------------------------------------------------------------------------- /database/src/traits/keys.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::io; 3 | 4 | use nimiq_keys::Address; 5 | 6 | use crate::{AsDatabaseBytes, FromDatabaseValue}; 7 | 8 | impl AsDatabaseBytes for Address { 9 | fn as_database_bytes(&self) -> Cow<[u8]> { 10 | Cow::Borrowed(self.as_bytes()) 11 | } 12 | } 13 | 14 | impl FromDatabaseValue for Address { 15 | fn copy_from_database(bytes: &[u8]) -> io::Result where Self: Sized { 16 | Ok(bytes.into()) 17 | } 18 | } -------------------------------------------------------------------------------- /database/src/traits/mod.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::ffi::CStr; 3 | use std::io; 4 | 5 | use lmdb_zero::traits::AsLmdbBytes; 6 | 7 | #[cfg(feature = "hash")] 8 | mod hash; 9 | 10 | #[cfg(feature = "block")] 11 | mod block; 12 | 13 | #[cfg(feature = "account")] 14 | mod accounts; 15 | 16 | #[cfg(feature = "keys")] 17 | mod keys; 18 | 19 | pub trait IntoDatabaseValue { 20 | fn database_byte_size(&self) -> usize; 21 | fn copy_into_database(&self, bytes: &mut [u8]); 22 | } 23 | 24 | pub trait FromDatabaseValue { 25 | fn copy_from_database(bytes: &[u8]) -> io::Result where Self: Sized; 26 | } 27 | 28 | pub trait AsDatabaseBytes { 29 | fn as_database_bytes(&self) -> Cow<[u8]>; 30 | } 31 | 32 | // Trait implementations 33 | impl IntoDatabaseValue for [u8] { 34 | fn database_byte_size(&self) -> usize { 35 | self.len() 36 | } 37 | 38 | fn copy_into_database(&self, bytes: &mut [u8]) { 39 | bytes.copy_from_slice(self); 40 | } 41 | } 42 | 43 | impl IntoDatabaseValue for str { 44 | fn database_byte_size(&self) -> usize { 45 | self.len() 46 | } 47 | 48 | fn copy_into_database(&self, bytes: &mut [u8]) { 49 | bytes.copy_from_slice(self.as_bytes()); 50 | } 51 | } 52 | 53 | impl FromDatabaseValue for String { 54 | fn copy_from_database(bytes: &[u8]) -> io::Result where Self: Sized { 55 | Ok(String::from_utf8(bytes.to_vec()).unwrap()) 56 | } 57 | } 58 | 59 | impl FromDatabaseValue for u32 { 60 | fn copy_from_database(bytes: &[u8]) -> io::Result where Self: Sized { 61 | let lmdb_result: Result<&lmdb_zero::Unaligned, String> = lmdb_zero::traits::FromLmdbBytes::from_lmdb_bytes(bytes); 62 | Ok(lmdb_result.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?.get()) 63 | } 64 | } 65 | 66 | // Conflicting implementation: 67 | //impl FromDatabaseValue for T 68 | // where T: lmdb_zero::traits::FromLmdbBytes + ?Sized { 69 | // fn copy_from_database(bytes: &[u8]) -> io::Result where Self: Sized { 70 | // Ok(lmdb_zero::traits::FromLmdbBytes::from_lmdb_bytes(bytes).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?.to_owned()) 71 | // } 72 | //} 73 | 74 | //impl AsDatabaseBytes for T 75 | // where T: lmdb_zero::traits::AsLmdbBytes + ?Sized { 76 | // fn as_database_bytes(&self) -> Cow<[u8]> { 77 | // return Cow::Borrowed(self.as_lmdb_bytes()); 78 | // } 79 | //} 80 | 81 | macro_rules! as_lmdb_bytes { 82 | ($t: ty) => { 83 | impl AsDatabaseBytes for $t { 84 | fn as_database_bytes(&self) -> Cow<[u8]> { 85 | return Cow::Borrowed(self.as_lmdb_bytes()); 86 | } 87 | } 88 | }; 89 | } 90 | 91 | as_lmdb_bytes!(u16); 92 | as_lmdb_bytes!(i16); 93 | as_lmdb_bytes!(u32); 94 | as_lmdb_bytes!(i32); 95 | as_lmdb_bytes!(u64); 96 | as_lmdb_bytes!(i64); 97 | as_lmdb_bytes!(f32); 98 | as_lmdb_bytes!(f64); 99 | as_lmdb_bytes!(str); 100 | as_lmdb_bytes!(CStr); 101 | as_lmdb_bytes!(char); 102 | 103 | as_lmdb_bytes!([u16]); 104 | as_lmdb_bytes!([i16]); 105 | as_lmdb_bytes!([u32]); 106 | as_lmdb_bytes!([i32]); 107 | as_lmdb_bytes!([u64]); 108 | as_lmdb_bytes!([i64]); 109 | as_lmdb_bytes!([f32]); 110 | as_lmdb_bytes!([f64]); 111 | as_lmdb_bytes!([char]); 112 | -------------------------------------------------------------------------------- /docs/nimiq_logo_rgb_horizontal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimiq/core-rs/24a91874d50ea052cd7a5e1b603b57bb0596a4f0/docs/nimiq_logo_rgb_horizontal.png -------------------------------------------------------------------------------- /fixed-unsigned/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fixed-unsigned" 3 | version = "0.2.0" 4 | authors = ["Janosch Gräf "] 5 | edition = "2018" 6 | description = "Fixed precision decimal numbers for Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | keywords = ["nimiq"] 11 | 12 | [badges] 13 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 14 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 15 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 16 | maintenance = { status = "experimental" } 17 | 18 | [dependencies] 19 | num-bigint = "0.2" 20 | num-traits = "0.2" 21 | 22 | [dev-dependencies] 23 | criterion = "0.2" 24 | lazy_static = "1.2" 25 | bigdecimal = "0.1.0" 26 | 27 | [[bench]] 28 | name = "fixed_unsigned" 29 | harness = false 30 | -------------------------------------------------------------------------------- /fixed-unsigned/benches/fixed_unsigned.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate criterion; 3 | #[macro_use] 4 | extern crate lazy_static; 5 | 6 | use std::str::FromStr; 7 | use criterion::{Criterion, Benchmark}; 8 | use fixed_unsigned::types::FixedUnsigned10; 9 | use bigdecimal::BigDecimal; 10 | 11 | 12 | const NUM_1: &'static str = "9127612783.1287512387"; 13 | const NUM_2: &'static str = "1021235791.2340980123"; 14 | 15 | // get numbers at fixed precision. We want to have exactly 10 decimal places. This won't do it 16 | // exactly, but I reckon changing the precision will have the same impact, regardless of by how 17 | // much. Should be correct +- 1 digit anyway. 18 | const PRECISION: u64 = 20; 19 | 20 | 21 | lazy_static! { 22 | pub static ref FIXED_1: FixedUnsigned10 = FixedUnsigned10::from_str(NUM_1).unwrap(); 23 | pub static ref FIXED_2: FixedUnsigned10 = FixedUnsigned10::from_str(NUM_2).unwrap(); 24 | pub static ref BIGDECIMAL_1: BigDecimal = BigDecimal::from_str(NUM_1).unwrap(); 25 | pub static ref BIGDECIMAL_2: BigDecimal = BigDecimal::from_str(NUM_2).unwrap(); 26 | } 27 | 28 | 29 | 30 | fn criterion_benchmark(c: &mut Criterion) { 31 | c.bench( 32 | "from_str", 33 | Benchmark::new("FixedUnsigned", |b| b.iter(|| FixedUnsigned10::from_str(NUM_1).unwrap())) 34 | .with_function("BigDecimal", |b| b.iter(|| BigDecimal::from_str(NUM_1).unwrap())) 35 | .with_function("BigDecimal::with_prec", |b| b.iter(|| BigDecimal::from_str(NUM_1).unwrap().with_prec(PRECISION))) 36 | ); 37 | c.bench( 38 | "to_str", 39 | Benchmark::new("FixedUnsigned", |b| b.iter(|| FIXED_1.to_string())) 40 | .with_function("BigDecimal", |b| b.iter(|| BIGDECIMAL_1.to_string())) 41 | ); 42 | 43 | c.bench( 44 | "add", 45 | Benchmark::new("FixedUnsigned", |b| b.iter(|| &*FIXED_1 + &*FIXED_2)) 46 | .with_function("BigDecimal", |b| b.iter(|| &*BIGDECIMAL_1 + &*BIGDECIMAL_2)) 47 | .with_function("BigDecimal::with_prec", |b| b.iter(|| (&*BIGDECIMAL_1 + &*BIGDECIMAL_2).with_prec(PRECISION))) 48 | ); 49 | c.bench( 50 | "sub", 51 | Benchmark::new("FixedUnsigned", |b| b.iter(|| &*FIXED_1 - &*FIXED_2)) 52 | .with_function("BigDecimal", |b| b.iter(|| &*BIGDECIMAL_1 - &*BIGDECIMAL_2)) 53 | .with_function("BigDecimal::with_prec", |b| b.iter(|| (&*BIGDECIMAL_1 - &*BIGDECIMAL_2).with_prec(PRECISION))) 54 | ); 55 | c.bench( 56 | "mul", 57 | Benchmark::new("FixedUnsigned", |b| b.iter(|| &*FIXED_1 * &*FIXED_2)) 58 | .with_function("BigDecimal", |b| b.iter(|| &*BIGDECIMAL_1 * &*BIGDECIMAL_2)) 59 | .with_function("BigDecimal::with_prec", |b| b.iter(|| (&*BIGDECIMAL_1 * &*BIGDECIMAL_2).with_prec(PRECISION))) 60 | ); 61 | c.bench( 62 | "div", 63 | Benchmark::new("FixedUnsigned", |b| b.iter(|| &*FIXED_1 / &*FIXED_2)) 64 | .with_function("BigDecimal", |b| b.iter(|| &*BIGDECIMAL_1 / &*BIGDECIMAL_2)) 65 | .with_function("BigDecimal::with_prec", |b| b.iter(|| (&*BIGDECIMAL_1 / &*BIGDECIMAL_2).with_prec(PRECISION))) 66 | ); 67 | } 68 | 69 | 70 | criterion_group!(benches, criterion_benchmark); 71 | criterion_main!(benches); -------------------------------------------------------------------------------- /fixed-unsigned/src/types.rs: -------------------------------------------------------------------------------- 1 | use crate::{FixedUnsigned, FixedScale}; 2 | 3 | 4 | 5 | /*macro_rules! create_typed_array { 6 | ($scale: expr) => { 7 | pub struct FixedScale$scale {} 8 | impl ::fixed_unsigned::FixedScale for FixedScale$scale { 9 | const SCALE: u64 = $scale; 10 | } 11 | pub type FixedUnsigned$scale = FixedUnsigned; 12 | }; 13 | }*/ 14 | 15 | 16 | /// A fixed point Uint with 4 decimal places 17 | #[derive(Clone, Debug)] 18 | pub struct FixedScale4 {} 19 | impl FixedScale for FixedScale4 { 20 | const SCALE: u64 = 4; 21 | } 22 | pub type FixedUnsigned4 = FixedUnsigned; 23 | 24 | 25 | /// A fixed point Uint with 8 decimal places 26 | #[derive(Clone, Debug)] 27 | pub struct FixedScale8 {} 28 | impl FixedScale for FixedScale8 { 29 | const SCALE: u64 = 8; 30 | } 31 | pub type FixedUnsigned8 = FixedUnsigned; 32 | 33 | 34 | /// A fixed point Uint with 16 decimal places 35 | #[derive(Clone, Debug)] 36 | pub struct FixedScale16 {} 37 | impl FixedScale for FixedScale16 { 38 | const SCALE: u64 = 16; 39 | } 40 | pub type FixedUnsigned16 = FixedUnsigned; 41 | 42 | 43 | /// A fixed point Uint with 10 decimal places 44 | /// 45 | /// NOTE: This should have the same behaviour as bignumber.js (default config with 10 decimal places) 46 | #[derive(Clone, Debug)] 47 | pub struct FixedScale10 {} 48 | impl FixedScale for FixedScale10 { 49 | const SCALE: u64 = 10; 50 | } 51 | pub type FixedUnsigned10 = FixedUnsigned; 52 | 53 | 54 | /// A fixed point Uint with 10 decimal places 55 | /// 56 | /// NOTE: This should have the same behaviour as bignumber.js (default config with 10 decimal places) 57 | #[derive(Clone, Debug)] 58 | pub struct FixedScale26 {} 59 | impl FixedScale for FixedScale26 { 60 | const SCALE: u64 = 26; 61 | } 62 | pub type FixedUnsigned26 = FixedUnsigned; 63 | -------------------------------------------------------------------------------- /hash/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-hash" 3 | version = "0.2.0" 4 | authors = ["Pascal B ", "The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Common wrapper around hash implementations used in Nimiq" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | blake2-rfc = "0.2" 21 | hex = "0.3" 22 | sha2 = "0.8" 23 | byteorder = "1.2" 24 | beserial = { path = "../beserial", version = "0.2" } 25 | nimiq-macros = { path = "../macros", version = "0.2" } 26 | libargon2-sys = { path = "../libargon2-sys", version = "0.2" } 27 | -------------------------------------------------------------------------------- /hash/src/hmac.rs: -------------------------------------------------------------------------------- 1 | use super::{Sha512Hasher, Sha512Hash, Hasher, SHA512_LENGTH}; 2 | 3 | enum Key<'a> { 4 | Borrowed(&'a [u8]), 5 | Owned([u8; SHA512_LENGTH]) 6 | } 7 | 8 | impl<'a> Key<'a> { 9 | fn get(&self, index: usize) -> Option { 10 | match self { 11 | Key::Borrowed(key) => key.get(index), 12 | Key::Owned(key) => key.get(index), 13 | }.cloned() 14 | } 15 | } 16 | 17 | pub fn compute_hmac_sha512(key: &[u8], data: &[u8]) -> Sha512Hash { 18 | let hashed_key = if key.len() > Sha512Hash::block_size() { 19 | Key::Owned(Sha512Hasher::default().digest(key).into()) 20 | } else { 21 | Key::Borrowed(key) 22 | }; 23 | 24 | let mut inner_key: Vec = Vec::with_capacity(Sha512Hash::block_size()); 25 | let mut outer_key: Vec = Vec::with_capacity(Sha512Hash::block_size()); 26 | for i in 0..Sha512Hash::block_size() { 27 | let byte: u8 = hashed_key.get(i).unwrap_or(0); 28 | inner_key.push(0x36 ^ byte); 29 | outer_key.push(0x5c ^ byte); 30 | } 31 | 32 | let inner_hash = Sha512Hasher::default().chain(&inner_key).chain(&data).finish(); 33 | Sha512Hasher::default().chain(&outer_key).chain(&inner_hash).finish() 34 | } 35 | -------------------------------------------------------------------------------- /hash/src/pbkdf2.rs: -------------------------------------------------------------------------------- 1 | use super::{Sha512Hash, SHA512_LENGTH}; 2 | use super::hmac::compute_hmac_sha512; 3 | use byteorder::{BigEndian, WriteBytesExt}; 4 | use std::io::{Write, Error}; 5 | 6 | #[derive(Debug)] 7 | pub enum Pbkdf2Error { 8 | KeyTooLong, 9 | IOError(Error), 10 | } 11 | 12 | pub fn compute_pbkdf2_sha512(password: &[u8], salt: &[u8], iterations: usize, derived_key_length: usize) -> Result, Pbkdf2Error> { 13 | // Following https://www.ietf.org/rfc/rfc2898.txt 14 | if (derived_key_length as u64) > u64::from(u32::max_value()) * (Sha512Hash::len() as u64) { 15 | return Err(Pbkdf2Error::KeyTooLong); 16 | } 17 | 18 | let mut l = derived_key_length / Sha512Hash::len(); 19 | if derived_key_length % Sha512Hash::len() != 0 { 20 | l += 1; 21 | } 22 | let r = derived_key_length - (l - 1) * Sha512Hash::len(); 23 | 24 | let mut derived_key = Vec::with_capacity(derived_key_length); 25 | for i in 1..=l { 26 | let mut u: Vec = Vec::with_capacity(salt.len() + 4); 27 | u.write(salt).map_err(Pbkdf2Error::IOError)?; 28 | u.write_u32::(i as u32).map_err(Pbkdf2Error::IOError)?; 29 | 30 | let mut t: [u8; SHA512_LENGTH] = compute_hmac_sha512(password, u.as_slice()).into(); 31 | let mut u = t; 32 | for _ in 1..iterations { 33 | u = compute_hmac_sha512(password, &u[..]).into(); 34 | for k in 0..Sha512Hash::len() { 35 | t[k] ^= u[k]; 36 | } 37 | } 38 | 39 | if i < l { 40 | derived_key.write(&t[..]).map_err(Pbkdf2Error::IOError)?; 41 | } else { 42 | derived_key.write(&t[..r]).map_err(Pbkdf2Error::IOError)?; 43 | } 44 | } 45 | Ok(derived_key) 46 | } 47 | -------------------------------------------------------------------------------- /hash/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod hmac; 2 | mod pbkdf2; 3 | use nimiq_hash::{Hasher,Argon2dHasher,Argon2dHash,Sha256Hasher,Sha256Hash,Blake2bHasher,Blake2bHash,Sha512Hasher,Sha512Hash}; 4 | use std::io::Write; 5 | 6 | #[test] 7 | fn it_can_compute_sha256() { 8 | // sha256('test') = '9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08' 9 | 10 | assert_eq!(Sha256Hasher::default().digest(b"test"), Sha256Hash::from("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")); 11 | let mut h = Sha256Hasher::default(); 12 | h.write(b"te").unwrap(); 13 | h.write(b"st").unwrap(); 14 | assert_eq!(h.finish(), Sha256Hash::from("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")); 15 | } 16 | 17 | 18 | #[test] 19 | fn it_can_compute_argon2d() { 20 | // argon2d('test') = '8c259fdcc2ad6799df728c11e895a3369e9dbae6a3166ebc3b353399fc565524' 21 | 22 | assert_eq!(Argon2dHasher::default().digest(b"test"), Argon2dHash::from("8c259fdcc2ad6799df728c11e895a3369e9dbae6a3166ebc3b353399fc565524")); 23 | let mut h = Argon2dHasher::default(); 24 | h.write(b"te").unwrap(); 25 | h.write(b"st").unwrap(); 26 | assert_eq!(h.finish(), Argon2dHash::from("8c259fdcc2ad6799df728c11e895a3369e9dbae6a3166ebc3b353399fc565524")); 27 | } 28 | 29 | 30 | #[test] 31 | fn it_can_compute_blake2b() { 32 | // blake2b('test') = '928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202' 33 | 34 | assert_eq!(Blake2bHasher::default().digest(b"test"), Blake2bHash::from("928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202")); 35 | let mut h = Blake2bHasher::default(); 36 | h.write(b"te").unwrap(); 37 | h.write(b"st").unwrap(); 38 | assert_eq!(h.finish(), Blake2bHash::from("928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202")); 39 | } 40 | 41 | #[test] 42 | fn it_can_compute_sha512() { 43 | // sha512('test') = 'ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff' 44 | 45 | assert_eq!(Sha512Hasher::default().digest(b"test"), Sha512Hash::from("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff")); 46 | let mut h = Sha512Hasher::default(); 47 | h.write(b"te").unwrap(); 48 | h.write(b"st").unwrap(); 49 | assert_eq!(h.finish(), Sha512Hash::from("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff")); 50 | } 51 | -------------------------------------------------------------------------------- /hash/tests/pbkdf2.rs: -------------------------------------------------------------------------------- 1 | use nimiq_hash::pbkdf2::*; 2 | use hex::FromHex; 3 | 4 | struct TestVector { 5 | password: &'static str, 6 | salt: &'static str, 7 | iterations: usize, 8 | derived_key_length: usize, 9 | derived_key: &'static str, 10 | } 11 | 12 | impl TestVector { 13 | fn get_password(&self) -> Vec { 14 | self.password.as_bytes().to_vec() 15 | } 16 | 17 | fn get_salt(&self) -> Vec { self.salt.as_bytes().to_vec() } 18 | 19 | fn get_derived_key(&self) -> Vec { Vec::from_hex(self.derived_key).unwrap() } 20 | } 21 | 22 | #[test] 23 | fn it_correctly_computes_hmac_sha512() { 24 | // Test vectors from https://tools.ietf.org/html/rfc4231 25 | const TEST_CASES: [TestVector; 5] = [ 26 | TestVector { 27 | password: "password", 28 | salt: "salt", 29 | iterations: 1, 30 | derived_key_length: 64, 31 | derived_key: "867f70cf1ade02cff3752599a3a53dc4af34c7a669815ae5d513554e1c8cf252c02d470a285a0501bad999bfe943c08f050235d7d68b1da55e63f73b60a57fce", 32 | }, 33 | TestVector { 34 | password: "password", 35 | salt: "salt", 36 | iterations: 2, 37 | derived_key_length: 64, 38 | derived_key: "e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53cf76cab2868a39b9f7840edce4fef5a82be67335c77a6068e04112754f27ccf4e", 39 | }, 40 | TestVector { 41 | password: "password", 42 | salt: "salt", 43 | iterations: 2, 44 | derived_key_length: 32, 45 | derived_key: "e1d9c16aa681708a45f5c7c4e215ceb66e011a2e9f0040713f18aefdb866d53c", 46 | }, 47 | TestVector { 48 | password: "password", 49 | salt: "salt", 50 | iterations: 4096, 51 | derived_key_length: 64, 52 | derived_key: "d197b1b33db0143e018b12f3d1d1479e6cdebdcc97c5c0f87f6902e072f457b5143f30602641b3d55cd335988cb36b84376060ecd532e039b742a239434af2d5", 53 | }, 54 | TestVector { 55 | password: "passwordPASSWORDpassword", 56 | salt: "saltSALTsaltSALTsaltSALTsaltSALTsalt", 57 | iterations: 4096, 58 | derived_key_length: 64, 59 | derived_key: "8c0511f4c6e597c6ac6315d8f0362e225f3c501495ba23b868c005174dc4ee71115b59f9e60cd9532fa33e0f75aefe30225c583a186cd82bd4daea9724a3d3b8", 60 | } 61 | ]; 62 | 63 | for (i, vector) in TEST_CASES.iter().enumerate() { 64 | let derived_key = compute_pbkdf2_sha512(&vector.get_password()[..], &vector.get_salt()[..], vector.iterations, vector.derived_key_length); 65 | assert_eq!(derived_key.unwrap(), vector.get_derived_key(), "Invalid pbkdf2 in test case {}", i); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /key-derivation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-key-derivation" 3 | version = "0.2.0" 4 | authors = ["Pascal B ", "The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Helper library for key derivation in Nimiq" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | regex = "1" 21 | byteorder = "1.2" 22 | beserial = { path = "../beserial", version = "0.2" } 23 | nimiq-hash = { path = "../hash", version = "0.2" } 24 | nimiq-keys = { path = "../keys", version = "0.2" } 25 | 26 | [dev-dependencies] 27 | hex = "0.3" 28 | -------------------------------------------------------------------------------- /keys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-keys" 3 | version = "0.2.0" 4 | authors = ["Pascal B ", "The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Ed25519 cryptographic key handling and signatures for Nimiq" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | curve25519-dalek = "1.0.2" 21 | ed25519-dalek = "1.0.0-pre.4" 22 | data-encoding = "2.1" 23 | sha2 = "0.8" 24 | rand = "0.7" 25 | hex = "0.3" 26 | failure = "0.1" 27 | beserial = { path = "../beserial", version = "0.2" } 28 | beserial_derive = { path = "../beserial/beserial_derive", version = "0.2" } 29 | nimiq-hash = { path = "../hash", version = "0.2" } 30 | nimiq-macros = { path = "../macros", version = "0.2" } 31 | -------------------------------------------------------------------------------- /keys/src/errors.rs: -------------------------------------------------------------------------------- 1 | use ed25519_dalek::SignatureError; 2 | use failure::Fail; 3 | use hex::FromHexError; 4 | 5 | pub type KeysError = SignatureError; 6 | 7 | #[derive(Debug, Fail)] 8 | pub enum ParseError { 9 | #[fail(display = "{}", _0)] 10 | FromHex(#[cause] FromHexError), 11 | #[fail(display = "{}", _0)] 12 | KeysError(#[cause] KeysError), 13 | } 14 | 15 | impl From for ParseError { 16 | fn from(e: FromHexError) -> Self { 17 | ParseError::FromHex(e) 18 | } 19 | } 20 | 21 | impl From for ParseError { 22 | fn from(e: KeysError) -> Self { 23 | ParseError::KeysError(e) 24 | } 25 | } -------------------------------------------------------------------------------- /keys/src/key_pair.rs: -------------------------------------------------------------------------------- 1 | use rand::rngs::OsRng; 2 | 3 | use beserial::{Deserialize, Serialize}; 4 | 5 | use crate::{PrivateKey, PublicKey, Signature}; 6 | 7 | #[derive(Clone, Debug, Serialize, Deserialize)] 8 | pub struct KeyPair { 9 | pub public: PublicKey, 10 | pub private: PrivateKey, 11 | } 12 | 13 | impl KeyPair { 14 | pub fn generate() -> Self { 15 | let key_pair = ed25519_dalek::Keypair::generate(&mut OsRng); 16 | let priv_key = PrivateKey(key_pair.secret); 17 | let pub_key = PublicKey(key_pair.public); 18 | KeyPair { private: priv_key, public: pub_key } 19 | } 20 | 21 | pub fn sign(&self, data: &[u8]) -> Signature { 22 | let ext_signature = ed25519_dalek::ExpandedSecretKey::from(&self.private.0).sign(data, &self.public.0); 23 | Signature(ext_signature) 24 | } 25 | } 26 | 27 | impl From for KeyPair { 28 | fn from(private_key: PrivateKey) -> Self { 29 | KeyPair { public: PublicKey::from(&private_key), private: private_key } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /keys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate beserial_derive; 3 | #[macro_use] 4 | extern crate nimiq_hash as hash; 5 | #[macro_use] 6 | extern crate nimiq_macros as macros; 7 | extern crate failure; 8 | 9 | pub use self::address::*; 10 | pub use self::key_pair::*; 11 | pub use self::private_key::*; 12 | pub use self::public_key::*; 13 | pub use self::signature::*; 14 | pub use self::errors::*; 15 | 16 | #[macro_export] 17 | macro_rules! implement_simple_add_sum_traits { 18 | ($name: ident, $identity: expr) => { 19 | impl<'a, 'b> Add<&'b $name> for &'a $name { 20 | type Output = $name; 21 | fn add(self, other: &'b $name) -> $name { 22 | $name(self.0 + other.0) 23 | } 24 | } 25 | impl<'b> Add<&'b $name> for $name { 26 | type Output = $name; 27 | fn add(self, rhs: &'b $name) -> $name { 28 | &self + rhs 29 | } 30 | } 31 | 32 | impl<'a> Add<$name> for &'a $name { 33 | type Output = $name; 34 | fn add(self, rhs: $name) -> $name { 35 | self + &rhs 36 | } 37 | } 38 | 39 | impl Add<$name> for $name { 40 | type Output = $name; 41 | fn add(self, rhs: $name) -> $name { 42 | &self + &rhs 43 | } 44 | } 45 | 46 | impl Sum for $name 47 | where 48 | T: Borrow<$name> 49 | { 50 | fn sum(iter: I) -> Self 51 | where 52 | I: Iterator 53 | { 54 | $name(iter.fold($identity, |acc, item| acc + item.borrow().0)) 55 | } 56 | } 57 | } 58 | } 59 | 60 | pub mod multisig; 61 | 62 | mod address; 63 | mod errors; 64 | mod key_pair; 65 | mod private_key; 66 | mod public_key; 67 | mod signature; 68 | -------------------------------------------------------------------------------- /keys/src/private_key.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::fmt::Error; 3 | use std::fmt::Formatter; 4 | use std::io; 5 | 6 | use rand::rngs::OsRng; 7 | 8 | use beserial::{Deserialize, ReadBytesExt, Serialize, SerializingError, WriteBytesExt}; 9 | 10 | use hash::{Hash, SerializeContent}; 11 | 12 | pub struct PrivateKey(pub(in super) ed25519_dalek::SecretKey); 13 | 14 | impl PrivateKey { 15 | pub const SIZE: usize = 32; 16 | 17 | pub fn generate() -> Self { 18 | PrivateKey(ed25519_dalek::SecretKey::generate(&mut OsRng)) 19 | } 20 | 21 | #[inline] 22 | pub fn as_bytes(&self) -> &[u8; PrivateKey::SIZE] { self.0.as_bytes() } 23 | 24 | #[inline] 25 | pub (crate) fn as_dalek(&self) -> &ed25519_dalek::SecretKey { &self.0 } 26 | } 27 | 28 | impl<'a> From<&'a [u8; PrivateKey::SIZE]> for PrivateKey { 29 | fn from(bytes: &'a [u8; PrivateKey::SIZE]) -> Self { 30 | PrivateKey(ed25519_dalek::SecretKey::from_bytes(bytes).unwrap()) 31 | } 32 | } 33 | 34 | impl From<[u8; PrivateKey::SIZE]> for PrivateKey { 35 | fn from(bytes: [u8; PrivateKey::SIZE]) -> Self { 36 | PrivateKey::from(&bytes) 37 | } 38 | } 39 | 40 | impl Clone for PrivateKey { 41 | fn clone(&self) -> Self { 42 | let cloned_dalek = ed25519_dalek::SecretKey::from_bytes(self.0.as_bytes()).unwrap(); 43 | PrivateKey(cloned_dalek) 44 | } 45 | } 46 | 47 | impl Deserialize for PrivateKey { 48 | fn deserialize(reader: &mut R) -> Result { 49 | let mut buf = [0u8; PrivateKey::SIZE]; 50 | reader.read_exact(&mut buf)?; 51 | Ok(PrivateKey::from(&buf)) 52 | } 53 | } 54 | 55 | impl Serialize for PrivateKey { 56 | fn serialize(&self, writer: &mut W) -> Result { 57 | writer.write_all(self.as_bytes())?; 58 | Ok(self.serialized_size()) 59 | } 60 | 61 | fn serialized_size(&self) -> usize { 62 | PrivateKey::SIZE 63 | } 64 | } 65 | 66 | impl SerializeContent for PrivateKey { 67 | fn serialize_content(&self, writer: &mut W) -> io::Result { Ok(self.serialize(writer)?) } 68 | } 69 | 70 | impl Hash for PrivateKey { } 71 | 72 | impl Debug for PrivateKey { 73 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 74 | write!(f, "PrivateKey") 75 | } 76 | } 77 | 78 | impl PartialEq for PrivateKey { 79 | fn eq(&self, other: &PrivateKey) -> bool { 80 | self.as_bytes() == other.as_bytes() 81 | } 82 | } 83 | 84 | impl Eq for PrivateKey {} 85 | -------------------------------------------------------------------------------- /keys/src/public_key.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::io; 3 | use hex::FromHex; 4 | use ed25519_dalek::Verifier; 5 | 6 | use beserial::{Deserialize, ReadBytesExt, Serialize, SerializingError, WriteBytesExt}; 7 | 8 | use crate::{PrivateKey, Signature}; 9 | use hash::{Hash, SerializeContent}; 10 | use crate::errors::{KeysError, ParseError}; 11 | 12 | #[derive(Eq, PartialEq, Debug, Clone, Copy)] 13 | pub struct PublicKey(pub(in super) ed25519_dalek::PublicKey); 14 | 15 | impl PublicKey { 16 | pub const SIZE: usize = 32; 17 | 18 | pub fn verify(&self, signature: &Signature, data: &[u8]) -> bool { 19 | self.as_dalek().verify(data, signature.as_dalek()).is_ok() 20 | } 21 | 22 | #[inline] 23 | pub fn as_bytes(&self) -> &[u8; PublicKey::SIZE] { self.0.as_bytes() } 24 | 25 | #[inline] 26 | pub(crate) fn as_dalek(&self) -> &ed25519_dalek::PublicKey { &self.0 } 27 | 28 | #[inline] 29 | pub fn from_bytes(bytes: &[u8]) -> Result { 30 | Ok(PublicKey(ed25519_dalek::PublicKey::from_bytes(bytes)?)) 31 | } 32 | 33 | pub fn to_hex(&self) -> String { 34 | hex::encode(self.as_bytes()) 35 | } 36 | } 37 | 38 | impl FromHex for PublicKey { 39 | type Error = ParseError; 40 | 41 | fn from_hex>(hex: T) -> Result { 42 | Ok(PublicKey::from_bytes(hex::decode(hex)?.as_slice())?) 43 | } 44 | } 45 | 46 | impl Ord for PublicKey { 47 | fn cmp(&self, other: &PublicKey) -> Ordering { 48 | self.0.as_bytes().cmp(other.0.as_bytes()) 49 | } 50 | } 51 | 52 | impl PartialOrd for PublicKey { 53 | fn partial_cmp(&self, other: &PublicKey) -> Option { 54 | Some(self.cmp(other)) 55 | } 56 | } 57 | 58 | impl<'a> From<&'a PrivateKey> for PublicKey { 59 | fn from(private_key: &'a PrivateKey) -> Self { 60 | let public_key = ed25519_dalek::PublicKey::from(private_key.as_dalek()); 61 | PublicKey(public_key) 62 | } 63 | } 64 | 65 | impl<'a> From<&'a [u8; PublicKey::SIZE]> for PublicKey { 66 | fn from(bytes: &'a [u8; PublicKey::SIZE]) -> Self { 67 | PublicKey(ed25519_dalek::PublicKey::from_bytes(bytes).unwrap()) 68 | } 69 | } 70 | 71 | impl From<[u8; PublicKey::SIZE]> for PublicKey { 72 | fn from(bytes: [u8; PublicKey::SIZE]) -> Self { 73 | PublicKey::from(&bytes) 74 | } 75 | } 76 | 77 | impl Deserialize for PublicKey { 78 | fn deserialize(reader: &mut R) -> Result { 79 | let mut buf = [0u8; PublicKey::SIZE]; 80 | reader.read_exact(&mut buf)?; 81 | Ok(PublicKey::from_bytes(&buf).map_err(|_| SerializingError::InvalidValue)?) 82 | } 83 | } 84 | 85 | impl Serialize for PublicKey { 86 | fn serialize(&self, writer: &mut W) -> Result { 87 | writer.write_all(self.as_bytes())?; 88 | Ok(self.serialized_size()) 89 | } 90 | 91 | fn serialized_size(&self) -> usize { 92 | PublicKey::SIZE 93 | } 94 | } 95 | 96 | impl SerializeContent for PublicKey { 97 | fn serialize_content(&self, writer: &mut W) -> io::Result { Ok(self.serialize(writer)?) } 98 | } 99 | 100 | // This is a different Hash than the std Hash. 101 | #[allow(clippy::derive_hash_xor_eq)] 102 | impl Hash for PublicKey { } 103 | 104 | -------------------------------------------------------------------------------- /keys/src/signature.rs: -------------------------------------------------------------------------------- 1 | use beserial::{Serialize, SerializingError, Deserialize, ReadBytesExt, WriteBytesExt}; 2 | use hex::FromHex; 3 | 4 | use crate::errors::{KeysError, ParseError}; 5 | use std::convert::TryFrom; 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct Signature(pub(in super) ed25519_dalek::Signature); 9 | 10 | impl Signature { 11 | pub const SIZE: usize = 64; 12 | 13 | #[inline] 14 | pub fn to_bytes(&self) -> [u8; Self::SIZE] { self.0.to_bytes() } 15 | 16 | #[inline] 17 | pub(crate) fn as_dalek(&self) -> &ed25519_dalek::Signature { &self.0 } 18 | 19 | #[inline] 20 | pub fn from_bytes(bytes: &[u8]) -> Result { 21 | Ok(Signature(ed25519_dalek::Signature::try_from(bytes)?)) 22 | } 23 | } 24 | 25 | impl Eq for Signature {} 26 | 27 | impl PartialEq for Signature { 28 | fn eq(&self, other: &Self) -> bool { 29 | self.0 == other.0 30 | } 31 | } 32 | 33 | impl FromHex for Signature { 34 | type Error = ParseError; 35 | 36 | fn from_hex>(hex: T) -> Result { 37 | Ok(Signature::from_bytes(hex::decode(hex)?.as_slice())?) 38 | } 39 | } 40 | 41 | impl<'a> From<&'a [u8; Self::SIZE]> for Signature { 42 | fn from(bytes: &'a [u8; Self::SIZE]) -> Self { 43 | Signature::from(*bytes) 44 | } 45 | } 46 | 47 | impl From<[u8; Self::SIZE]> for Signature { 48 | fn from(bytes: [u8; Self::SIZE]) -> Self { 49 | Signature(ed25519_dalek::Signature::from(bytes)) 50 | } 51 | } 52 | 53 | impl Deserialize for Signature { 54 | fn deserialize(reader: &mut R) -> Result { 55 | let mut buf = [0u8; Signature::SIZE]; 56 | reader.read_exact(&mut buf)?; 57 | Self::from_bytes(&buf).map_err(|_| SerializingError::InvalidValue) 58 | } 59 | } 60 | 61 | impl Serialize for Signature { 62 | fn serialize(&self, writer: &mut W) -> Result { 63 | writer.write_all(&self.to_bytes())?; 64 | Ok(self.serialized_size()) 65 | } 66 | 67 | fn serialized_size(&self) -> usize { 68 | Self::SIZE 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-lib" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Nimiq's Rust library" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | futures = "0.1" 21 | failure = "0.1" 22 | lazy_static = "1.2" 23 | nimiq-network = { path = "../network", version = "0.2" } 24 | nimiq-consensus = { path = "../consensus", version = "0.2" } 25 | nimiq-database = { path = "../database", version = "0.2" } 26 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2", features = ["all"] } 27 | nimiq-primitives = { path = "../primitives", version = "0.2", features = ["networks"] } 28 | nimiq-mempool = { path = "../mempool", version = "0.2" } 29 | -------------------------------------------------------------------------------- /lib/src/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | 3 | use consensus::error::Error as ConsensusError; 4 | use network::error::Error as NetworkError; 5 | 6 | 7 | /// Prototype for a Error returned by these futures 8 | /// Errors can occur, when e.g. the bind port is already used 9 | #[derive(Debug, Fail)] 10 | pub enum ClientError { 11 | #[fail(display = "{}", _0)] 12 | NetworkError(#[cause] NetworkError), 13 | #[fail(display = "Reverse Proxy can only be configured on Ws")] 14 | ConfigureReverseProxyError, 15 | #[fail(display = "{}", _0)] 16 | ConsensusError(#[cause] ConsensusError), 17 | #[fail(display = "Rtc is not implemented")] 18 | RtcNotImplemented, 19 | #[fail(display = "Protocol expects a hostname")] 20 | MissingHostname, 21 | #[fail(display = "Protocol expects a port")] 22 | MissingPort, 23 | #[fail(display = "Protocol doesn't expect a hostname")] 24 | UnexpectedHostname, 25 | #[fail(display = "Protocol doesn't expect a port")] 26 | UnexpectedPort, 27 | #[fail(display = "TLS identity file is missing")] 28 | MissingIdentityFile, 29 | 30 | } 31 | 32 | impl From for ClientError { 33 | fn from(e: NetworkError) -> Self { 34 | ClientError::NetworkError(e) 35 | } 36 | } 37 | 38 | impl From for ClientError { 39 | fn from(e: ConsensusError) -> Self { 40 | ClientError::ConsensusError(e) 41 | } 42 | } -------------------------------------------------------------------------------- /lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate lazy_static; 3 | 4 | extern crate nimiq_consensus as consensus; 5 | extern crate nimiq_database as database; 6 | extern crate nimiq_network as network; 7 | extern crate nimiq_network_primitives as network_primitives; 8 | extern crate nimiq_primitives as primitives; 9 | extern crate nimiq_mempool as mempool; 10 | 11 | pub mod prelude; 12 | pub mod client; 13 | pub mod error; -------------------------------------------------------------------------------- /lib/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::client::{Client, ClientBuilder}; 2 | pub use crate::error::ClientError; 3 | pub use database::Environment; 4 | pub use primitives::networks::NetworkId; 5 | -------------------------------------------------------------------------------- /libargon2-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libargon2-sys" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | links = "argon2" 6 | build = "build.rs" 7 | description = "Rust bindings for Argon2" 8 | homepage = "https://nimiq.com" 9 | repository = "https://github.com/nimiq/core-rs" 10 | license = "Apache-2.0" 11 | edition = "2018" 12 | categories = ["cryptography", "external-ffi-bindings"] 13 | keywords = ["hash"] 14 | 15 | [badges] 16 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 17 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 18 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 19 | maintenance = { status = "experimental" } 20 | 21 | [dependencies] 22 | libc = "0.2" 23 | 24 | [build-dependencies] 25 | cc = "1.0" 26 | -------------------------------------------------------------------------------- /libargon2-sys/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut config = cc::Build::new(); 3 | config.file("native/argon2.c") 4 | .file("native/core.c") 5 | .file("native/blake2/blake2b.c") 6 | .file("native/opt.c"); 7 | 8 | config.opt_level(2); 9 | 10 | config.include("native"); 11 | config.flag("-DARGON2_NO_THREADS"); 12 | 13 | config.compile("libargon2.a"); 14 | } 15 | -------------------------------------------------------------------------------- /libargon2-sys/native/blake2/blake2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Argon2 reference source code package - reference C implementations 3 | * 4 | * Copyright 2015 5 | * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves 6 | * 7 | * You may use this work under the terms of a Creative Commons CC0 1.0 8 | * License/Waiver or the Apache Public License 2.0, at your option. The terms of 9 | * these licenses can be found at: 10 | * 11 | * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 12 | * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * You should have received a copy of both of these licenses along with this 15 | * software. If not, they may be obtained at the above URLs. 16 | */ 17 | 18 | #ifndef PORTABLE_BLAKE2_H 19 | #define PORTABLE_BLAKE2_H 20 | 21 | #include 22 | 23 | #if defined(__cplusplus) 24 | extern "C" { 25 | #endif 26 | 27 | enum blake2b_constant { 28 | BLAKE2B_BLOCKBYTES = 128, 29 | BLAKE2B_OUTBYTES = 64, 30 | BLAKE2B_KEYBYTES = 64, 31 | BLAKE2B_SALTBYTES = 16, 32 | BLAKE2B_PERSONALBYTES = 16 33 | }; 34 | 35 | #pragma pack(push, 1) 36 | typedef struct __blake2b_param { 37 | uint8_t digest_length; /* 1 */ 38 | uint8_t key_length; /* 2 */ 39 | uint8_t fanout; /* 3 */ 40 | uint8_t depth; /* 4 */ 41 | uint32_t leaf_length; /* 8 */ 42 | uint64_t node_offset; /* 16 */ 43 | uint8_t node_depth; /* 17 */ 44 | uint8_t inner_length; /* 18 */ 45 | uint8_t reserved[14]; /* 32 */ 46 | uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ 47 | uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ 48 | } blake2b_param; 49 | #pragma pack(pop) 50 | 51 | typedef struct __blake2b_state { 52 | uint64_t h[8]; 53 | uint64_t t[2]; 54 | uint64_t f[2]; 55 | uint8_t buf[BLAKE2B_BLOCKBYTES]; 56 | unsigned buflen; 57 | unsigned outlen; 58 | uint8_t last_node; 59 | } blake2b_state; 60 | 61 | /* Ensure param structs have not been wrongly padded */ 62 | /* Poor man's static_assert */ 63 | enum { 64 | blake2_size_check_0 = 1 / !!(CHAR_BIT == 8), 65 | blake2_size_check_2 = 66 | 1 / !!(sizeof(blake2b_param) == sizeof(uint64_t) * CHAR_BIT) 67 | }; 68 | 69 | /* Streaming API */ 70 | ARGON2_LOCAL int blake2b_init(blake2b_state *S, size_t outlen); 71 | ARGON2_LOCAL int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, 72 | size_t keylen); 73 | ARGON2_LOCAL int blake2b_init_param(blake2b_state *S, const blake2b_param *P); 74 | ARGON2_LOCAL int blake2b_update(blake2b_state *S, const void *in, size_t inlen); 75 | ARGON2_LOCAL int blake2b_final(blake2b_state *S, void *out, size_t outlen); 76 | 77 | /* Simple API */ 78 | ARGON2_LOCAL int blake2b(void *out, size_t outlen, const void *in, size_t inlen, 79 | const void *key, size_t keylen); 80 | 81 | /* Argon2 Team - Begin Code */ 82 | ARGON2_LOCAL int blake2b_long(void *out, size_t outlen, const void *in, size_t inlen); 83 | /* Argon2 Team - End Code */ 84 | 85 | #if defined(__cplusplus) 86 | } 87 | #endif 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /libargon2-sys/native/blake2/blamka-round-ref.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Argon2 reference source code package - reference C implementations 3 | * 4 | * Copyright 2015 5 | * Daniel Dinu, Dmitry Khovratovich, Jean-Philippe Aumasson, and Samuel Neves 6 | * 7 | * You may use this work under the terms of a Creative Commons CC0 1.0 8 | * License/Waiver or the Apache Public License 2.0, at your option. The terms of 9 | * these licenses can be found at: 10 | * 11 | * - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 12 | * - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * You should have received a copy of both of these licenses along with this 15 | * software. If not, they may be obtained at the above URLs. 16 | */ 17 | 18 | #ifndef BLAKE_ROUND_MKA_H 19 | #define BLAKE_ROUND_MKA_H 20 | 21 | #include "blake2.h" 22 | #include "blake2-impl.h" 23 | 24 | /* designed by the Lyra PHC team */ 25 | static BLAKE2_INLINE uint64_t fBlaMka(uint64_t x, uint64_t y) { 26 | const uint64_t m = UINT64_C(0xFFFFFFFF); 27 | const uint64_t xy = (x & m) * (y & m); 28 | return x + y + 2 * xy; 29 | } 30 | 31 | #define G(a, b, c, d) \ 32 | do { \ 33 | a = fBlaMka(a, b); \ 34 | d = rotr64(d ^ a, 32); \ 35 | c = fBlaMka(c, d); \ 36 | b = rotr64(b ^ c, 24); \ 37 | a = fBlaMka(a, b); \ 38 | d = rotr64(d ^ a, 16); \ 39 | c = fBlaMka(c, d); \ 40 | b = rotr64(b ^ c, 63); \ 41 | } while ((void)0, 0) 42 | 43 | #define BLAKE2_ROUND_NOMSG(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \ 44 | v12, v13, v14, v15) \ 45 | do { \ 46 | G(v0, v4, v8, v12); \ 47 | G(v1, v5, v9, v13); \ 48 | G(v2, v6, v10, v14); \ 49 | G(v3, v7, v11, v15); \ 50 | G(v0, v5, v10, v15); \ 51 | G(v1, v6, v11, v12); \ 52 | G(v2, v7, v8, v13); \ 53 | G(v3, v4, v9, v14); \ 54 | } while ((void)0, 0) 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /libargon2-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use libc::{c_int, size_t}; 4 | 5 | extern { 6 | fn argon2d_hash_raw_flags(t_cost: u32, m_cost: u32, 7 | parallelism: u32, pwd: *const u8, 8 | pwdlen: size_t, salt: *const u8, 9 | saltlen: size_t, hash: *mut u8, 10 | hashlen: size_t, flags: u32) -> c_int; 11 | } 12 | 13 | pub fn argon2d_hash(t_cost: u32, m_cost: u32, parallelism: u32, pwd: &[u8], salt: &[u8], out: &mut [u8], flags: u32) { 14 | unsafe { argon2d_hash_raw_flags(t_cost, m_cost, parallelism,pwd.as_ptr(), pwd.len(), salt.as_ptr(), salt.len(), out.as_mut_ptr(), out.len(), flags) }; 15 | } 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use super::*; 20 | 21 | #[test] 22 | fn direct_call() { 23 | let mut res = [0u8; 32]; 24 | unsafe { argon2d_hash_raw_flags(1, 512, 1, "test".as_ptr(), 4, "nimiqrocks!".as_ptr(), 11, res.as_mut_ptr(), 32, 0) }; 25 | assert_eq!(res, [140, 37, 159, 220, 194, 173, 103, 153, 223, 114, 140, 17, 232, 149, 163, 54, 158, 157, 186, 230, 163, 22, 110, 188, 59, 53, 51, 153, 252, 86, 85, 36]); 26 | } 27 | 28 | #[test] 29 | fn api_call() { 30 | let mut res = [0u8; 32]; 31 | argon2d_hash(1, 512, 1,b"test", b"nimiqrocks!", &mut res, 0); 32 | assert_eq!(res, [140, 37, 159, 220, 194, 173, 103, 153, 223, 114, 140, 17, 232, 149, 163, 54, 158, 157, 186, 230, 163, 22, 110, 188, 59, 53, 51, 153, 252, 86, 85, 36]); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-macros" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Macros for Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! create_typed_array { 3 | ($name: ident, $t: ty, $len: expr) => { 4 | #[repr(C)] 5 | #[derive(Default, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 6 | pub struct $name ([$t; $len]); 7 | 8 | impl<'a> From<&'a [$t]> for $name { 9 | fn from(slice: &'a [$t]) -> Self { 10 | assert_eq!(slice.len(), $len, "Tried to create instance with slice of wrong length"); 11 | let mut a = [0 as $t; $len]; 12 | a.clone_from_slice(&slice[0..$len]); 13 | $name(a) 14 | } 15 | } 16 | 17 | impl ::beserial::Deserialize for $name { 18 | fn deserialize(reader: &mut R) -> Result { 19 | let mut a = [0 as $t; $len]; 20 | reader.read_exact(&mut a[..])?; 21 | Ok($name(a)) 22 | } 23 | } 24 | 25 | impl ::beserial::Serialize for $name { 26 | fn serialize(&self, writer: &mut W) -> Result { 27 | writer.write_all(&self.0)?; 28 | Ok($len) 29 | } 30 | 31 | fn serialized_size(&self) -> usize { 32 | $len 33 | } 34 | } 35 | 36 | impl From<[$t; $len]> for $name { 37 | fn from(arr: [$t; $len]) -> Self { 38 | $name(arr) 39 | } 40 | } 41 | 42 | impl From<$name> for [$t; $len] { 43 | fn from(i: $name) -> [$t; $len] { 44 | i.0 45 | } 46 | } 47 | 48 | impl ::std::fmt::Debug for $name { 49 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 50 | f.write_str(&::hex::encode(&self.0)) 51 | } 52 | } 53 | 54 | impl $name { 55 | pub const SIZE: usize = $len; 56 | #[inline] 57 | pub fn len() -> usize { $len } 58 | pub fn as_bytes(&self) -> &[$t] { &self.0 } 59 | } 60 | }; 61 | } 62 | 63 | #[macro_export] 64 | macro_rules! add_hex_io_fns_typed_arr { 65 | ($name: ident, $len: expr) => { 66 | impl ::std::fmt::Display for $name { 67 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 68 | f.write_str(&::hex::encode(&self.0)) 69 | } 70 | } 71 | 72 | impl $name { 73 | pub fn to_hex(&self) -> String { ::hex::encode(&self.0) } 74 | } 75 | 76 | impl ::std::str::FromStr for $name { 77 | type Err = ::hex::FromHexError; 78 | 79 | fn from_str(s: &str) -> Result { 80 | let vec = Vec::from_hex(s)?; 81 | if vec.len() == $len { 82 | Ok($name::from(&vec[..])) 83 | } else { 84 | Err(::hex::FromHexError::InvalidStringLength) 85 | } 86 | } 87 | } 88 | 89 | impl From<&'static str> for $name { 90 | fn from(s: &'static str) -> Self { 91 | s.parse().unwrap() 92 | } 93 | } 94 | }; 95 | } 96 | 97 | #[macro_export] 98 | macro_rules! upgrade_weak { 99 | ($weak_ref: expr) => { 100 | if let Some(arc) = $weak_ref.upgrade() { 101 | arc 102 | } else { 103 | return; 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /mempool/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-mempool" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Mempool implementation for Nimiq" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | log = "0.4" 21 | parking_lot = "0.7" 22 | beserial = { path = "../beserial", version = "0.2" } 23 | nimiq-hash = { path = "../hash", version = "0.2" } 24 | nimiq-keys = { path = "../keys", version = "0.2" } 25 | nimiq-account = { path = "../primitives/account", version = "0.2" } 26 | nimiq-block = { path = "../primitives/block", version = "0.2" } 27 | nimiq-transaction = { path = "../primitives/transaction", version = "0.2" } 28 | nimiq-utils = { path = "../utils", version = "0.2", features = ["observer", "timers", "mutable-once"] } 29 | nimiq-accounts = { path = "../accounts", version = "0.2" } 30 | nimiq-blockchain = { path = "../blockchain", version = "0.2" } 31 | nimiq-collections = { path = "../collections", version = "0.2" } 32 | nimiq-primitives = { path = "../primitives", version = "0.2", features = ["coin", "networks"] } 33 | 34 | [dev-dependencies] 35 | hex = "0.3" 36 | nimiq-database = { path = "../database", version = "0.2" } 37 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2" } 38 | -------------------------------------------------------------------------------- /mempool/src/filter.rs: -------------------------------------------------------------------------------- 1 | use account::Account; 2 | use collections::LimitHashSet; 3 | use nimiq_hash::Blake2bHash; 4 | use primitives::coin::Coin; 5 | use transaction::{Transaction, TransactionFlags}; 6 | 7 | #[derive(Debug)] 8 | pub struct MempoolFilter { 9 | blacklist: LimitHashSet, 10 | rules: Rules, 11 | } 12 | 13 | impl MempoolFilter { 14 | pub const DEFAULT_BLACKLIST_SIZE: usize = 25000; 15 | 16 | pub fn new(rules: Rules, blacklist_limit: usize) -> Self { 17 | MempoolFilter { 18 | blacklist: LimitHashSet::new(blacklist_limit), 19 | rules, 20 | } 21 | } 22 | 23 | pub fn blacklist(&mut self, hash: Blake2bHash) -> &mut Self { 24 | self.blacklist.insert(hash); 25 | self 26 | } 27 | 28 | pub fn remove(&mut self, hash: &Blake2bHash) -> &mut Self { 29 | self.blacklist.remove(hash); 30 | self 31 | } 32 | 33 | pub fn blacklisted(&self, hash: &Blake2bHash) -> bool { 34 | self.blacklist.contains(hash) 35 | } 36 | 37 | pub fn accepts_transaction(&self, tx: &Transaction) -> bool { 38 | tx.fee >= self.rules.tx_fee && 39 | tx.value >= self.rules.tx_value && 40 | // Unchecked addition of coins. 41 | tx.value + tx.fee >= self.rules.tx_value_total && 42 | tx.fee_per_byte() >= self.rules.tx_fee_per_byte && ( 43 | !tx.flags.contains(TransactionFlags::CONTRACT_CREATION) || ( 44 | tx.fee >= self.rules.contract_fee || 45 | tx.fee_per_byte() >= self.rules.contract_fee_per_byte || 46 | tx.value >= self.rules.contract_value 47 | ) 48 | ) 49 | } 50 | 51 | pub fn accepts_recipient_account(&self, tx: &Transaction, old_account: &Account, new_account: &Account) -> bool { 52 | new_account.balance() >= self.rules.recipient_balance && ( 53 | !old_account.is_initial() || 54 | (tx.fee >= self.rules.creation_fee && 55 | tx.fee_per_byte() >= self.rules.creation_fee_per_byte && 56 | tx.value >= self.rules.creation_value 57 | ) 58 | ) 59 | } 60 | 61 | pub fn accepts_sender_account(&self, _tx: &Transaction, _old_account: &Account, new_account: &Account) -> bool { 62 | new_account.balance() >= self.rules.sender_balance || 63 | new_account.is_initial() || 64 | new_account.is_to_be_pruned() 65 | } 66 | } 67 | 68 | impl Default for MempoolFilter{ 69 | fn default() -> Self { 70 | MempoolFilter::new(Rules::default(), Self::DEFAULT_BLACKLIST_SIZE) 71 | } 72 | } 73 | 74 | 75 | #[derive(Debug, Clone)] 76 | pub struct Rules { 77 | pub tx_fee: Coin, 78 | pub tx_fee_per_byte: f64, 79 | pub tx_value: Coin, 80 | pub tx_value_total: Coin, 81 | pub contract_fee: Coin, 82 | pub contract_fee_per_byte: f64, 83 | pub contract_value: Coin, 84 | pub creation_fee: Coin, 85 | pub creation_fee_per_byte: f64, 86 | pub creation_value: Coin, 87 | pub recipient_balance: Coin, 88 | pub sender_balance: Coin, 89 | } 90 | 91 | impl Default for Rules { 92 | fn default() -> Rules { 93 | Rules { 94 | tx_fee: Coin::ZERO, 95 | tx_fee_per_byte: 0.0, 96 | tx_value: Coin::ZERO, 97 | tx_value_total: Coin::ZERO, 98 | contract_fee: Coin::ZERO, 99 | contract_fee_per_byte: 0.0, 100 | contract_value: Coin::ZERO, 101 | creation_fee: Coin::ZERO, 102 | creation_fee_per_byte: 0.0, 103 | creation_value: Coin::ZERO, 104 | sender_balance: Coin::ZERO, 105 | recipient_balance: Coin::ZERO, 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /mempool/tests/filter.rs: -------------------------------------------------------------------------------- 1 | use nimiq_hash::{Hash, Blake2bHash}; 2 | use nimiq_keys::Address; 3 | use nimiq_mempool::filter::{MempoolFilter, Rules}; 4 | use nimiq_primitives::coin::Coin; 5 | use nimiq_primitives::networks::NetworkId; 6 | use nimiq_transaction::Transaction; 7 | 8 | #[test] 9 | fn it_can_blacklist_transactions() { 10 | let mut f: MempoolFilter = Default::default(); 11 | 12 | let tx = Transaction::new_basic( 13 | Address::from([32u8; Address::SIZE]), 14 | Address::from([213u8; Address::SIZE]), 15 | Coin::from_u64(100).unwrap(), 16 | Coin::from_u64(1).unwrap(), 17 | 123, 18 | NetworkId::Main, 19 | ); 20 | 21 | let hash: Blake2bHash = tx.hash(); 22 | f.blacklist(hash.clone()); 23 | assert!(f.blacklisted(&hash)); 24 | f.remove(&hash); 25 | assert!(!f.blacklisted(&hash)); 26 | } 27 | 28 | #[test] 29 | fn it_accepts_and_rejects_transactions() { 30 | let mut s: Rules = Rules::default(); 31 | s.tx_fee = Coin::from_u64(1).unwrap(); 32 | 33 | let f = MempoolFilter::new(s, MempoolFilter::DEFAULT_BLACKLIST_SIZE); 34 | 35 | let mut tx = Transaction::new_basic( 36 | Address::from([32u8; Address::SIZE]), 37 | Address::from([213u8; Address::SIZE]), 38 | Coin::from_u64(0).unwrap(), 39 | Coin::from_u64(0).unwrap(), 40 | 0, 41 | NetworkId::Main, 42 | ); 43 | 44 | assert!(!f.accepts_transaction(&tx)); 45 | tx.fee = Coin::from_u64(1).unwrap(); 46 | assert!(f.accepts_transaction(&tx)); 47 | } 48 | -------------------------------------------------------------------------------- /messages/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-messages" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Messages send over the nimiq network" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | bitflags = "1.0" 21 | byteorder = "1.2" 22 | hex = "0.3" 23 | parking_lot = "0.7" 24 | rand = "0.6" 25 | beserial = { path = "../beserial", version = "0.2" } 26 | beserial_derive = { path = "../beserial/beserial_derive", version = "0.2" } 27 | nimiq-hash = { path = "../hash", version = "0.2" } 28 | nimiq-keys = { path = "../keys", version = "0.2" } 29 | nimiq-macros = { path = "../macros", version = "0.2" } 30 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2", features = ["networks", "subscription", "version"] } 31 | nimiq-tree-primitives = { path = "../accounts/tree-primitives", version = "0.2" } 32 | nimiq-block = { path = "../primitives/block", version = "0.2" } 33 | nimiq-transaction = { path = "../primitives/transaction", version = "0.2" } 34 | nimiq-utils = { path = "../utils", version = "0.2", features = ["observer", "crc", "time"] } 35 | 36 | [dev-dependencies] 37 | nimiq-utils = { path = "../utils", version = "0.2", features = ["observer", "crc", "time", "iterators"] } 38 | -------------------------------------------------------------------------------- /messages/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod message; 2 | -------------------------------------------------------------------------------- /metrics-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-metrics-server" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | license = "Apache-2.0" 6 | edition = "2018" 7 | description = "Prometheus metrics server for the Nimiq Rust implementation" 8 | homepage = "https://nimiq.com" 9 | repository = "https://github.com/nimiq/core-rs" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | hyper = "0.12" 21 | futures = "0.1" 22 | log = "0.4" 23 | base64 = "0.10" 24 | failure = "0.1" 25 | tokio = "0.1" 26 | tokio-tls = "0.2" 27 | native-tls = "0.2" 28 | 29 | nimiq-blockchain = { path = "../blockchain", version = "0.2", features = ["metrics"] } 30 | nimiq-consensus = { path = "../consensus", version = "0.2" } 31 | nimiq-network = { path = "../network", version = "0.2", features = ["metrics"] } 32 | nimiq-mempool = { path = "../mempool", version = "0.2" } 33 | nimiq-block = { path = "../primitives/block", version = "0.2" } 34 | beserial = { path = "../beserial", version = "0.2" } 35 | -------------------------------------------------------------------------------- /metrics-server/src/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | use hyper::Error as HyperError; 3 | 4 | #[derive(Fail, Debug)] 5 | pub enum Error { 6 | #[fail(display = "{}", _0)] 7 | HyperError(#[cause] HyperError), 8 | } 9 | 10 | impl From for Error { 11 | fn from(e: HyperError) -> Self { 12 | Error::HyperError(e) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /metrics-server/src/metrics/chain.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::sync::Arc; 3 | 4 | use blockchain::Blockchain; 5 | use block::Difficulty; 6 | 7 | use crate::server; 8 | use crate::server::SerializationType; 9 | 10 | pub struct ChainMetrics { 11 | blockchain: Arc>, 12 | } 13 | 14 | impl ChainMetrics { 15 | pub fn new(blockchain: Arc>) -> Self { 16 | ChainMetrics { 17 | blockchain, 18 | } 19 | } 20 | } 21 | 22 | impl server::Metrics for ChainMetrics { 23 | fn metrics(&self, serializer: &mut server::MetricsSerializer) -> Result<(), io::Error> { 24 | // Release lock as fast as possible. 25 | { 26 | let head = self.blockchain.head(); 27 | 28 | serializer.metric("chain_head_height", head.header.height)?; 29 | serializer.metric("chain_head_difficulty", Difficulty::from(head.header.n_bits))?; 30 | serializer.metric("chain_head_transactions", head.body.as_ref().map(|body| body.transactions.len()).unwrap_or(0))?; 31 | } 32 | serializer.metric("chain_total_work", self.blockchain.total_work())?; 33 | 34 | serializer.metric_with_attributes("chain_block", self.blockchain.metrics.block_forked_count(), attributes!{"action" => "forked"})?; 35 | serializer.metric_with_attributes("chain_block", self.blockchain.metrics.block_rebranched_count(), attributes!{"action" => "rebranched"})?; 36 | serializer.metric_with_attributes("chain_block", self.blockchain.metrics.block_extended_count(), attributes!{"action" => "extended"})?; 37 | serializer.metric_with_attributes("chain_block", self.blockchain.metrics.block_orphan_count(), attributes!{"action" => "orphan"})?; 38 | serializer.metric_with_attributes("chain_block", self.blockchain.metrics.block_invalid_count(), attributes!{"action" => "invalid"})?; 39 | serializer.metric_with_attributes("chain_block", self.blockchain.metrics.block_known_count(), attributes!{"action" => "known"})?; 40 | 41 | Ok(()) 42 | } 43 | } -------------------------------------------------------------------------------- /metrics-server/src/metrics/mempool.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::sync::Arc; 3 | 4 | use mempool::{Mempool, SIZE_MAX}; 5 | 6 | use crate::server; 7 | use crate::server::SerializationType; 8 | use beserial::Serialize; 9 | 10 | pub struct MempoolMetrics { 11 | mempool: Arc>, 12 | } 13 | 14 | impl MempoolMetrics { 15 | pub fn new(mempool: Arc>) -> Self { 16 | MempoolMetrics { 17 | mempool, 18 | } 19 | } 20 | } 21 | 22 | impl server::Metrics for MempoolMetrics { 23 | fn metrics(&self, serializer: &mut server::MetricsSerializer) -> Result<(), io::Error> { 24 | let txs = self.mempool.get_transactions(SIZE_MAX, 0f64); 25 | let group = [0usize, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000]; 26 | for i in 1..group.len() { 27 | let lower_bound = group[i - 1]; 28 | let upper_bound = group[i]; 29 | serializer.metric_with_attributes( 30 | "mempool_transactions", 31 | txs.iter().filter(|tx| (tx.fee_per_byte() as usize) >= lower_bound && (tx.fee_per_byte() as usize) < upper_bound).count(), 32 | attributes!{"fee_per_byte" => format!("<{}", group[i])} 33 | )?; 34 | } 35 | let lower_bound = *group.last().unwrap(); 36 | serializer.metric_with_attributes( 37 | "mempool_transactions", 38 | txs.iter().filter(|tx| (tx.fee_per_byte() as usize) >= lower_bound).count(), 39 | attributes!{"fee_per_byte" => format!(">={}", lower_bound)} 40 | )?; 41 | serializer.metric( 42 | "mempool_size", 43 | txs.iter().map(|tx| tx.serialized_size()).sum::(), 44 | )?; 45 | 46 | Ok(()) 47 | } 48 | } -------------------------------------------------------------------------------- /metrics-server/src/metrics/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod chain; 2 | pub(crate) mod mempool; 3 | pub(crate) mod network; 4 | -------------------------------------------------------------------------------- /mnemonic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-mnemonic" 3 | version = "0.2.0" 4 | authors = ["Pascal B ", "The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Mnemonic helper library for Nimiq" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | bit-vec = "0.5" 21 | hex = "0.3" 22 | unicode-normalization = "0.1" 23 | nimiq-hash = { path = "../hash", version = "0.2" } 24 | beserial = { path = "../beserial", version = "0.2" } 25 | nimiq-macros = { path = "../macros", version = "0.2" } 26 | nimiq-utils = { path = "../utils", version = "0.2", features = ["bit-vec", "crc"] } 27 | nimiq-key-derivation = { path = "../key-derivation", version = "0.2", optional = true } 28 | 29 | [features] 30 | default = ["key-derivation"] 31 | key-derivation = ["nimiq-key-derivation"] -------------------------------------------------------------------------------- /mnemonic/src/key_derivation.rs: -------------------------------------------------------------------------------- 1 | use hash::pbkdf2::Pbkdf2Error; 2 | use nimiq_key_derivation::ExtendedPrivateKey; 3 | 4 | use crate::Mnemonic; 5 | 6 | pub trait ToExtendedPrivateKey { 7 | /// Returns the corresponding master extended private key for this mnemonic. 8 | fn to_master_key(&self, password: Option<&str>) -> Result; 9 | } 10 | 11 | impl ToExtendedPrivateKey for Mnemonic { 12 | /// Returns the corresponding master extended private key for this mnemonic. 13 | fn to_master_key(&self, password: Option<&str>) -> Result { 14 | Ok(ExtendedPrivateKey::from_seed(self.to_seed(password)?)) 15 | } 16 | } 17 | 18 | pub trait FromMnemonic: Sized { 19 | /// Converts a mnemonic into the corresponding master extended private key. 20 | fn from_mnemonic(mnemonic: &Mnemonic, password: Option<&str>) -> Result; 21 | } 22 | 23 | impl FromMnemonic for ExtendedPrivateKey { 24 | /// Converts a mnemonic into the corresponding master extended private key. 25 | fn from_mnemonic(mnemonic: &Mnemonic, password: Option<&str>) -> Result { 26 | mnemonic.to_master_key(password) 27 | } 28 | } -------------------------------------------------------------------------------- /network-primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-network-primitives" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Network primitives and constants for Nimiq" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | bitflags = "1.0" 21 | hex = "0.3" 22 | lazy_static = "1.2" 23 | atomic = "0.4" 24 | url = "1.7" 25 | failure = "0.1" 26 | beserial = { path = "../beserial", version = "0.2" } 27 | beserial_derive = { path = "../beserial/beserial_derive", version = "0.2" } 28 | nimiq-keys = { path = "../keys", version = "0.2" } 29 | nimiq-hash = { path = "../hash", version = "0.2" } 30 | nimiq-block = { path = "../primitives/block", version = "0.2" } 31 | nimiq-transaction = { path = "../primitives/transaction", version = "0.2" } 32 | nimiq-primitives = { path = "../primitives", version = "0.2", features = ["coin", "networks"] } 33 | nimiq-utils = { path = "../utils", version = "0.2", features = ["observer", "crc", "time"] } 34 | nimiq-macros = { path = "../macros", version = "0.2" } 35 | 36 | [features] 37 | default = ["all"] 38 | all = ["networks", "time", "address", "services", "version", "protocol", "subscription"] 39 | networks = ["address", "services", "protocol"] 40 | time = [] 41 | address = ["services", "protocol"] 42 | services = [] 43 | version = [] 44 | protocol = [] 45 | subscription = [] 46 | -------------------------------------------------------------------------------- /network-primitives/src/address/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod net_address; 2 | pub mod peer_address; 3 | pub mod peer_uri; 4 | pub mod seed_list; 5 | 6 | pub use self::net_address::*; 7 | pub use self::peer_address::*; 8 | pub use self::peer_uri::PeerUri; 9 | pub use self::seed_list::SeedList; 10 | 11 | use hex::FromHex; 12 | 13 | use nimiq_keys::{PublicKey}; 14 | use nimiq_hash::{Blake2bHash, Blake2bHasher, Hasher}; 15 | use std::net::IpAddr; 16 | 17 | create_typed_array!(PeerId, u8, 16); 18 | add_hex_io_fns_typed_arr!(PeerId, PeerId::SIZE); 19 | 20 | impl From for PeerId { 21 | fn from(hash: Blake2bHash) -> Self { 22 | let hash_arr: [u8; 32] = hash.into(); 23 | PeerId::from(&hash_arr[0..PeerId::len()]) 24 | } 25 | } 26 | 27 | impl<'a> From<&'a PublicKey> for PeerId { 28 | fn from(public_key: &'a PublicKey) -> Self { 29 | let hash = Blake2bHasher::default().digest(public_key.as_bytes()); 30 | PeerId::from(hash) 31 | } 32 | } 33 | 34 | fn is_ip_globally_reachable_legacy(ip: &IpAddr) -> bool { 35 | match ip { 36 | IpAddr::V4(ipv4) => { 37 | let octets = ipv4.octets(); 38 | if let [127, 0, 0, 1] = octets { 39 | return false; 40 | } 41 | if ipv4.is_private() || ipv4.is_link_local() { 42 | return false; 43 | } 44 | // 100.64.0.0/10 45 | if octets[0] == 100 && (octets[1] >= 64 && octets[1] <= 127) { 46 | return false; 47 | } 48 | }, 49 | IpAddr::V6(ipv6) => { 50 | let octets = ipv6.octets(); 51 | // check for local ip ::1 52 | if octets[0..octets.len() - 1] == [0;15] && octets[15] == 1 { 53 | return false; 54 | } 55 | // Private subnet is fc00::/7. 56 | // So, we only check the first 7 bits of the address to be equal fc00. 57 | if (octets[0] & 0xfe) == 0xfc { 58 | return false; 59 | } 60 | // Link-local addresses are fe80::/10. 61 | if octets[0] == 0xfe && (octets[1] & 0xc0) == 0x80 { 62 | return false; 63 | } 64 | } 65 | }; 66 | true 67 | } 68 | 69 | #[test] 70 | fn is_ip_globally_reachable_legacy_falsifys() { 71 | use std::str::FromStr; 72 | 73 | let bad_ips = vec![ 74 | // Local IPs 75 | "127.0.0.1", 76 | // Private IPs 77 | "192.168.2.1", "172.16.0.0", "172.31.0.0", "172.31.255.255", "100.64.0.0", "169.254.0.0", 78 | "fd12:3456:789a:1::1", "fe80:3456:789a:1::1", "fd00:3456:789a:1::1" 79 | ]; 80 | for bad_ip in bad_ips { 81 | assert!(!is_ip_globally_reachable_legacy(&IpAddr::from_str(bad_ip).unwrap())); 82 | } 83 | } 84 | 85 | #[test] 86 | fn is_ip_globally_reachable_legacy_verifies() { 87 | use std::str::FromStr; 88 | 89 | let good_ips = vec![ 90 | // Non-Private IPs 91 | "100.168.2.1", "172.32.0.0", "172.15.255.255", 92 | "fbff:3456:789a:1::1", "fe00:3456:789a:1::1", "ff02:3456:789a:1::1", "::3456:789a:1:1" 93 | ]; 94 | for good_ip in good_ips { 95 | assert!(is_ip_globally_reachable_legacy(&IpAddr::from_str(good_ip).unwrap())); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /network-primitives/src/address/seed_list.rs: -------------------------------------------------------------------------------- 1 | use keys::PublicKey; 2 | use url::Url; 3 | 4 | #[derive(Clone, Debug)] 5 | pub struct SeedList { 6 | url: Url, 7 | public_key: Option, 8 | } 9 | 10 | impl SeedList { 11 | pub fn new(url: Url, public_key: Option) -> Self { 12 | Self { 13 | url, 14 | public_key, 15 | } 16 | } 17 | 18 | pub fn url(&self) -> &Url { 19 | &self.url 20 | } 21 | 22 | pub fn public_key(&self) -> &Option { 23 | &self.public_key 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /network-primitives/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(ip)] // For Ip::is_global 2 | 3 | #[macro_use] 4 | extern crate beserial_derive; 5 | #[macro_use] 6 | extern crate bitflags; 7 | #[macro_use] 8 | extern crate nimiq_macros as macros; 9 | extern crate nimiq_utils as utils; 10 | extern crate nimiq_keys as keys; 11 | extern crate nimiq_block as block; 12 | extern crate nimiq_transaction as transaction; 13 | extern crate nimiq_primitives as primitives; 14 | extern crate nimiq_hash as hash; 15 | #[macro_use] 16 | extern crate lazy_static; 17 | 18 | #[cfg(feature = "address")] 19 | pub mod address; 20 | #[cfg(feature = "services")] 21 | pub mod services; 22 | #[cfg(feature = "version")] 23 | pub mod version; 24 | #[cfg(feature = "networks")] 25 | pub mod networks; 26 | #[cfg(feature = "protocol")] 27 | pub mod protocol; 28 | #[cfg(feature = "subscription")] 29 | pub mod subscription; 30 | #[cfg(feature = "time")] 31 | pub mod time; 32 | 33 | pub const IPV4_SUBNET_MASK: u8 = 24; 34 | pub const IPV6_SUBNET_MASK: u8 = 96; 35 | pub const PEER_COUNT_PER_IP_MAX: usize = 20; 36 | pub const OUTBOUND_PEER_COUNT_PER_SUBNET_MAX: usize = 2; 37 | pub const INBOUND_PEER_COUNT_PER_SUBNET_MAX: usize = 100; 38 | pub const PEER_COUNT_MAX: usize = 4000; 39 | pub const PEER_COUNT_DUMB_MAX: usize = 1000; 40 | -------------------------------------------------------------------------------- /network-primitives/src/protocol.rs: -------------------------------------------------------------------------------- 1 | use beserial::{Serialize, Deserialize}; 2 | 3 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)] 4 | #[repr(u8)] 5 | pub enum Protocol { 6 | Dumb = 0, 7 | Wss = 1, 8 | Rtc = 2, 9 | Ws = 4 10 | } 11 | 12 | impl From for Vec { 13 | fn from(flags: ProtocolFlags) -> Self { 14 | let mut v = Vec::new(); 15 | if flags.contains(ProtocolFlags::DUMB) { 16 | v.push(Protocol::Dumb); 17 | } 18 | if flags.contains(ProtocolFlags::WSS) { 19 | v.push(Protocol::Wss); 20 | } 21 | if flags.contains(ProtocolFlags::RTC) { 22 | v.push(Protocol::Rtc); 23 | } 24 | if flags.contains(ProtocolFlags::WS) { 25 | v.push(Protocol::Ws); 26 | } 27 | v 28 | } 29 | } 30 | 31 | bitflags! { 32 | #[derive(Default, Serialize, Deserialize)] 33 | pub struct ProtocolFlags: u8 { 34 | const DUMB = 0b0000_0000; 35 | const WSS = 0b0000_0001; 36 | const RTC = 0b0000_0010; 37 | const WS = 0b0000_0100; 38 | } 39 | } 40 | 41 | impl From for ProtocolFlags { 42 | fn from(protocol: Protocol) -> Self { 43 | match protocol { 44 | Protocol::Dumb => ProtocolFlags::DUMB, 45 | Protocol::Rtc => ProtocolFlags::RTC, 46 | Protocol::Wss => ProtocolFlags::WSS, 47 | Protocol::Ws => ProtocolFlags::WS, 48 | } 49 | } 50 | } 51 | 52 | impl From> for ProtocolFlags { 53 | fn from(protocols: Vec) -> Self { 54 | let mut flags = ProtocolFlags::default(); 55 | for protocol in protocols { 56 | flags |= ProtocolFlags::from(protocol); 57 | } 58 | flags 59 | } 60 | } 61 | 62 | impl Protocol { 63 | pub fn default_port(self) -> Option { 64 | match self { 65 | Protocol::Ws | Protocol::Wss => Some(8443), 66 | _ => None 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /network-primitives/src/services.rs: -------------------------------------------------------------------------------- 1 | use beserial::{Deserialize, Serialize}; 2 | 3 | bitflags! { 4 | #[derive(Serialize, Deserialize)] 5 | pub struct ServiceFlags: u32 { 6 | const NONE = 0b0000_0000; 7 | const NANO = 0b0000_0001; 8 | const LIGHT = 0b0000_0010; 9 | const FULL = 0b0000_0100; 10 | } 11 | } 12 | 13 | impl ServiceFlags { 14 | pub fn is_full_node(self) -> bool { 15 | self.contains(ServiceFlags::FULL) 16 | } 17 | 18 | pub fn is_light_node(self) -> bool { 19 | self.contains(ServiceFlags::LIGHT) 20 | } 21 | 22 | pub fn is_nano_node(self) -> bool { 23 | self.contains(ServiceFlags::NANO) 24 | } 25 | } 26 | 27 | #[derive(Debug, Clone, Serialize, Deserialize)] 28 | pub struct Services { 29 | pub provided: ServiceFlags, 30 | pub accepted: ServiceFlags, 31 | } 32 | 33 | impl Services { 34 | pub fn new(provided: ServiceFlags, accepted: ServiceFlags) -> Self { 35 | Services { 36 | provided, 37 | accepted 38 | } 39 | } 40 | 41 | pub fn full() -> Self { 42 | Services { 43 | provided: ServiceFlags::FULL, 44 | accepted: ServiceFlags::FULL, 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /network-primitives/src/time.rs: -------------------------------------------------------------------------------- 1 | use std::time::SystemTime; 2 | use std::time::Duration; 3 | use utils::time::systemtime_to_timestamp; 4 | use atomic::Atomic; 5 | use atomic::Ordering; 6 | 7 | #[derive(Debug, Default)] 8 | pub struct NetworkTime { 9 | offset: Atomic 10 | } 11 | 12 | impl NetworkTime { 13 | pub fn new() -> Self { 14 | NetworkTime::with_offset(0) 15 | } 16 | 17 | pub fn with_offset(offset: i64) -> Self { 18 | NetworkTime { 19 | offset: Atomic::new(offset) 20 | } 21 | } 22 | 23 | pub fn set_offset(&self, new_offset: i64) { 24 | self.offset.store(new_offset, Ordering::Relaxed); 25 | } 26 | 27 | pub fn now(&self) -> u64 { 28 | let offset = self.offset.load(Ordering::Relaxed); 29 | let abs_offset = offset.abs() as u64; 30 | let system_time = if offset > 0 { 31 | SystemTime::now() + Duration::from_millis(abs_offset) 32 | } else { 33 | SystemTime::now() - Duration::from_millis(abs_offset) 34 | }; 35 | 36 | systemtime_to_timestamp(system_time) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /network-primitives/src/version.rs: -------------------------------------------------------------------------------- 1 | pub const CODE: u32 = 1; 2 | 3 | pub fn is_compatible(code: u32) -> bool { 4 | // Allow future, backwards-compatible versions. 5 | code >= CODE 6 | } 7 | -------------------------------------------------------------------------------- /network-primitives/tests/address/mod.rs: -------------------------------------------------------------------------------- 1 | mod peer_address; 2 | mod peer_uri; 3 | -------------------------------------------------------------------------------- /network-primitives/tests/address/peer_address.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimiq/core-rs/24a91874d50ea052cd7a5e1b603b57bb0596a4f0/network-primitives/tests/address/peer_address.rs -------------------------------------------------------------------------------- /network-primitives/tests/address/peer_uri.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use network_primitives::address::PeerUri; 4 | use network_primitives::protocol::Protocol; 5 | 6 | 7 | 8 | #[test] 9 | fn test_parse_uri_dumb() { 10 | let uri = PeerUri::from_str("dumb://2b3f0f59334ef71ee7869b451139587f").unwrap(); 11 | assert_eq!(uri.protocol(), Protocol::Dumb); 12 | assert_eq!(uri.hostname(), None); 13 | assert_eq!(uri.port(), None); 14 | assert_eq!(uri.peer_id(), Some(String::from("2b3f0f59334ef71ee7869b451139587f")).as_ref()); 15 | } 16 | 17 | #[test] 18 | fn test_parse_uri_rtc() { 19 | let uri = PeerUri::from_str("rtc://2b3f0f59334ef71ee7869b451139587f").unwrap(); 20 | assert_eq!(uri.protocol(), Protocol::Rtc); 21 | assert_eq!(uri.hostname(), None); 22 | assert_eq!(uri.port(), None); 23 | assert_eq!(uri.peer_id(), Some(String::from("2b3f0f59334ef71ee7869b451139587f")).as_ref()); 24 | } 25 | 26 | #[test] 27 | fn test_parse_uri_ws_port_peerid() { 28 | let uri = PeerUri::from_str("ws://seed-20.nimiq.com:8443/2b3f0f59334ef71ee7869b451139587f").unwrap(); 29 | assert_eq!(uri.protocol(), Protocol::Ws); 30 | assert_eq!(uri.hostname(), Some(String::from("seed-20.nimiq.com")).as_ref()); 31 | assert_eq!(uri.port(), Some(8443)); 32 | assert_eq!(uri.peer_id(), Some(String::from("2b3f0f59334ef71ee7869b451139587f")).as_ref()); 33 | } 34 | 35 | #[test] 36 | fn test_parse_uri_ws_peerid() { 37 | let uri = PeerUri::from_str("ws://seed-20.nimiq.com/2b3f0f59334ef71ee7869b451139587f").unwrap(); 38 | assert_eq!(uri.protocol(), Protocol::Ws); 39 | assert_eq!(uri.hostname(), Some(String::from("seed-20.nimiq.com")).as_ref()); 40 | assert_eq!(uri.port(), None); 41 | assert_eq!(uri.peer_id(), Some(String::from("2b3f0f59334ef71ee7869b451139587f")).as_ref()); 42 | } 43 | 44 | #[test] 45 | fn test_parse_uri_ws1() { 46 | let uri = PeerUri::from_str("ws://seed-20.nimiq.com/").unwrap(); 47 | assert_eq!(uri.protocol(), Protocol::Ws); 48 | assert_eq!(uri.hostname(), Some(String::from("seed-20.nimiq.com")).as_ref()); 49 | assert_eq!(uri.port(), None); 50 | assert_eq!(uri.peer_id(), None); 51 | } 52 | 53 | #[test] 54 | fn test_parse_uri_ws2() { 55 | let uri = PeerUri::from_str("ws://seed-20.nimiq.com").unwrap(); 56 | assert_eq!(uri.protocol(), Protocol::Ws); 57 | assert_eq!(uri.hostname(), Some(String::from("seed-20.nimiq.com")).as_ref()); 58 | assert_eq!(uri.port(), None); 59 | assert_eq!(uri.peer_id(), None); 60 | } 61 | 62 | #[test] 63 | fn test_parse_uri_ws() { 64 | let uri = PeerUri::from_str("wss://seed-20.nimiq.com/2b3f0f59334ef71ee7869b451139587f").unwrap(); 65 | assert_eq!(uri.protocol(), Protocol::Wss); 66 | assert_eq!(uri.hostname(), Some(String::from("seed-20.nimiq.com")).as_ref()); 67 | assert_eq!(uri.port(), None); 68 | assert_eq!(uri.peer_id(), Some(String::from("2b3f0f59334ef71ee7869b451139587f")).as_ref()); 69 | } 70 | 71 | -------------------------------------------------------------------------------- /network-primitives/tests/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate nimiq_network_primitives as network_primitives; 2 | 3 | #[cfg(feature = "networks")] 4 | mod networks; 5 | #[cfg(feature = "subscription")] 6 | mod subscription; 7 | mod address; 8 | -------------------------------------------------------------------------------- /network-primitives/tests/networks/mod.rs: -------------------------------------------------------------------------------- 1 | use nimiq_hash::{Blake2bHash, Hash}; 2 | use nimiq_network_primitives::networks::*; 3 | use hex; 4 | 5 | #[test] 6 | fn it_has_expected_main_hash() { 7 | assert_eq!( 8 | get_network_info(NetworkId::Main).unwrap().genesis_block.header.hash::().as_bytes(), 9 | &hex::decode("264AAF8A4F9828A76C550635DA078EB466306A189FCC03710BEE9F649C869D12").unwrap()[..] 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /network-primitives/tests/subscription/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate nimiq_keys as keys; 2 | extern crate nimiq_network_primitives as network_primitives; 3 | extern crate nimiq_primitives as primitives; 4 | 5 | use beserial::Deserialize; 6 | use network_primitives::subscription::{Subscription, SubscriptionType}; 7 | use primitives::coin::Coin; 8 | use keys::Address; 9 | use std::collections::HashSet; 10 | 11 | // Nimiq.Subscription.fromAddresses([Nimiq.Address.fromUserFriendlyAddress("NQ43 P4RM 6KUV RP79 QMTR SE0K FJRQ K22P MD35"),Nimiq.Address.fromUserFriendlyAddress("NQ29 8MRT RYYN CNCE XRN5 DGYG HA2G CAVC MPDJ")]).serialize().toString() 12 | const SUBSCRIPTION_ADDRESSES: &'static str = "020002b933534f9dcdce9c5779d38137cb3898857ab4654573bcfff66598ef66c56c3f08a85062bacaddb2"; 13 | 14 | // Nimiq.Subscription.fromMinFeePerByte(42).serialize().toString() 15 | const SUBSCRIPTION_MINFEE: &'static str = "03000000000000002a"; 16 | 17 | // Subscription::None 18 | const SUBSCRIPTION_NONE: &'static str = "00"; 19 | 20 | // Subscription::Any 21 | const SUBSCRIPTION_ANY: &'static str = "01"; 22 | 23 | 24 | 25 | #[test] 26 | fn test_subscription_none() { 27 | let vec = hex::decode(SUBSCRIPTION_NONE).unwrap(); 28 | let subscription: Subscription = Deserialize::deserialize(&mut &vec[..]).unwrap(); 29 | assert_eq!(SubscriptionType::None, subscription.subscription_type()); 30 | } 31 | 32 | #[test] 33 | fn test_subscription_any() { 34 | let vec = hex::decode(SUBSCRIPTION_ANY).unwrap(); 35 | let subscription: Subscription = Deserialize::deserialize(&mut &vec[..]).unwrap(); 36 | assert_eq!(SubscriptionType::Any, subscription.subscription_type()); 37 | } 38 | 39 | #[test] 40 | fn test_subscription_minfee() { 41 | let vec = hex::decode(SUBSCRIPTION_MINFEE).unwrap(); 42 | let subscription: Subscription = Deserialize::deserialize(&mut &vec[..]).unwrap(); 43 | match subscription { 44 | Subscription::MinFee(fee) => assert_eq!(Coin::from_u64(42u64).unwrap(), fee), 45 | _ => assert!(false) 46 | }; 47 | } 48 | 49 | #[test] 50 | fn test_subscription_addresses() { 51 | let vec = hex::decode(SUBSCRIPTION_ADDRESSES).unwrap(); 52 | let subscription: Subscription = Deserialize::deserialize(&mut &vec[..]).unwrap(); 53 | match subscription { 54 | Subscription::Addresses(addresses) => { 55 | let mut expected = HashSet::with_capacity(2); 56 | expected.insert(Address::from_user_friendly_address(&String::from("NQ43 P4RM 6KUV RP79 QMTR SE0K FJRQ K22P MD35")).unwrap()); 57 | expected.insert(Address::from_user_friendly_address(&String::from("NQ29 8MRT RYYN CNCE XRN5 DGYG HA2G CAVC MPDJ")).unwrap()); 58 | assert_eq!(expected, addresses); 59 | }, 60 | _ => assert!(false) 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /network/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-network" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Nimiq's network implementation in Rust" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | atomic = "0.4" 21 | failure = "0.1" 22 | futures = "0.1" 23 | hex = "0.3" 24 | log = "0.4" 25 | native-tls = "0.2" 26 | parking_lot = "0.7" 27 | rand = "0.6" 28 | reqwest = "0.9" 29 | tokio = "0.1" 30 | tokio-tls = "0.2" 31 | tokio-tungstenite = "0.8" 32 | tk-listen = "0.2.1" 33 | url = "1.7" 34 | beserial = { path = "../beserial", version = "0.2" } 35 | beserial_derive = { path = "../beserial/beserial_derive", version = "0.2" } 36 | nimiq-blockchain = { path = "../blockchain", version = "0.2" } 37 | nimiq-collections = { path = "../collections", version = "0.2" } 38 | nimiq-keys = { path = "../keys", version = "0.2" } 39 | nimiq-hash = { path = "../hash", version = "0.2" } 40 | nimiq-macros = { path = "../macros", version = "0.2" } 41 | nimiq-messages = { path = "../messages", version = "0.2" } 42 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2", features = ["all"] } 43 | nimiq-utils = { path = "../utils", version = "0.2", features = ["timers", "observer", "mutable-once", "time", "unique-ptr", "iterators", "locking", "rate-limit", "unique-id"] } 44 | 45 | [dependencies.tungstenite] 46 | version = "0.8" 47 | default-features = false 48 | 49 | [features] 50 | metrics = [] 51 | -------------------------------------------------------------------------------- /network/src/address/mod.rs: -------------------------------------------------------------------------------- 1 | mod peer_address_seeder; 2 | pub mod peer_address_book; 3 | pub mod peer_address_state; 4 | -------------------------------------------------------------------------------- /network/src/connection/close_type.rs: -------------------------------------------------------------------------------- 1 | use tungstenite::protocol::frame::coding::CloseCode; 2 | 3 | use beserial::Deserialize; 4 | 5 | #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Deserialize)] 6 | #[repr(u16)] 7 | pub enum CloseType { 8 | Unknown = 0, 9 | GetBlocksTimeout = 1, 10 | GetChainProofTimeout = 2, 11 | GetAccountsTreeChunkTimeout = 3, 12 | GetHeaderTimeout = 4, 13 | InvalidAccountsTreeChunk = 5, 14 | AccountsTreeChunckRootHashMismatch = 6, 15 | ReceivedWrongHeader = 8, 16 | DidNotGetRequestedHeader = 9, 17 | 18 | GetAccountsProofTimeout = 11, 19 | GetTransactionsProofTimeout = 12, 20 | GetTransactionReceiptsTimeout = 13, 21 | InvalidAccountsProof = 14, 22 | AccountsProofRootHashMismatch = 15, 23 | IncompleteAccountsProof = 16, 24 | InvalidBlock = 17, 25 | InvalidChainProof = 18, 26 | InvalidTransactionProof = 19, 27 | InvalidBlockProof = 20, 28 | 29 | SendFailed = 21, 30 | SendingPingMessageFailed = 22, 31 | SendingVersionMessageFailed = 23, 32 | 33 | SimultaneousConnection = 29, 34 | DuplicateConnection = 30, 35 | PeerIsBanned = 31, 36 | ManualNetworkDisconnect = 33, 37 | ManualWebsocketDisconnect = 34, 38 | MaxPeerCountReached = 35, 39 | 40 | PeerConnectionRecycled = 36, 41 | PeerConnectionRecycledInboundExchange = 37, 42 | InboundConnectionsBlocked = 38, 43 | 44 | InvalidConnectionState = 40, 45 | 46 | ManualPeerDisconnect = 90, 47 | 48 | // Ban Close Types 49 | 50 | ReceivedInvalidBlock = 100, 51 | BlockchainSyncFailed = 101, 52 | ReceivedInvalidHeader = 102, 53 | ReceivedTransactionNotMatchingOurSubscription = 103, 54 | AddrMessageTooLarge = 104, 55 | InvalidAddr = 105, 56 | AddrNotGloballyReachable = 106, 57 | InvalidSignalTtl = 107, 58 | InvalidSignature = 108, 59 | ReceivedBlockNotMatchingOurSubscription = 109, 60 | 61 | IncompatibleVersion = 110, 62 | DifferentGenesisBlock = 111, 63 | InvalidPeerAddressInVersionMessage = 112, 64 | UnexpectedPeerAddressInVersionMessage = 113, 65 | InvalidPublicKeyInVerackMessage = 114, 66 | InvalidSignatureInVerackMessage = 115, 67 | BannedIp = 116, 68 | 69 | RateLimitExceeded = 120, 70 | 71 | ManualPeerBan = 190, 72 | 73 | // Fail Close Types 74 | 75 | ClosedByRemote = 200, 76 | PingTimeout = 201, 77 | ConnectionFailed = 202, 78 | NetworkError = 203, 79 | VersionTimeout = 204, 80 | VerackTimeout = 205, 81 | AbortedSync = 206, 82 | FailedToParseMessageType = 207, 83 | ConnectionLimitPerIp = 208, 84 | ChannelClosing = 209, 85 | ConnectionLimitDumb = 210, 86 | 87 | ManualPeerFail = 290, 88 | } 89 | 90 | impl CloseType { 91 | pub fn is_banning_type(self) -> bool { 92 | (self as u16) >= 100 && (self as u16) < 200 93 | } 94 | 95 | pub fn is_failing_type(self) -> bool { 96 | (self as u16) >= 200 97 | } 98 | } 99 | 100 | impl Into for CloseType { 101 | fn into(self) -> CloseCode { 102 | CloseCode::Library(4000 + (self as u16)) // Library specific is 4000-4999. 103 | } 104 | } 105 | 106 | impl From for CloseType { 107 | fn from(code: CloseCode) -> Self { 108 | match code { 109 | CloseCode::Library(code) => Deserialize::deserialize_from_vec(&(code - 4000).to_be_bytes().to_vec()).unwrap_or(CloseType::Unknown), 110 | _ => CloseType::Unknown, 111 | } 112 | } 113 | } 114 | 115 | impl From> for CloseType { 116 | fn from(code: Option) -> Self { 117 | match code { 118 | Some(code) => code.into(), 119 | _ => CloseType::Unknown, 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /network/src/connection/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod connection_pool; 2 | pub mod connection_info; 3 | pub mod close_type; 4 | pub mod network_connection; 5 | pub mod network_agent; 6 | mod signal_processor; 7 | 8 | pub use self::network_connection::*; 9 | 10 | -------------------------------------------------------------------------------- /network/src/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | use tokio::io::Error as IoError; 3 | 4 | use network_primitives::networks::NetworkId; 5 | use network_primitives::address::peer_uri::PeerUriError; 6 | 7 | use crate::websocket::error::ServerStartError; 8 | 9 | 10 | #[derive(Fail, Debug)] 11 | pub enum Error { 12 | #[fail(display = "PeerKey could not be deserialized")] 13 | InvalidPeerKey, 14 | #[fail(display = "PeerKey has not been initialized")] 15 | UninitializedPeerKey, 16 | #[fail(display = "{}", _0)] 17 | IoError(#[cause] IoError), 18 | #[fail(display = "{}", _0)] 19 | ServerStartError(#[cause] ServerStartError), 20 | #[fail(display = "Could not load network info for id {:?}", _0)] 21 | InvalidNetworkInfo(NetworkId), 22 | #[fail(display = "Could not add seed node {}", _0)] 23 | InvalidSeed(#[cause] SeedError) 24 | } 25 | 26 | impl From for Error { 27 | fn from(e: IoError) -> Self { 28 | Error::IoError(e) 29 | } 30 | } 31 | 32 | impl From for Error { 33 | fn from(e: ServerStartError) -> Self { 34 | Error::ServerStartError(e) 35 | } 36 | } 37 | 38 | impl From for Error { 39 | fn from(e: SeedError) -> Self { 40 | Error::InvalidSeed(e) 41 | } 42 | } 43 | 44 | #[derive(Debug, Fail)] 45 | pub enum SeedError { 46 | #[fail(display = "Invalid peer URI: {}", _0)] 47 | Peer(#[cause] PeerUriError), 48 | #[fail(display = "Invalid seed list URL: {}", _0)] 49 | Url(#[cause] url::ParseError) 50 | } 51 | 52 | impl From for SeedError { 53 | fn from(e: PeerUriError) -> SeedError { 54 | SeedError::Peer(e) 55 | } 56 | } 57 | 58 | impl From for SeedError { 59 | fn from(e: url::ParseError) -> SeedError { 60 | SeedError::Url(e) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /network/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | #[macro_use] 5 | extern crate beserial_derive; 6 | #[macro_use] 7 | extern crate nimiq_macros as macros; 8 | extern crate nimiq_messages as network_messages; 9 | extern crate nimiq_network_primitives as network_primitives; 10 | extern crate nimiq_utils as utils; 11 | extern crate nimiq_blockchain as blockchain; 12 | extern crate nimiq_hash as hash; 13 | extern crate nimiq_keys as keys; 14 | extern crate nimiq_collections as collections; 15 | 16 | pub mod address; 17 | pub mod websocket; 18 | pub mod peer_channel; 19 | pub mod peer_scorer; 20 | pub mod connection; 21 | pub mod peer; 22 | pub mod network_config; 23 | pub mod network; 24 | pub mod error; 25 | #[cfg(feature = "metrics")] 26 | mod network_metrics; 27 | 28 | pub use crate::peer::Peer; 29 | pub use crate::network::{Network, NetworkEvent}; 30 | pub use crate::network_config::NetworkConfig; 31 | 32 | -------------------------------------------------------------------------------- /network/src/peer.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::hash::Hash; 3 | use std::hash::Hasher; 4 | use std::sync::Arc; 5 | 6 | use hash::Blake2bHash; 7 | use network_primitives::address::net_address::NetAddress; 8 | use network_primitives::address::peer_address::PeerAddress; 9 | 10 | use crate::peer_channel::PeerChannel; 11 | 12 | #[derive(Clone, Debug)] 13 | pub struct Peer { 14 | pub channel: Arc, 15 | pub version: u32, 16 | pub head_hash: Blake2bHash, 17 | pub time_offset: i64, 18 | pub user_agent: Option, 19 | } 20 | 21 | impl Peer { 22 | pub fn new(channel: Arc, version: u32, head_hash: Blake2bHash, time_offset: i64, user_agent: Option) -> Self { 23 | Peer { 24 | channel, 25 | version, 26 | head_hash, 27 | time_offset, 28 | user_agent 29 | } 30 | } 31 | 32 | pub fn peer_address(&self) -> Arc { 33 | // If a peer object exists, peer_address should be set. 34 | self.channel.address_info.peer_address().unwrap() 35 | } 36 | 37 | pub fn net_address(&self) -> Option> { 38 | self.channel.address_info.net_address() 39 | } 40 | } 41 | 42 | impl fmt::Display for Peer { 43 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 44 | write!(f, "{}", self.peer_address()) 45 | } 46 | } 47 | 48 | impl PartialEq for Peer { 49 | fn eq(&self, other: &Peer) -> bool { 50 | self.channel == other.channel 51 | } 52 | } 53 | 54 | impl Eq for Peer {} 55 | 56 | impl Hash for Peer { 57 | fn hash(&self, state: &mut H) { 58 | self.channel.hash(state); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /network/src/peer_channel/mod.rs: -------------------------------------------------------------------------------- 1 | pub use self::channel::*; 2 | pub use self::sink::*; 3 | pub use self::stream::*; 4 | 5 | pub mod channel; 6 | pub mod sink; 7 | pub mod stream; 8 | 9 | -------------------------------------------------------------------------------- /network/src/peer_channel/sink.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::fmt::Debug; 3 | use std::hash::Hash; 4 | use std::hash::Hasher; 5 | 6 | use futures::sync::mpsc::*; 7 | 8 | use network_messages::Message; 9 | use utils::unique_id::UniqueId; 10 | 11 | use crate::connection::close_type::CloseType; 12 | use crate::websocket::Message as WebSocketMessage; 13 | use crate::connection::network_connection::ClosedFlag; 14 | 15 | #[derive(Clone)] 16 | pub struct PeerSink { 17 | sink: UnboundedSender, 18 | unique_id: UniqueId, 19 | closed_flag: ClosedFlag, 20 | } 21 | 22 | impl PeerSink { 23 | pub fn new(channel: UnboundedSender, unique_id: UniqueId, closed_flag: ClosedFlag) -> Self { 24 | PeerSink { 25 | sink: channel, 26 | unique_id, 27 | closed_flag, 28 | } 29 | } 30 | 31 | pub fn send(&self, msg: Message) -> Result<(), SendError> { 32 | // Do not send messages over already closed connections. 33 | // Stop sending silently until connection is really closed. 34 | if self.closed_flag.is_closed() { 35 | return Ok(()); 36 | } 37 | self.sink.unbounded_send(WebSocketMessage::Message(msg)) 38 | } 39 | 40 | /// Closes the connection. 41 | pub fn close(&self, ty: CloseType, reason: Option) { 42 | // Immediately mark channel as closed, so that no more messages are sent over it. 43 | // Do not send messages over already closed connections. 44 | // Stop sending silently until connection is really closed. 45 | if self.closed_flag.set_closed(true) { 46 | return; 47 | } 48 | self.closed_flag.set_close_type(ty); 49 | debug!("Closing connection, reason: {:?} ({:?})", ty, reason); 50 | if let Err(error) = self.sink.unbounded_send(WebSocketMessage::Close(None)) { 51 | debug!("Error closing connection: {}", error); 52 | } 53 | 54 | /* 55 | * Theoretically, we can also send the CloseType to the other party: 56 | * Some(CloseFrame { 57 | * code: ty.into(), 58 | * reason: Cow::Owned(reason.unwrap_or_else(|| "Unknown reason".to_string())), 59 | * }) 60 | */ 61 | } 62 | } 63 | 64 | impl Debug for PeerSink { 65 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 66 | write!(f, "PeerSink {{}}") 67 | } 68 | } 69 | 70 | impl PartialEq for PeerSink { 71 | fn eq(&self, other: &PeerSink) -> bool { 72 | self.unique_id == other.unique_id 73 | } 74 | } 75 | 76 | impl Eq for PeerSink {} 77 | 78 | impl Hash for PeerSink { 79 | fn hash(&self, state: &mut H) { 80 | self.unique_id.hash(state); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /network/src/websocket/client.rs: -------------------------------------------------------------------------------- 1 | use futures::future; 2 | use futures::prelude::*; 3 | use tokio_tungstenite::connect_async; 4 | use url::Url; 5 | 6 | use crate::websocket::error::Error; 7 | use crate::websocket::NimiqMessageStream; 8 | 9 | /// Connect to a given URL and return a Future that will resolve to a NimiqMessageStream 10 | pub fn nimiq_connect_async(url: Url) -> Box + Send> { 11 | Box::new( 12 | connect_async(url).then(|result| { 13 | match result { 14 | Ok((ws_stream,_)) => future::result(NimiqMessageStream::new(ws_stream, true)), 15 | Err(e) => future::err(e.into()) 16 | } 17 | }) 18 | ) 19 | } -------------------------------------------------------------------------------- /network/src/websocket/mod.rs: -------------------------------------------------------------------------------- 1 | use tungstenite::protocol::CloseFrame; 2 | 3 | use network_messages::Message as NimiqMessage; 4 | 5 | pub use self::client::nimiq_connect_async; 6 | pub use self::error::Error; 7 | pub use self::server::nimiq_accept_async; 8 | pub use self::shared_stream::SharedNimiqMessageStream; 9 | pub use self::stream::NimiqMessageStream; 10 | 11 | pub mod websocket_connector; 12 | mod reverse_proxy; 13 | pub mod error; 14 | pub mod public_state; 15 | pub mod stream; 16 | pub mod client; 17 | pub mod server; 18 | pub mod shared_stream; 19 | 20 | /// This enum encapsulates the two types of messages we send over the channel: 21 | /// - Nimiq messages 22 | /// - Close frames 23 | #[derive(Debug)] 24 | pub enum Message { 25 | Message(NimiqMessage), // TODO: Box? 26 | Close(Option>), 27 | #[doc(hidden)] 28 | Resume(Vec, Option), 29 | } 30 | 31 | -------------------------------------------------------------------------------- /network/src/websocket/public_state.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "metrics")] 2 | use std::sync::Arc; 3 | 4 | use network_primitives::address::net_address::NetAddress; 5 | 6 | #[cfg(feature = "metrics")] 7 | use crate::network_metrics::NetworkMetrics; 8 | use crate::websocket::NimiqMessageStream; 9 | 10 | /// This struct stores public information about the stream. 11 | #[derive(Clone, Debug)] 12 | pub struct PublicStreamInfo { 13 | // Constant info. 14 | pub net_address: NetAddress, 15 | pub outbound: bool, 16 | 17 | #[cfg(feature = "metrics")] 18 | pub network_metrics: Arc, 19 | } 20 | 21 | impl<'a> From<&'a PublicStreamInfo> for PublicStreamInfo { 22 | fn from(state: &'a PublicStreamInfo) -> Self { 23 | state.clone() 24 | } 25 | } 26 | 27 | impl<'a> From<&'a NimiqMessageStream> for PublicStreamInfo { 28 | fn from(stream: &'a NimiqMessageStream) -> Self { 29 | stream.public_state.clone() 30 | } 31 | } 32 | 33 | impl PublicStreamInfo { 34 | pub fn new(net_address: NetAddress, outbound: bool) -> Self { 35 | PublicStreamInfo { 36 | net_address, 37 | outbound, 38 | 39 | #[cfg(feature = "metrics")] 40 | network_metrics: Arc::new(NetworkMetrics::default()), 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /network/src/websocket/server.rs: -------------------------------------------------------------------------------- 1 | use futures::future; 2 | use futures::prelude::*; 3 | use tokio::net::TcpStream; 4 | use tokio_tungstenite::{accept_hdr_async, MaybeTlsStream}; 5 | use tungstenite::handshake::server::Callback; 6 | 7 | use crate::websocket::error::Error; 8 | use crate::websocket::NimiqMessageStream; 9 | 10 | /// Accept an incoming connection and return a Future that will resolve to a NimiqMessageStream 11 | pub fn nimiq_accept_async(stream: MaybeTlsStream, callback: C) -> Box + Send> 12 | where C: Callback + Send + 'static { // TODO: Why do we need 'static here?! 13 | Box::new( 14 | accept_hdr_async(stream, callback).then(|result| { 15 | match result { 16 | Ok(ws_stream) => future::result(NimiqMessageStream::new(ws_stream, false)), 17 | Err(e) => future::err(e.into()) 18 | } 19 | }) 20 | ) 21 | } -------------------------------------------------------------------------------- /network/src/websocket/shared_stream.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "metrics")] 2 | use std::sync::Arc; 3 | 4 | use futures::prelude::*; 5 | 6 | use utils::locking::MultiLock; 7 | use network_primitives::address::net_address::NetAddress; 8 | 9 | #[cfg(feature = "metrics")] 10 | use crate::network_metrics::NetworkMetrics; 11 | use crate::websocket::error::Error; 12 | use crate::websocket::Message; 13 | use crate::websocket::NimiqMessageStream; 14 | use crate::websocket::public_state::PublicStreamInfo; 15 | 16 | #[derive(Clone, Debug)] 17 | pub struct SharedNimiqMessageStream { 18 | inner: MultiLock, 19 | state: PublicStreamInfo, 20 | } 21 | 22 | impl SharedNimiqMessageStream { 23 | pub fn net_address(&self) -> NetAddress { 24 | self.state.net_address 25 | } 26 | 27 | pub fn outbound(&self) -> bool { 28 | self.state.outbound 29 | } 30 | 31 | #[cfg(feature = "metrics")] 32 | pub fn network_metrics(&self) -> &Arc { 33 | &self.state.network_metrics 34 | } 35 | } 36 | 37 | impl From for SharedNimiqMessageStream { 38 | fn from(stream: NimiqMessageStream) -> Self { 39 | let state = PublicStreamInfo::from(&stream); 40 | SharedNimiqMessageStream { 41 | inner: MultiLock::new(stream), 42 | state, 43 | } 44 | } 45 | } 46 | 47 | impl Stream for SharedNimiqMessageStream { 48 | type Item = Message; 49 | type Error = Error; 50 | 51 | fn poll(&mut self) -> Poll, Self::Error> { 52 | match self.inner.poll_lock() { 53 | Async::Ready(mut inner) => inner.poll(), 54 | Async::NotReady => Ok(Async::NotReady), 55 | } 56 | } 57 | } 58 | 59 | impl Sink for SharedNimiqMessageStream { 60 | type SinkItem = Message; 61 | type SinkError = Error; 62 | 63 | fn start_send(&mut self, item: Self::SinkItem) 64 | -> StartSend 65 | { 66 | match self.inner.poll_lock() { 67 | Async::Ready(mut inner) => inner.start_send(item), 68 | Async::NotReady => Ok(AsyncSink::NotReady(item)), 69 | } 70 | } 71 | 72 | fn poll_complete(&mut self) -> Poll<(), Self::SinkError> { 73 | match self.inner.poll_lock() { 74 | Async::Ready(mut inner) => inner.poll_complete(), 75 | Async::NotReady => Ok(Async::NotReady), 76 | } 77 | } 78 | 79 | fn close(&mut self) -> Poll<(), Self::SinkError> { 80 | match self.inner.poll_lock() { 81 | Async::Ready(mut inner) => inner.close(), 82 | Async::NotReady => Ok(Async::NotReady), 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /network/tests/mod.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nimiq/core-rs/24a91874d50ea052cd7a5e1b603b57bb0596a4f0/network/tests/mod.rs -------------------------------------------------------------------------------- /primitives/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-primitives" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Primitives (e.g., block, account, transaction) to be used in Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | num-bigint = { version = "0.2", optional = true } 21 | num-traits = { version = "0.2", optional = true } 22 | parking_lot = { version = "0.7", optional = true } 23 | hex = { version = "0.3", optional = true } 24 | lazy_static = { version = "1.2", optional = true } 25 | failure = { version = "0.1", optional = true} 26 | enum-display-derive = { version = "0.1", optional = true } 27 | beserial = { path = "../beserial", version = "0.2" } 28 | beserial_derive = { path = "../beserial/beserial_derive", version = "0.2" } 29 | nimiq-macros = { path = "../macros", version = "0.2", optional = true } 30 | fixed-unsigned = { path = "../fixed-unsigned", version = "0.2", optional = true } 31 | 32 | [dev-dependencies] 33 | lazy_static = "1.2" 34 | 35 | [features] 36 | default = ["all"] 37 | all = ["coin", "account", "policy", "networks"] 38 | coin = ["hex", "failure"] 39 | account = ["hex", "nimiq-macros", "failure", "enum-display-derive"] 40 | policy = ["num-bigint", "num-traits", "parking_lot", "lazy_static", "fixed-unsigned"] 41 | networks = [] 42 | -------------------------------------------------------------------------------- /primitives/account/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-account" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Account primitives to be used in Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | log = "0.4" 21 | failure = "0.1" 22 | beserial = { path = "../../beserial", version = "0.2" } 23 | beserial_derive = { path = "../../beserial/beserial_derive", version = "0.2" } 24 | nimiq-hash = { path = "../../hash", version = "0.2" } 25 | nimiq-keys = { path = "../../keys", version = "0.2" } 26 | nimiq-transaction = { path = "../transaction", version = "0.2" } 27 | nimiq-primitives = { path = "..", version = "0.2", features = ["coin"] } 28 | 29 | [dev-dependencies] 30 | hex = "0.3" 31 | -------------------------------------------------------------------------------- /primitives/account/src/basic_account.rs: -------------------------------------------------------------------------------- 1 | use beserial::{Deserialize, Serialize}; 2 | use primitives::coin::Coin; 3 | use transaction::Transaction; 4 | 5 | use crate::Account; 6 | use crate::AccountError; 7 | use crate::AccountTransactionInteraction; 8 | use crate::AccountType; 9 | 10 | #[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)] 11 | pub struct BasicAccount { 12 | pub balance: Coin 13 | } 14 | 15 | impl AccountTransactionInteraction for BasicAccount { 16 | fn new_contract(_account_type: AccountType, _balance: Coin, _transaction: &Transaction, _block_height: u32) -> Result { 17 | Err(AccountError::InvalidForRecipient) 18 | } 19 | 20 | fn create(_balance: Coin, _transaction: &Transaction, _block_height: u32) -> Result { 21 | Err(AccountError::InvalidForRecipient) 22 | } 23 | 24 | fn with_incoming_transaction(&self, transaction: &Transaction, _block_height: u32) -> Result { 25 | let balance: Coin = Account::balance_add(self.balance, transaction.value)?; 26 | Ok(BasicAccount { balance }) 27 | } 28 | 29 | fn without_incoming_transaction(&self, transaction: &Transaction, _block_height: u32) -> Result { 30 | let balance: Coin = Account::balance_sub(self.balance, transaction.value)?; 31 | Ok(BasicAccount { balance }) 32 | } 33 | 34 | fn with_outgoing_transaction(&self, transaction: &Transaction, _block_height: u32) -> Result { 35 | let balance: Coin = Account::balance_sub(self.balance, transaction.value.checked_add(transaction.fee).ok_or(AccountError::InvalidCoinValue)?)?; 36 | Ok(BasicAccount { balance }) 37 | } 38 | 39 | fn without_outgoing_transaction(&self, transaction: &Transaction, _block_height: u32) -> Result { 40 | let balance: Coin = Account::balance_add(self.balance, transaction.value.checked_add(transaction.fee).ok_or(AccountError::InvalidCoinValue)?)?; 41 | Ok(BasicAccount { balance }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /primitives/account/tests/account/mod.rs: -------------------------------------------------------------------------------- 1 | mod htlc_contract; 2 | mod vesting_contract; 3 | -------------------------------------------------------------------------------- /primitives/account/tests/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate beserial; 2 | extern crate nimiq_hash as hash; 3 | extern crate nimiq_keys as keys; 4 | extern crate nimiq_primitives as primitives; 5 | extern crate nimiq_transaction as transaction; 6 | 7 | mod account; -------------------------------------------------------------------------------- /primitives/block/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-block" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Block primitives to be used in Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | num-bigint = "0.2" 21 | log = "0.4" 22 | hex = "0.3" 23 | beserial = { path = "../../beserial", version = "0.2" } 24 | beserial_derive = { path = "../../beserial/beserial_derive", version = "0.2" } 25 | fixed-unsigned = { path = "../../fixed-unsigned", version = "0.2" } 26 | nimiq-hash = { path = "../../hash", version = "0.2" } 27 | nimiq-keys = { path = "../../keys", version = "0.2" } 28 | nimiq-utils = { path = "../../utils", version = "0.2", features = ["merkle"] } 29 | nimiq-primitives = { path = "..", version = "0.2", features = ["policy", "networks"] } 30 | nimiq-transaction = { path = "../transaction", version = "0.2" } 31 | nimiq-account = { path = "../account", version = "0.2" } 32 | nimiq-macros = { path = "../../macros", version = "0.2" } 33 | 34 | [dev-dependencies] 35 | num-traits = "0.2" 36 | -------------------------------------------------------------------------------- /primitives/block/src/header.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use beserial::{Deserialize, Serialize}; 4 | use hash::{Argon2dHash, Blake2bHash, Hash, SerializeContent}; 5 | 6 | use crate::{Target, TargetCompact}; 7 | 8 | #[derive(Default, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)] 9 | pub struct BlockHeader { 10 | pub version: u16, 11 | pub prev_hash: Blake2bHash, 12 | pub interlink_hash: Blake2bHash, 13 | pub body_hash: Blake2bHash, 14 | pub accounts_hash: Blake2bHash, 15 | pub n_bits: TargetCompact, 16 | pub height: u32, 17 | pub timestamp: u32, 18 | pub nonce: u32, 19 | } 20 | 21 | impl SerializeContent for BlockHeader { 22 | fn serialize_content(&self, writer: &mut W) -> io::Result { Ok(self.serialize(writer)?) } 23 | } 24 | 25 | // Different hash implementation than std 26 | #[allow(clippy::derive_hash_xor_eq)] 27 | impl Hash for BlockHeader {} 28 | 29 | impl BlockHeader { 30 | pub const SIZE: usize = 146; 31 | 32 | pub fn verify_proof_of_work(&self) -> bool { 33 | let target: Target = self.n_bits.into(); 34 | target.is_met_by(&self.pow()) 35 | } 36 | 37 | pub fn pow(&self) -> Argon2dHash { 38 | self.hash() 39 | } 40 | 41 | pub fn is_immediate_successor_of(&self, prev_header: &BlockHeader) -> bool { 42 | // Check that the height is one higher than the previous height. 43 | if self.height != prev_header.height + 1 { 44 | return false; 45 | } 46 | 47 | // Check that the timestamp is greater or equal to the predecessor's timestamp. 48 | if self.timestamp < prev_header.timestamp { 49 | return false; 50 | } 51 | 52 | // Check that the hash of the predecessor block equals prevHash. 53 | let prev_hash: Blake2bHash = prev_header.hash(); 54 | if self.prev_hash != prev_hash { 55 | return false; 56 | } 57 | 58 | // Everything checks out. 59 | true 60 | } 61 | 62 | pub fn timestamp_in_millis(&self) -> u64 { 63 | u64::from(self.timestamp) * 1000 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /primitives/block/src/interlink.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | 3 | use beserial::{Deserialize, ReadBytesExt, Serialize, SerializingError, WriteBytesExt}; 4 | use hash::{Blake2bHash, Hash}; 5 | use utils::merkle; 6 | 7 | #[derive(Default, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)] 8 | pub struct BlockInterlink { 9 | pub hashes: Vec, 10 | repeat_bits: Vec, 11 | compressed: Vec, 12 | } 13 | 14 | impl BlockInterlink { 15 | #[inline] 16 | pub fn len(&self) -> usize { self.hashes.len() } 17 | 18 | #[inline] 19 | pub fn is_empty(&self) -> bool { self.hashes.is_empty() } 20 | 21 | fn compress(hashes: &[Blake2bHash], prev_hash: &Blake2bHash) -> (Vec, Vec) { 22 | let repeat_bits_size = if hashes.is_empty() { 0 } else { (hashes.len() - 1) / 8 + 1 }; 23 | let mut repeat_bits = vec![0u8; repeat_bits_size]; 24 | 25 | let mut hash = prev_hash; 26 | let mut compressed: Vec = vec![]; 27 | 28 | for i in 0..hashes.len() { 29 | if &hashes[i] != hash { 30 | hash = &hashes[i]; 31 | compressed.push(hash.clone()); 32 | } else { 33 | repeat_bits[(i / 8) as usize] |= 0x80 >> (i % 8); 34 | } 35 | } 36 | 37 | (repeat_bits, compressed) 38 | } 39 | 40 | pub fn new(hashes: Vec, prev_hash: &Blake2bHash) -> Self { 41 | let (repeat_bits, compressed) = Self::compress(&hashes, prev_hash); 42 | BlockInterlink { hashes, repeat_bits, compressed } 43 | } 44 | 45 | pub fn deserialize(reader: &mut R, prev_hash: &Blake2bHash) -> io::Result { 46 | let count: u8 = Deserialize::deserialize(reader)?; 47 | let repeat_bits_size = if count > 0 { (count - 1) / 8 + 1 } else { 0 }; 48 | let mut repeat_bits = vec![0u8; repeat_bits_size as usize]; 49 | reader.read_exact(&mut repeat_bits[..])?; 50 | 51 | let mut hashes = Vec::with_capacity(count as usize); 52 | let mut compressed: Vec = vec![]; 53 | 54 | for i in 0..count { 55 | let repeated = (repeat_bits[(i / 8) as usize] & (0x80 >> (i % 8))) != 0; 56 | if !repeated { 57 | compressed.push(Deserialize::deserialize(reader)?); 58 | } 59 | hashes.push(if !compressed.is_empty() { compressed[compressed.len() - 1].clone() } else { prev_hash.clone() }); 60 | } 61 | 62 | Ok(BlockInterlink { hashes, repeat_bits, compressed }) 63 | } 64 | } 65 | 66 | impl Serialize for BlockInterlink { 67 | fn serialize(&self, writer: &mut W) -> Result { 68 | let mut size = 0; 69 | size += Serialize::serialize(&(self.hashes.len() as u8), writer)?; 70 | writer.write_all(&self.repeat_bits[..])?; 71 | size += self.repeat_bits.len(); 72 | for h in &self.compressed { 73 | size += Serialize::serialize(&h, writer)?; 74 | } 75 | Ok(size) 76 | } 77 | 78 | fn serialized_size(&self) -> usize { 79 | let mut hash_sizes = 0; 80 | for h in &self.compressed { 81 | hash_sizes += Serialize::serialized_size(&h); 82 | } 83 | 84 | 1 + self.repeat_bits.len() + hash_sizes 85 | } 86 | } 87 | 88 | impl BlockInterlink { 89 | pub fn hash(&self, genesis_hash: Blake2bHash) -> Blake2bHash { 90 | let mut vec: Vec = Vec::with_capacity(2 + self.compressed.len()); 91 | vec.push(self.repeat_bits.hash()); 92 | vec.push(genesis_hash); 93 | for h in &self.compressed { 94 | vec.push(h.clone()); 95 | } 96 | merkle::compute_root_from_hashes::(&vec) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /primitives/block/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate beserial_derive; 3 | #[macro_use] 4 | extern crate log; 5 | extern crate nimiq_account as account; 6 | extern crate nimiq_hash as hash; 7 | extern crate nimiq_keys as keys; 8 | #[macro_use] 9 | extern crate nimiq_macros as macros; 10 | extern crate nimiq_primitives as primitives; 11 | extern crate nimiq_transaction as transaction; 12 | extern crate nimiq_utils as utils; 13 | 14 | mod block; 15 | mod body; 16 | mod header; 17 | mod interlink; 18 | mod target; 19 | pub mod proof; 20 | 21 | pub use self::block::Block; 22 | pub use self::body::BlockBody; 23 | pub use self::header::BlockHeader; 24 | pub use self::interlink::BlockInterlink; 25 | pub use self::target::{Target, TargetCompact, Difficulty}; 26 | 27 | use crate::transaction::TransactionError; 28 | 29 | #[derive(Clone, PartialEq, Eq, Debug)] 30 | pub enum BlockError { 31 | UnsupportedVersion, 32 | FromTheFuture, 33 | InvalidPoW, 34 | SizeExceeded, 35 | InterlinkHashMismatch, 36 | BodyHashMismatch, 37 | 38 | DuplicateTransaction, 39 | InvalidTransaction(TransactionError), 40 | ExpiredTransaction, 41 | TransactionsNotOrdered, 42 | 43 | DuplicatePrunedAccount, 44 | PrunedAccountsNotOrdered, 45 | InvalidPrunedAccount, 46 | } 47 | -------------------------------------------------------------------------------- /primitives/block/src/proof.rs: -------------------------------------------------------------------------------- 1 | use beserial::{Deserialize, Serialize}; 2 | use crate::{Block, BlockHeader}; 3 | 4 | #[derive(Clone, Debug, Serialize, Deserialize)] 5 | pub struct ChainProof { 6 | #[beserial(len_type(u16))] 7 | pub prefix: Vec, 8 | #[beserial(len_type(u16))] 9 | pub suffix: Vec, 10 | } 11 | -------------------------------------------------------------------------------- /primitives/block/tests/block/mod.rs: -------------------------------------------------------------------------------- 1 | mod block; 2 | mod body; 3 | mod header; 4 | mod target; 5 | -------------------------------------------------------------------------------- /primitives/block/tests/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate nimiq_account as account; 2 | extern crate nimiq_hash as hash; 3 | extern crate nimiq_keys as keys; 4 | extern crate nimiq_primitives as primitives; 5 | extern crate nimiq_transaction as transaction; 6 | 7 | mod block; -------------------------------------------------------------------------------- /primitives/src/account.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Display; 2 | 3 | use hex::FromHex; 4 | 5 | use beserial::{Deserialize, Serialize}; 6 | 7 | 8 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize, Display)] 9 | #[repr(u8)] 10 | pub enum AccountType { 11 | Basic = 0, 12 | Vesting = 1, 13 | HTLC = 2, 14 | } 15 | 16 | impl AccountType { 17 | pub fn from_int(x: u8) -> Option { 18 | match x { 19 | 0 => Some(AccountType::Basic), 20 | 1 => Some(AccountType::Vesting), 21 | 2 => Some(AccountType::HTLC), 22 | _ => None 23 | } 24 | } 25 | } 26 | 27 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)] 28 | #[repr(u8)] 29 | pub enum HashAlgorithm { 30 | Blake2b = 1, 31 | Sha256 = 3 32 | } 33 | 34 | #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)] 35 | #[repr(u8)] 36 | pub enum ProofType { 37 | RegularTransfer = 1, 38 | EarlyResolve = 2, 39 | TimeoutResolve = 3 40 | } 41 | 42 | create_typed_array!(AnyHash, u8, 32); 43 | add_hex_io_fns_typed_arr!(AnyHash, AnyHash::SIZE); 44 | -------------------------------------------------------------------------------- /primitives/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate beserial_derive; 3 | #[cfg(feature = "lazy_static")] 4 | #[macro_use] 5 | extern crate lazy_static; 6 | #[cfg(feature = "nimiq-macros")] 7 | #[macro_use] 8 | extern crate nimiq_macros; 9 | #[cfg(feature = "account")] 10 | #[macro_use] 11 | extern crate enum_display_derive; 12 | 13 | #[cfg(feature = "coin")] 14 | pub mod coin; 15 | #[cfg(feature = "account")] 16 | pub mod account; 17 | #[cfg(feature = "policy")] 18 | pub mod policy; 19 | #[cfg(feature = "networks")] 20 | pub mod networks; 21 | -------------------------------------------------------------------------------- /primitives/src/networks.rs: -------------------------------------------------------------------------------- 1 | use beserial::{Deserialize, Serialize}; 2 | use std::fmt::Display; 3 | 4 | #[derive(Serialize, Deserialize, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Debug, Hash, Display)] 5 | #[repr(u8)] 6 | pub enum NetworkId { 7 | Test = 1, 8 | Dev = 2, 9 | Bounty = 3, 10 | Dummy = 4, 11 | Main = 42, 12 | } -------------------------------------------------------------------------------- /primitives/tests/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate nimiq_primitives as primitives; 2 | #[cfg(feature = "lazy_static")] 3 | #[macro_use] 4 | extern crate lazy_static; 5 | #[cfg(feature = "fixed-unsigned")] 6 | extern crate fixed_unsigned; 7 | 8 | #[cfg(feature = "coin")] 9 | mod coin; 10 | -------------------------------------------------------------------------------- /primitives/transaction/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-transaction" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Transaction primitive to be used in Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | 11 | [badges] 12 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 13 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 14 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 15 | maintenance = { status = "experimental" } 16 | 17 | [dependencies] 18 | bitflags = "1.0" 19 | log = "0.4" 20 | failure = "0.1" 21 | nimiq-hash = { path = "../../hash", version = "0.2" } 22 | nimiq-keys = { path = "../../keys", version = "0.2" } 23 | nimiq-utils = { path = "../../utils", version = "0.2", features = ["merkle"] } 24 | nimiq-primitives = { path = "..", version = "0.2", features = ["policy", "networks", "account", "coin"] } 25 | beserial = { path = "../../beserial", version = "0.2" } 26 | beserial_derive = { path = "../../beserial/beserial_derive", version = "0.2" } 27 | 28 | [dev-dependencies] 29 | hex = "0.3" 30 | -------------------------------------------------------------------------------- /rpc-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-rpc-server" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | license = "Apache-2.0" 6 | edition = "2018" 7 | description = "JSON RPC server for the Nimiq Rust implementation" 8 | homepage = "https://nimiq.com" 9 | repository = "https://github.com/nimiq/core-rs" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | hyper = "0.12" 21 | json = "0.11" 22 | futures = "0.1" 23 | log = "0.4" 24 | hex = "0.3" 25 | failure = "0.1" 26 | parking_lot = "0.7" 27 | base64 = "0.10" 28 | beserial = { path = "../beserial", version = "0.2" } 29 | nimiq-consensus = { path = "../consensus", version = "0.2" } 30 | nimiq-blockchain = { path = "../blockchain", version = "0.2" } 31 | nimiq-mempool = { path = "../mempool", version = "0.2" } 32 | nimiq-network = { path = "../network", version = "0.2", features = ["metrics"] } 33 | nimiq-hash = { path = "../hash", version = "0.2" } 34 | nimiq-network-primitives = { path = "../network-primitives", version = "0.2" } 35 | nimiq-block = { path = "../primitives/block", version = "0.2" } 36 | nimiq-transaction = { path = "../primitives/transaction", version = "0.2" } 37 | nimiq-keys = { path = "../keys", version = "0.2" } 38 | nimiq-block-production = { path = "../block-production", version = "0.2" } 39 | nimiq-utils = { path = "../utils", version = "0.2", features = ["merkle", "time"] } 40 | -------------------------------------------------------------------------------- /rpc-server/src/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | use hyper::Error as HyperError; 3 | 4 | #[derive(Fail, Debug)] 5 | pub enum Error { 6 | #[fail(display = "{}", _0)] 7 | HyperError(#[cause] HyperError), 8 | } 9 | 10 | impl From for Error { 11 | fn from(e: HyperError) -> Self { 12 | Error::HyperError(e) 13 | } 14 | } 15 | 16 | #[derive(Debug, Fail)] 17 | pub enum AuthenticationError { 18 | #[fail(display = "Invalid authorization header.")] 19 | InvalidHeader, 20 | #[fail(display = "Incorrect credentials.")] 21 | IncorrectCredentials, 22 | } 23 | -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export RUSTFLAGS="-Clink-dead-code" 3 | 4 | # Check for jq, cmake 5 | if ! [ -x "$(which jq)" ]; then echo "jq not found"; exit; fi 6 | if ! [ -x "$(which cmake)" ]; then echo "cmake not found"; exit; fi 7 | 8 | # Guess CARGO_HOME if needed 9 | if [ -z "$CARGO_HOME" ] && ! [ -z "$HOME" ]; then export CARGO_HOME="$HOME/.cargo"; fi 10 | 11 | # Switch to top dir if necessary 12 | if ! [ -e Cargo.toml ]; then cd ..; fi 13 | 14 | # Remove old broken kcov build 15 | if [ -e cov/kcov-master ] && ! [ -x cov/kcov-master/build/src/kcov ]; then rm -rf cov/kcov-master; fi 16 | 17 | # New kcov build 18 | if ! [ -e cov/kcov-master ]; then 19 | mkdir -p cov && 20 | cd cov && 21 | wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz -q && 22 | tar xzf master.tar.gz && 23 | mkdir -p kcov-master/build && 24 | cd kcov-master/build && 25 | cmake .. && 26 | make && 27 | cd ../../.. && 28 | rm cov/master.tar.gz 29 | fi 30 | 31 | # Remove old results 32 | rm -rf cov/cov 33 | 34 | # Compile tests 35 | tests="$(cargo test --target-dir cov --no-run --message-format=json $* | jq -r 'if .reason == "compiler-artifact" and .profile.test then .filenames[0] else "" end' | sed '/^$/d')" 36 | 37 | # Execute tests 38 | for file in $tests; do 39 | echo Test: $file; 40 | if [ -x $file ] && [ -f $file ]; then 41 | mkdir -p "cov/cov/$(basename $file)" && 42 | cov/kcov-master/build/src/kcov --exclude-line="unreachable!" --exclude-pattern=$CARGO_HOME,/usr,$PWD/tests --verify "cov/cov/$(basename $file)" "$file" 43 | fi 44 | done 45 | 46 | # Merge results 47 | cov/kcov-master/build/src/kcov --exclude-line="unreachable!" --merge cov/cov cov/cov/* 48 | 49 | echo "Coverage:" $(grep -Po 'covered":.*?[^\\]"' cov/cov/index.js | grep "[0-9]*\.[0-9]" -o) 50 | echo "Report: file://$PWD/cov/cov/index.html" 51 | -------------------------------------------------------------------------------- /scripts/docker_config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function entry () { 4 | if [[ "$3" == "string" ]]; then 5 | echo "$1 = \"$2\"" 6 | else 7 | echo "$1 = $2" 8 | fi 9 | } 10 | 11 | function required () { 12 | local var=${!2} 13 | if [[ -z "$var" ]]; then 14 | echo "\$$2 required but not present" >&2 15 | exit 1 16 | fi 17 | entry "$1" "$var" "$3" 18 | } 19 | 20 | function optional () { 21 | local var=${!2} 22 | if [[ ! -z "$var" ]]; then 23 | entry "$1" "$var" "$3" 24 | fi 25 | } 26 | 27 | optional peer-key-file PEER_KEY_FILE string 28 | 29 | echo '[network]' 30 | required host NIMIQ_HOST string 31 | entry port 8443 number 32 | echo 'seed_nodes = [' 33 | nodes_arr=($NIMIQ_SEED_NODES) 34 | for node in "${nodes_arr[@]}"; do 35 | echo "{ uri = \"$node\" }" 36 | done 37 | echo ']' 38 | optional instant_inbound NIMIQ_INSTANT_INBOUND boolean 39 | 40 | echo '[consensus]' 41 | required network NIMIQ_NETWORK string 42 | 43 | echo '[database]' 44 | entry path "/root/database" string 45 | optional size NIMIQ_DATABASE_SIZE number 46 | optional max_dbs NIMIQ_MAX_DBS number 47 | optional no_lmdb_sync NIMIQ_NO_LMDB_SYNC boolean 48 | 49 | echo '[log]' 50 | optional level NIMIQ_LOG_LEVEL string 51 | entry timestamps false boolean 52 | entry file /root/nimiq.log.pipe string 53 | optional statistics NIMIQ_LOG_STATISTICS number 54 | optional file NIMIQ_LOG_FILE string 55 | 56 | echo '[validator]' 57 | required type NIMIQ_VALIDATOR string 58 | optional block_delay VALIDATOR_BLOCK_DELAY number 59 | optional key_file VALIDATOR_KEY_FILE string 60 | 61 | if [[ "$RPC_ENABLED" == "true" ]]; then 62 | echo '[rpc-server]' 63 | entry bind 0.0.0.0 string 64 | optional username RPC_USERNAME string 65 | optional password RPC_PASSWORD string 66 | fi 67 | 68 | if [[ "$METRICS_ENABLED" == "true" ]]; then 69 | echo '[metrics-server]' 70 | entry bind 0.0.0.0 string 71 | optional password METRICS_PASSWORD string 72 | fi 73 | 74 | if [[ "$REVERSE_PROXY_ENABLED" == "true" ]]; then 75 | echo '[reverse-proxy]' 76 | required address REVERSE_PROXY_ADDRESS string 77 | optional header REVERSE_PROXY_HEADER string 78 | optional with_tls_termination REVERSE_PROXY_TLS_TERMINATION bool 79 | fi 80 | 81 | echo '[mempool]' 82 | optional tx_fee MEMPOOL_TX_FEE number 83 | optional tx_fee_per_byte MEMPOOL_TX_FEE_PER_BYTE number 84 | optional tx_value MEMPOOL_TX_VALUE number 85 | optional tx_value_total MEMPOOL_TX_VALUE_TOTAL number 86 | optional contract_fee MEMPOOL_CONTRACT_FEE number 87 | optional contract_fee_per_byte MEMPOOL_CONTRACT_FEE_PER_BYTE number 88 | optional contract_value MEMPOOL_CONTRACT_VALUE number 89 | optional creation_fee MEMPOOL_CREATION_FEE number 90 | optional creation_fee_per_byte MEMPOOL_CREATION_FEE_PER_BYTE number 91 | optional creation_value MEMPOOL_CREATION_VALUE number 92 | optional recipient_balance MEMPOOL_RECIPIENT_BALANCE number 93 | optional sender_balance MEMPOOL_SENDER_BALANCE number 94 | -------------------------------------------------------------------------------- /scripts/docker_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | mkfifo /root/nimiq.log.pipe || true 6 | cat /root/nimiq.log.pipe & 7 | mkdir -p /root/.nimiq 8 | 9 | function hex2bin () { 10 | sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf 11 | } 12 | 13 | if [[ ! -z "$NIMIQ_PEER_KEY" ]]; then 14 | export NIMIQ_PEER_KEY_FILE=/root/.nimiq/peer_key.dat 15 | echo "$NIMIQ_PEER_KEY" | hex2bin > $NIMIQ_PEER_KEY_FILE 16 | fi 17 | 18 | if [[ ! -z "$VALIDATOR_KEY" ]]; then 19 | export VALIDATOR_KEY_FILE=/root/.nimiq/validator_key.dat 20 | echo "$VALIDATOR_KEY" | hex2bin > $VALIDATOR_KEY_FILE 21 | fi 22 | 23 | ./docker_config.sh > /root/.nimiq/client.toml 24 | 25 | /bin/nimiq-client $@ 26 | -------------------------------------------------------------------------------- /scripts/publish.py: -------------------------------------------------------------------------------- 1 | from sh import cargo, grep 2 | from pathlib import Path 3 | 4 | def publish_order(): 5 | order = [] 6 | for line in cargo.tree(no_indent=True, all=True, _cwd="client/"): 7 | parts = line.strip().split() 8 | if parts[-1] == "(*)": 9 | parts = parts[:-1] 10 | if len(parts) == 2: 11 | continue 12 | crate, version, path = parts 13 | path = path[1:-1] 14 | if path.startswith("http"): 15 | continue 16 | path = Path(path).relative_to(Path.cwd()) 17 | order.append((crate, version, path)) 18 | order.reverse() 19 | seen = set() 20 | new_order = [] 21 | for crate, version, path in order: 22 | if crate not in seen: 23 | seen.add(crate) 24 | new_order.append((crate, version, path)) 25 | return new_order 26 | 27 | 28 | def print_files(crates): 29 | for crate, version, path in crates: 30 | print("# Packaging {} {}: {}".format(crate, version, path)) 31 | for l in cargo.package(list=True, _cwd=str(path)): 32 | print(l.strip()) 33 | print() 34 | 35 | def print_paths(crates): 36 | for _, _, path in crates: 37 | print(path) 38 | 39 | 40 | crates = publish_order() 41 | #print_files(crates) 42 | print_paths(crates) -------------------------------------------------------------------------------- /utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nimiq-utils" 3 | version = "0.2.0" 4 | authors = ["The Nimiq Core Development Team "] 5 | edition = "2018" 6 | description = "Various utilities (e.g., CRC, Merkle proofs, timers) for Nimiq's Rust implementation" 7 | homepage = "https://nimiq.com" 8 | repository = "https://github.com/nimiq/core-rs" 9 | license = "Apache-2.0" 10 | categories = ["cryptography::cryptocurrencies"] 11 | keywords = ["nimiq", "cryptocurrency", "blockchain"] 12 | 13 | [badges] 14 | travis-ci = { repository = "nimiq/core-rs", branch = "master" } 15 | is-it-maintained-issue-resolution = { repository = "nimiq/core-rs" } 16 | is-it-maintained-open-issues = { repository = "nimiq/core-rs" } 17 | maintenance = { status = "experimental" } 18 | 19 | [dependencies] 20 | bit-vec = { version = "0.5", optional = true } 21 | futures = { version = "0.1", optional = true } 22 | parking_lot = { version = "0.7", optional = true } 23 | tokio = { version = "0.1", optional = true } 24 | log = { version = "0.4", optional = true } 25 | nimiq-hash = { path = "../hash", version = "0.2", optional = true } 26 | beserial = { path = "../beserial", version = "0.2", optional = true } 27 | nimiq-collections = { path = "../collections", version = "0.2", optional = true } 28 | 29 | [features] 30 | crc = [] 31 | iterators = [] 32 | locking = ["futures", "parking_lot"] 33 | merkle = ["beserial", "nimiq-hash", "bit-vec"] 34 | mutable-once = [] 35 | observer = [] 36 | time = [] 37 | timers = ["futures", "parking_lot", "tokio", "log"] 38 | unique-ptr = [] 39 | throttled-queue = ["nimiq-collections"] 40 | rate-limit = [] 41 | unique-id = [] 42 | # Compiles this package with all features. 43 | all = ["bit-vec", "crc", "iterators", "locking", "merkle", "mutable-once", "observer", "time", "timers", "unique-ptr", "throttled-queue", "rate-limit", "unique-id"] 44 | # Compiles this package with the features needed for the nimiq client. 45 | full-nimiq = ["crc", "iterators", "locking", "merkle", "mutable-once", "observer", "time", "timers", "unique-ptr"] -------------------------------------------------------------------------------- /utils/src/bit_vec.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::min; 2 | use bit_vec::{BitVec, BitBlock}; 3 | 4 | pub trait IntoChunkedBitVecIterator<'a, B: 'a = u32> { 5 | fn chunks(&'a self, chunk_size: usize) -> ChunkedBitVecIterator<'a, B>; 6 | } 7 | 8 | #[derive(Clone)] 9 | pub struct ChunkedBitVecIterator<'a, B: 'a = u32> { 10 | bit_vec: &'a BitVec, 11 | pos: usize, 12 | chunk_size: usize, 13 | } 14 | 15 | impl<'a, B: BitBlock> Iterator for ChunkedBitVecIterator<'a, B> { 16 | type Item = usize; 17 | 18 | #[inline] 19 | fn next(&mut self) -> Option { 20 | if self.pos >= self.bit_vec.len() { 21 | return None 22 | } 23 | let end_pos = min(self.pos + self.chunk_size, self.bit_vec.len()); 24 | let ret = (self.pos..end_pos).fold(0, |acc, pos| (acc << 1) | (self.bit_vec.get(pos).unwrap() as usize) as usize); 25 | self.pos += self.chunk_size; 26 | Some(ret) 27 | } 28 | 29 | fn size_hint(&self) -> (usize, Option) { 30 | let exact_fit = (self.bit_vec.len() % self.chunk_size) == 0; 31 | let size = (self.bit_vec.len() / self.chunk_size) + (if exact_fit { 0 } else { 1 }); 32 | (size, Some(size)) 33 | } 34 | } 35 | 36 | impl<'a, B: BitBlock> ExactSizeIterator for ChunkedBitVecIterator<'a, B> {} 37 | 38 | impl<'a, B: 'a> IntoChunkedBitVecIterator<'a, B> for BitVec { 39 | fn chunks(&'a self, chunk_size: usize) -> ChunkedBitVecIterator<'a, B> { 40 | ChunkedBitVecIterator { 41 | bit_vec: &self, 42 | pos: 0, 43 | chunk_size, 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "log")] 2 | #[macro_use] 3 | extern crate log; 4 | 5 | #[cfg(feature = "crc")] 6 | pub mod crc; 7 | #[cfg(feature = "merkle")] 8 | pub mod merkle; 9 | #[cfg(feature = "locking")] 10 | pub mod locking; 11 | #[cfg(feature = "bit-vec")] 12 | pub mod bit_vec; 13 | #[cfg(feature = "observer")] 14 | pub mod observer; 15 | #[cfg(feature = "timers")] 16 | pub mod timers; 17 | #[cfg(feature = "unique-ptr")] 18 | pub mod unique_ptr; 19 | #[cfg(feature = "iterators")] 20 | pub mod iterators; 21 | #[cfg(feature = "mutable-once")] 22 | pub mod mutable_once; 23 | #[cfg(feature = "time")] 24 | pub mod time; 25 | #[cfg(feature = "throttled-queue")] 26 | pub mod throttled_queue; 27 | #[cfg(feature = "rate-limit")] 28 | pub mod rate_limit; 29 | #[cfg(feature = "unique-id")] 30 | pub mod unique_id; -------------------------------------------------------------------------------- /utils/src/mutable_once.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | use std::ops::Deref; 3 | use std::mem; 4 | 5 | pub struct MutableOnce { 6 | inner: UnsafeCell, 7 | } 8 | 9 | impl MutableOnce { 10 | /// Initializes object with a default value. 11 | pub fn new(default: T) -> Self { 12 | MutableOnce { 13 | inner: UnsafeCell::new(default), 14 | } 15 | } 16 | 17 | /// This mutates the content of the cell 18 | /// and should only be called once, before any other reference can exist. 19 | /// This condition is not checked, thus this call is unsafe. 20 | pub unsafe fn replace(&self, value: T) { 21 | mem::replace(&mut *self.inner.get(), value); 22 | } 23 | 24 | fn get(&self) -> &T { 25 | unsafe { &*self.inner.get() } 26 | } 27 | } 28 | 29 | impl Deref for MutableOnce { 30 | type Target = T; 31 | 32 | fn deref(&self) -> &T { 33 | self.get() 34 | } 35 | } 36 | 37 | impl AsRef for MutableOnce { 38 | fn as_ref(&self) -> &T { 39 | self.get() 40 | } 41 | } 42 | 43 | unsafe impl Send for MutableOnce {} 44 | unsafe impl Sync for MutableOnce {} 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::*; 49 | 50 | #[test] 51 | fn mutable_once_works() { 52 | let v = MutableOnce::new(1); 53 | assert_eq!(*v.as_ref(), 1); 54 | 55 | let v = MutableOnce::new(1); 56 | unsafe { v.replace(2) }; 57 | assert_eq!(*v.as_ref(), 2); 58 | } 59 | } -------------------------------------------------------------------------------- /utils/src/observer.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{Weak, Arc}; 2 | 3 | pub trait Listener: Send + Sync { 4 | fn on_event(&self, event: &E); 5 | } 6 | 7 | impl Listener for F 8 | where F: Send + Sync { 9 | fn on_event(&self, event: &E) { 10 | self(event); 11 | } 12 | } 13 | 14 | pub type ListenerHandle = usize; 15 | 16 | #[derive(Default)] 17 | pub struct Notifier<'l, E> { 18 | listeners: Vec<(ListenerHandle, Box + 'l>)>, 19 | next_handle: ListenerHandle 20 | } 21 | 22 | impl<'l, E> Notifier<'l, E> { 23 | pub fn new() -> Self { 24 | Self { 25 | listeners: Vec::new(), 26 | next_handle: 0, 27 | } 28 | } 29 | 30 | pub fn register + 'l>(&mut self, listener: T) -> ListenerHandle { 31 | let handle = self.next_handle; 32 | self.listeners.push((handle, Box::new(listener))); 33 | self.next_handle += 1; 34 | handle 35 | } 36 | 37 | pub fn deregister(&mut self, handle: ListenerHandle) { 38 | for (i, (stored_handle, _)) in self.listeners.iter().enumerate() { 39 | if handle == *stored_handle { 40 | self.listeners.remove(i); 41 | return; 42 | } 43 | } 44 | } 45 | 46 | pub fn notify(&self, event: E) { 47 | for (_, listener) in &self.listeners { 48 | listener.on_event(&event); 49 | } 50 | } 51 | } 52 | 53 | 54 | pub fn weak_listener(weak_ref: Weak, closure: C) -> impl Listener 55 | where C: Fn(Arc, &E) + Send + Sync, T: Send + Sync { 56 | move |event: &E| { 57 | if let Some(arc) = weak_ref.upgrade() { 58 | closure(arc, event); 59 | } 60 | } 61 | } 62 | 63 | pub fn weak_passthru_listener(weak_ref: Weak, closure: C) -> impl PassThroughListener 64 | where C: Fn(Arc, E) + Send + Sync, T: Send + Sync { 65 | move |event: E| { 66 | if let Some(arc) = weak_ref.upgrade() { 67 | closure(arc, event); 68 | } 69 | } 70 | } 71 | 72 | pub trait PassThroughListener: Send + Sync { 73 | fn on_event(&self, event: E); 74 | } 75 | 76 | impl PassThroughListener for F 77 | where F: Send + Sync { 78 | fn on_event(&self, event: E) { 79 | self(event); 80 | } 81 | } 82 | 83 | #[derive(Default)] 84 | pub struct PassThroughNotifier<'l, E> { 85 | listener: Option + 'l>>, 86 | } 87 | 88 | impl<'l, E> PassThroughNotifier<'l, E> { 89 | pub fn new() -> Self { 90 | Self { 91 | listener: None, 92 | } 93 | } 94 | 95 | pub fn register + 'l>(&mut self, listener: T) { 96 | self.listener = Some(Box::new(listener)); 97 | } 98 | 99 | pub fn deregister(&mut self) { 100 | self.listener = None; 101 | } 102 | 103 | pub fn notify(&self, event: E) { 104 | if let Some(ref listener) = self.listener { 105 | listener.on_event(event); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /utils/src/rate_limit.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use std::time::Instant; 3 | 4 | /// A `RateLimit` can be used to limit the number of occurrences 5 | /// of certain actions within a time period. 6 | pub struct RateLimit { 7 | allowed_occurrences: usize, 8 | time_period: Duration, 9 | last_reset: Instant, 10 | counter: usize, 11 | } 12 | 13 | impl RateLimit { 14 | const ONE_MINUTE: Duration = Duration::from_secs(60); 15 | 16 | /// Creates a `RateLimit`. 17 | /// 18 | /// * `allowed_occurrences` - The limit on occurrences of an action within the defined `time_period`. 19 | /// * `time_period` - The interval at which the number of occurrences will be reset. 20 | #[inline] 21 | pub fn new(allowed_occurrences: usize, time_period: Duration) -> Self { 22 | RateLimit { 23 | allowed_occurrences, 24 | time_period, 25 | last_reset: Instant::now(), 26 | counter: 0, 27 | } 28 | } 29 | 30 | /// Creates a `RateLimit` with a `time_period` of one minute. 31 | /// 32 | /// * `allowed_occurrences` - The limit on occurrences of an action within the defined `time_period`. 33 | #[inline] 34 | pub fn new_per_minute(allowed_occurrences: usize) -> Self { 35 | Self::new(allowed_occurrences, Self::ONE_MINUTE) 36 | } 37 | 38 | /// Internally reset counter if necessary. 39 | #[inline] 40 | fn check_reset(&mut self) { 41 | let now = Instant::now(); 42 | if now.duration_since(self.last_reset) > self.time_period { 43 | self.last_reset = now; 44 | self.counter = 0; 45 | } 46 | } 47 | 48 | /// Determine whether a single action is still within the current rate limit. 49 | #[inline] 50 | pub fn note_single(&mut self) -> bool { 51 | self.note(1) 52 | } 53 | 54 | /// Determine whether `number` actions are still within the current rate limit. 55 | pub fn note(&mut self, number: usize) -> bool { 56 | self.check_reset(); 57 | self.counter += number; 58 | self.counter <= self.allowed_occurrences 59 | } 60 | 61 | /// Determine how many actions are still within the current rate limit. 62 | pub fn num_allowed(&mut self) -> usize { 63 | self.check_reset(); 64 | self.allowed_occurrences.saturating_sub(self.counter) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /utils/src/time.rs: -------------------------------------------------------------------------------- 1 | use std::time::{SystemTime, UNIX_EPOCH}; 2 | use std::time::Duration; 3 | 4 | pub fn systemtime_to_timestamp(time: SystemTime) -> u64 { 5 | match time.duration_since(UNIX_EPOCH) { 6 | Ok(duration) => duration.as_secs() * 1000 + u64::from(duration.subsec_nanos()) / 1_000_000, 7 | Err(e) => panic!("SystemTime before UNIX EPOCH! Difference: {:?}", e.duration()), 8 | } 9 | } 10 | 11 | pub fn timestamp_to_systemtime(timestamp: u64) -> SystemTime { 12 | UNIX_EPOCH + Duration::from_millis(timestamp) 13 | } 14 | -------------------------------------------------------------------------------- /utils/src/unique_id.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::atomic::{AtomicUsize, Ordering}; 3 | 4 | #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Debug, Hash)] 5 | pub struct UniqueId(usize); 6 | 7 | static GLOBAL_COUNTER: AtomicUsize = AtomicUsize::new(0); 8 | 9 | #[allow(clippy::new_without_default)] 10 | impl UniqueId { 11 | pub fn new() -> Self { 12 | UniqueId(GLOBAL_COUNTER.fetch_add(1, Ordering::Release)) 13 | } 14 | } 15 | 16 | impl fmt::Display for UniqueId { 17 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 18 | write!(f, "{}", self.0) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /utils/src/unique_ptr.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Deref; 2 | use std::cmp::Ordering; 3 | 4 | #[derive(Debug)] 5 | pub struct UniquePtr(*const T); 6 | 7 | impl UniquePtr { 8 | pub fn new(v: &T) -> Self { 9 | UniquePtr(v as *const T) 10 | } 11 | } 12 | 13 | impl Deref for UniquePtr { 14 | type Target = T; 15 | 16 | fn deref(&self) -> &::Target { 17 | unsafe { &*self.0 } 18 | } 19 | } 20 | 21 | impl PartialEq for UniquePtr { 22 | fn eq(&self, other: &Self) -> bool { 23 | Deref::deref(self).eq(other) 24 | } 25 | } 26 | 27 | impl PartialOrd for UniquePtr { 28 | fn partial_cmp(&self, other: &Self) -> Option { 29 | Deref::deref(self).partial_cmp(other) 30 | } 31 | } 32 | 33 | impl Ord for UniquePtr { 34 | fn cmp(&self, other: &Self) -> Ordering { 35 | Deref::deref(self).cmp(other) 36 | } 37 | } 38 | 39 | impl Eq for UniquePtr {} 40 | 41 | impl AsRef for UniquePtr { 42 | fn as_ref(&self) -> &T { 43 | self.deref() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /utils/tests/crc/mod.rs: -------------------------------------------------------------------------------- 1 | use nimiq_utils::crc::*; 2 | 3 | #[test] 4 | fn compute_crc32() { 5 | let mut crc32_comp = Crc32Computer::default(); 6 | crc32_comp.update(&[0u8]); 7 | assert!(u32::from_str_radix("d202ef8d", 16).unwrap() == crc32_comp.result()); 8 | 9 | crc32_comp = Crc32Computer::default(); 10 | let a: [u8; 12] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 11 | crc32_comp.update(&a); 12 | assert!(u32::from_str_radix("9270c965", 16).unwrap() == crc32_comp.result()); 13 | 14 | crc32_comp = Crc32Computer::default(); 15 | let a: [u8; 43] = [84, 104, 101, 32, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110, 32, 102, 111, 120, 16 | 32, 106, 117, 109, 112, 115, 32, 111, 118, 101, 114, 32, 116, 104, 101, 32, 108, 97, 122, 121, 32, 100, 111, 103]; 17 | crc32_comp.update(&a); 18 | assert!(u32::from_str_radix("414fa339", 16).unwrap() == crc32_comp.result()); 19 | } 20 | 21 | #[test] 22 | fn compute_crc8() { 23 | let mut crc8_comp = Crc8Computer::default(); 24 | crc8_comp.update(&[0u8]); 25 | assert_eq!(crc8_comp.result(), 0); 26 | 27 | crc8_comp = Crc8Computer::default(); 28 | let a: [u8; 12] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; 29 | crc8_comp.update(&a); 30 | assert_eq!(crc8_comp.result(), 255); 31 | 32 | crc8_comp = Crc8Computer::default(); 33 | let a: [u8; 32] = [ 169, 203, 76, 129, 160, 230, 129, 141, 117, 240, 195, 239, 197, 18, 196, 30, 26, 52, 253, 1, 21, 81, 249, 22, 234, 115, 246, 14, 62, 197, 228, 223 ]; 34 | crc8_comp.update(&a); 35 | assert_eq!(crc8_comp.result(), 165); 36 | } 37 | -------------------------------------------------------------------------------- /utils/tests/iterators/mod.rs: -------------------------------------------------------------------------------- 1 | use nimiq_utils::iterators::*; 2 | 3 | #[test] 4 | fn it_can_iterate_over_two_iterators() { 5 | let a = vec![1, 3, 5, 7, 9]; 6 | let b = vec![2, 4, 6, 8, 10]; 7 | 8 | let combined: Vec = Alternate::new(a.iter(), b.iter()).map(|&i| i).collect(); 9 | assert_eq!(combined, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "alternate iterator did not iterate correctly"); 10 | } 11 | 12 | #[test] 13 | fn it_can_determine_the_correct_size() { 14 | let a = vec![1, 3, 5, 7, 9]; 15 | let b = vec![2, 4, 6, 8, 10]; 16 | 17 | let it = Alternate::new(a.iter(), b.iter()); 18 | assert_eq!(it.size_hint(), (10, Some(10)), "alternate iterator did not determine the correct size hint"); 19 | } 20 | 21 | #[test] 22 | fn it_can_determine_the_correct_count() { 23 | let a = vec![1, 3, 5, 7, 9]; 24 | let b = vec![2, 4, 6, 8, 10]; 25 | 26 | let it = Alternate::new(a.iter(), b.iter()); 27 | assert_eq!(it.count(), 10, "alternate iterator did not determine the correct count"); 28 | 29 | let it = Alternate::new(a.iter(), b.iter()); 30 | assert_eq!(it.skip(1).count(), 9, "alternate iterator did not determine the correct count after skipping one item"); 31 | let it = Alternate::new(a.iter(), b.iter()); 32 | assert_eq!(it.skip(2).count(), 8, "alternate iterator did not determine the correct count after skipping two items"); 33 | } 34 | -------------------------------------------------------------------------------- /utils/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "crc")] 2 | pub mod crc; 3 | #[cfg(feature = "merkle")] 4 | pub mod merkle; 5 | #[cfg(feature = "observer")] 6 | pub mod observer; 7 | #[cfg(feature = "iterators")] 8 | pub mod iterators; 9 | #[cfg(feature = "throttled-queue")] 10 | pub mod throttled_queue; 11 | #[cfg(feature = "rate-limit")] 12 | pub mod rate_limit; 13 | #[cfg(feature = "unique-id")] 14 | pub mod unique_id; -------------------------------------------------------------------------------- /utils/tests/rate_limit/mod.rs: -------------------------------------------------------------------------------- 1 | use std::thread::sleep; 2 | use std::time::Duration; 3 | 4 | use nimiq_utils::rate_limit::*; 5 | 6 | #[test] 7 | fn it_limits_access() { 8 | let mut limit = RateLimit::new_per_minute(3); 9 | 10 | assert_eq!(limit.num_allowed(), 3); 11 | assert!(limit.note_single()); 12 | assert_eq!(limit.num_allowed(), 2); 13 | assert!(limit.note(2)); 14 | assert_eq!(limit.num_allowed(), 0); 15 | assert!(!limit.note(1)); 16 | assert_eq!(limit.num_allowed(), 0); 17 | } 18 | 19 | #[test] 20 | fn it_frees_limit_after_time() { 21 | let time_period = Duration::from_millis(100); 22 | let mut limit = RateLimit::new(1, time_period); 23 | 24 | assert_eq!(limit.num_allowed(), 1); 25 | assert!(limit.note(1)); 26 | assert_eq!(limit.num_allowed(), 0); 27 | assert!(!limit.note_single()); 28 | 29 | sleep(time_period); 30 | 31 | assert_eq!(limit.num_allowed(), 1); 32 | assert!(limit.note(1)); 33 | } 34 | -------------------------------------------------------------------------------- /utils/tests/throttled_queue/mod.rs: -------------------------------------------------------------------------------- 1 | use std::thread::sleep; 2 | use std::time::Duration; 3 | 4 | use nimiq_collections::queue::Queue; 5 | use nimiq_utils::throttled_queue::*; 6 | 7 | #[test] 8 | fn it_can_enqueue_dequeue() { 9 | let mut queue = ThrottledQueue::new(1000, Duration::default(), 0, None); 10 | 11 | queue.enqueue(1); 12 | queue.enqueue(2); 13 | queue.enqueue(8); 14 | queue.remove(&8); 15 | queue.enqueue(3); 16 | queue.enqueue(4); 17 | 18 | assert_eq!(queue.len(), 4); 19 | assert_eq!(queue.dequeue(), Some(1)); 20 | assert_eq!(queue.dequeue_multi(2), vec![2, 3]); 21 | assert!(queue.check_available()); 22 | assert_eq!(queue.num_available(), 1); 23 | } 24 | 25 | #[test] 26 | fn it_can_throttle() { 27 | let interval = Duration::new(0, 50000000); 28 | let mut queue = ThrottledQueue::new(2, interval, 1, Some(10)); 29 | 30 | queue.enqueue(1); 31 | queue.enqueue(2); 32 | queue.enqueue(3); 33 | queue.enqueue(4); 34 | queue.enqueue(5); 35 | queue.enqueue(6); 36 | 37 | // TODO: This test is dependent on timing! 38 | assert_eq!(queue.dequeue_multi(3), vec![1, 2]); 39 | sleep(interval); 40 | assert_eq!(queue.dequeue_multi(2), vec![3]); 41 | sleep(3 * interval); 42 | assert_eq!(queue.num_available(), 2); 43 | } 44 | -------------------------------------------------------------------------------- /utils/tests/unique_id/mod.rs: -------------------------------------------------------------------------------- 1 | use nimiq_utils::unique_id::UniqueId; 2 | 3 | #[test] 4 | fn it_can_generate_unique_ids() { 5 | let id1 = UniqueId::new(); 6 | let id2 = UniqueId::new(); 7 | assert_ne!(id1, id2); 8 | 9 | let id1_copy = id1; 10 | let id2_copy = id2; 11 | assert_eq!(id1, id1_copy); 12 | assert_eq!(id2, id2_copy); 13 | assert_ne!(id1_copy, id2_copy); 14 | } --------------------------------------------------------------------------------