├── .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