├── .github └── workflows │ ├── auto_merge_prs.yml │ ├── bump_version.yml │ ├── commitlint.yml │ ├── github_release.yml │ ├── master.yml │ ├── pr.yml │ ├── security_audit.yml │ └── tag_release.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-BSD ├── LICENSE-MIT ├── README.md ├── codeowners ├── rustfmt.toml └── src ├── lib.rs └── main.rs /.github/workflows/auto_merge_prs.yml: -------------------------------------------------------------------------------- 1 | # auto merge workflow. 2 | # 3 | # Auto merge PR if commit msg begins with `chore(release):`, 4 | # or if it has been raised by Dependabot. 5 | # Uses https://github.com/ridedott/merge-me-action. 6 | 7 | name: Merge Version Change and Dependabot PRs automatically 8 | 9 | on: pull_request 10 | 11 | jobs: 12 | merge: 13 | runs-on: ubuntu-20.04 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | fetch-depth: '0' 18 | 19 | - name: get commit message 20 | run: | 21 | commitmsg=$(git log --format=%s -n 1 ${{ github.event.pull_request.head.sha }}) 22 | echo "commitmsg=${commitmsg}" >> $GITHUB_ENV 23 | 24 | - name: show commit message 25 | run : echo $commitmsg 26 | 27 | - name: Merge Version change PR 28 | if: startsWith( env.commitmsg, 'chore(release):') 29 | uses: ridedott/merge-me-action@81667e6ae186ddbe6d3c3186d27d91afa7475e2c 30 | with: 31 | GITHUB_LOGIN: dirvine 32 | GITHUB_TOKEN: ${{ secrets.MERGE_BUMP_BRANCH_TOKEN }} 33 | MERGE_METHOD: REBASE 34 | 35 | - name: Dependabot Merge 36 | uses: ridedott/merge-me-action@master 37 | with: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | MERGE_METHOD: REBASE 40 | -------------------------------------------------------------------------------- /.github/workflows/bump_version.yml: -------------------------------------------------------------------------------- 1 | name: Version bump and create PR for changes 2 | 3 | on: 4 | # Trigger the workflow on push only for the master branch 5 | push: 6 | branches: 7 | - master 8 | 9 | env: 10 | NODE_ENV: 'development' 11 | 12 | jobs: 13 | update_changelog: 14 | runs-on: ubuntu-20.04 15 | # Dont run if we're on a release commit 16 | if: "!startsWith(github.event.head_commit.message, 'chore(release):')" 17 | steps: 18 | - uses: actions/checkout@v2 19 | with: 20 | fetch-depth: '0' 21 | - name: Bump Version 22 | uses: maidsafe/rust-version-bump-branch-creator@v2 23 | with: 24 | token: ${{ secrets.BRANCH_CREATOR_TOKEN }} 25 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yml: -------------------------------------------------------------------------------- 1 | name: Commitlint 2 | on: [pull_request] 3 | 4 | jobs: 5 | lint: 6 | runs-on: ubuntu-latest 7 | env: 8 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | fetch-depth: 0 13 | - uses: wagoid/commitlint-github-action@f114310111fdbd07e99f47f9ca13d62b3ec98372 14 | -------------------------------------------------------------------------------- /.github/workflows/github_release.yml: -------------------------------------------------------------------------------- 1 | name: Create GitHub Release 2 | 3 | 4 | on: 5 | push: 6 | tags: 7 | - 'v*' 8 | 9 | jobs: 10 | release: 11 | # only if we have a tag 12 | name: Release 13 | runs-on: ubuntu-20.04 14 | if: "startsWith(github.event.head_commit.message, 'chore(release):')" 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | fetch-depth: '0' 20 | 21 | - name: Set tag as env 22 | shell: bash 23 | run: echo "RELEASE_VERSION=$(echo ${GITHUB_REF:10})" >> $GITHUB_ENV 24 | 25 | - name: lets check tag 26 | shell: bash 27 | run: echo ${{ env.RELEASE_VERSION }} 28 | 29 | - name: Generate Changelog 30 | shell: bash 31 | run: awk '/# \[/{c++;p=1}{if(c==2){exit}}p;' CHANGELOG.md > RELEASE-CHANGELOG.txt 32 | - run: cat RELEASE-CHANGELOG.txt 33 | - name: Release generation 34 | uses: softprops/action-gh-release@91409e712cf565ce9eff10c87a8d1b11b81757ae 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.MERGE_BUMP_BRANCH_TOKEN }} 37 | with: 38 | body_path: RELEASE-CHANGELOG.txt 39 | -------------------------------------------------------------------------------- /.github/workflows/master.yml: -------------------------------------------------------------------------------- 1 | # Push to master workflow. 2 | # 3 | # Runs when a PR has been merged to the master branch. 4 | # 5 | # 1. Generates a release build. 6 | # 2. If the last commit is a version change, publish. 7 | 8 | name: Build and Publish (on tag) 9 | 10 | on: 11 | push: 12 | branches: 13 | - master 14 | 15 | env: 16 | # Run all cargo commands with --verbose. 17 | CARGO_TERM_VERBOSE: true 18 | RUST_BACKTRACE: 1 19 | 20 | jobs: 21 | build: 22 | name: Build 23 | runs-on: ${{ matrix.os }} 24 | if: "startsWith(github.event.head_commit.message, 'chore(release):')" 25 | 26 | strategy: 27 | matrix: 28 | os: [ubuntu-latest, windows-latest, macOS-latest] 29 | steps: 30 | - uses: actions/checkout@v2 31 | # Install Rust 32 | - uses: actions-rs/toolchain@v1 33 | with: 34 | profile: minimal 35 | toolchain: stable 36 | override: true 37 | 38 | # Cache. 39 | - name: Cargo cache registry, index and build 40 | uses: actions/cache@v2.1.4 41 | with: 42 | path: | 43 | ~/.cargo/registry 44 | ~/.cargo/git 45 | target 46 | key: ${{ runner.os }}-cargo-cache-${{ hashFiles('**/Cargo.lock') }} 47 | 48 | # Make sure the code builds. 49 | - name: Build 50 | run: cargo build --release 51 | 52 | # Publish if we're on a tag here. 53 | publish: 54 | name: Publish 55 | runs-on: ubuntu-latest 56 | needs: build 57 | if: "startsWith(github.event.head_commit.message, 'chore(release):')" 58 | steps: 59 | - uses: actions/checkout@v2 60 | # checkout with fetch-depth: '0' to be sure to retrieve all commits to look for the semver commit message 61 | with: 62 | fetch-depth: '0' 63 | 64 | ## Install Rust 65 | - uses: actions-rs/toolchain@v1 66 | with: 67 | profile: minimal 68 | toolchain: stable 69 | override: true 70 | 71 | # Publish to crates.io. 72 | - uses: actions-rs/cargo@v1 73 | with: 74 | command: login 75 | args: ${{ secrets.CRATES_IO_TOKEN }} 76 | - uses: actions-rs/cargo@v1 77 | with: 78 | command: package 79 | - uses: actions-rs/cargo@v1 80 | with: 81 | command: publish 82 | -------------------------------------------------------------------------------- /.github/workflows/pr.yml: -------------------------------------------------------------------------------- 1 | # PR workflow. 2 | # 3 | # Runs full suite of checks, with warnings treated as errors. 4 | # Gather code coverage stats and publish them on coveralls.io. 5 | 6 | name: PR 7 | 8 | on: pull_request 9 | 10 | env: 11 | # Run all cargo commands with --verbose. 12 | CARGO_TERM_VERBOSE: true 13 | RUST_BACKTRACE: 1 14 | # Deny all compiler warnings. 15 | RUSTFLAGS: "-D warnings" 16 | 17 | jobs: 18 | checks: 19 | if: "!startsWith(github.event.pull_request.title, 'Automated version bump')" 20 | name: Clippy & fmt 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v2 24 | # Install Rust and required components 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: stable 29 | override: true 30 | components: rustfmt, clippy 31 | 32 | # Cache. 33 | - name: Cargo cache registry, index and build 34 | uses: actions/cache@v2.1.4 35 | with: 36 | path: | 37 | ~/.cargo/registry 38 | ~/.cargo/git 39 | target 40 | key: ${{ runner.os }}-cargo-cache-${{ hashFiles('**/Cargo.lock') }} 41 | 42 | # Check if the code is formatted correctly. 43 | - name: Check formatting 44 | run: cargo fmt --all -- --check 45 | 46 | # Run Clippy. 47 | - name: Clippy checks 48 | run: cargo clippy --all-targets 49 | 50 | check_pr_size: 51 | if: "!startsWith(github.event.pull_request.title, 'Automated version bump')" 52 | name: Check PR size doesn't break set limit 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v2 56 | with: 57 | fetch-depth: '0' 58 | - uses: maidsafe/pr_size_checker@v2 59 | with: 60 | max_lines_changed: 200 61 | 62 | coverage: 63 | if: "!startsWith(github.event.pull_request.title, 'Automated version bump')" 64 | name: Code coverage check 65 | runs-on: ubuntu-latest 66 | env: 67 | PROPTEST_CASES: 1 68 | steps: 69 | - uses: actions/checkout@v2 70 | # Install Rust 71 | - uses: actions-rs/toolchain@v1 72 | with: 73 | profile: minimal 74 | toolchain: stable 75 | override: true 76 | 77 | # Cache. 78 | - name: Cargo cache registry, index and build 79 | uses: actions/cache@v2.1.4 80 | with: 81 | path: | 82 | ~/.cargo/registry 83 | ~/.cargo/git 84 | target 85 | key: ${{ runner.os }}-cargo-cache-${{ hashFiles('**/Cargo.lock') }} 86 | 87 | # Run cargo tarpaulin & push result to coveralls.io 88 | - name: rust-tarpaulin code coverage check 89 | uses: actions-rs/tarpaulin@v0.1 90 | with: 91 | args: '-v -t 300 --release --out Lcov' 92 | - name: Push code coverage results to coveralls.io 93 | uses: coverallsapp/github-action@master 94 | with: 95 | github-token: ${{ secrets.GITHUB_TOKEN }} 96 | parallel: true 97 | path-to-lcov: ./lcov.info 98 | - name: Coveralls Finished 99 | uses: coverallsapp/github-action@master 100 | with: 101 | github-token: ${{ secrets.GITHUB_TOKEN }} 102 | parallel-finished: true 103 | 104 | cargo-udeps: 105 | if: "!startsWith(github.event.pull_request.title, 'Automated version bump')" 106 | name: Unused dependency check 107 | runs-on: ubuntu-latest 108 | steps: 109 | - uses: actions/checkout@v2 110 | # Install Rust and required components 111 | - uses: actions-rs/toolchain@v1 112 | with: 113 | toolchain: nightly 114 | override: true 115 | 116 | - name: Run cargo-udeps 117 | uses: aig787/cargo-udeps-action@v1 118 | with: 119 | version: 'latest' 120 | args: '--all-targets' 121 | 122 | cargo-deny: 123 | if: "!startsWith(github.event.pull_request.title, 'Automated version bump')" 124 | runs-on: ubuntu-latest 125 | steps: 126 | - uses: actions/checkout@v2 127 | 128 | # wget the shared deny.toml file from the QA repo 129 | - shell: bash 130 | run: wget https://raw.githubusercontent.com/maidsafe/QA/master/misc-scripts/deny.toml 131 | 132 | - uses: EmbarkStudios/cargo-deny-action@v1 133 | 134 | test: 135 | name: Test 136 | runs-on: ${{ matrix.os }} 137 | strategy: 138 | matrix: 139 | os: [ubuntu-latest, windows-latest, macOS-latest] 140 | steps: 141 | - uses: actions/checkout@v2 142 | # Install Rust 143 | - uses: actions-rs/toolchain@v1 144 | with: 145 | profile: minimal 146 | toolchain: stable 147 | override: true 148 | 149 | # Cache. 150 | - name: Cargo cache registry, index and build 151 | uses: actions/cache@v2.1.4 152 | with: 153 | path: | 154 | ~/.cargo/registry 155 | ~/.cargo/git 156 | target 157 | key: ${{ runner.os }}-cargo-cache-${{ hashFiles('**/Cargo.lock') }} 158 | 159 | # Run tests. 160 | - name: Cargo test 161 | run: cargo test --release 162 | 163 | # Test publish using --dry-run. 164 | test-publish: 165 | if: "!startsWith(github.event.pull_request.title, 'Automated version bump')" 166 | name: Test Publish 167 | runs-on: ubuntu-latest 168 | steps: 169 | - uses: actions/checkout@v2 170 | # Install Rust 171 | - uses: actions-rs/toolchain@v1 172 | with: 173 | profile: minimal 174 | toolchain: stable 175 | override: true 176 | 177 | - name: Cargo publish dry run 178 | run: cargo publish --dry-run 179 | -------------------------------------------------------------------------------- /.github/workflows/security_audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | jobs: 6 | audit: 7 | if: github.repository_owner == 'maidsafe' 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-rs/audit-check@v1 12 | with: 13 | token: ${{ secrets.GITHUB_TOKEN }} 14 | -------------------------------------------------------------------------------- /.github/workflows/tag_release.yml: -------------------------------------------------------------------------------- 1 | name: Tag release commit 2 | 3 | on: 4 | # Trigger the workflow on push only for the master branch 5 | push: 6 | branches: 7 | - master 8 | 9 | env: 10 | NODE_ENV: 'development' 11 | GITHUB_TOKEN: ${{ secrets.BRANCH_CREATOR_TOKEN }} 12 | 13 | jobs: 14 | tag: 15 | runs-on: ubuntu-latest 16 | # Only run on a release commit 17 | if: "startsWith(github.event.head_commit.message, 'chore(release):')" 18 | steps: 19 | - uses: actions/checkout@v2 20 | with: 21 | fetch-depth: '0' 22 | token: ${{ secrets.BRANCH_CREATOR_TOKEN }} 23 | - run: echo "RELEASE_VERSION=$(git log -1 --pretty=%s)" >> $GITHUB_ENV 24 | # parse out non-tag text 25 | - run: echo "RELEASE_VERSION=$( echo $RELEASE_VERSION | sed 's/chore(release)://' )" >> $GITHUB_ENV 26 | # remove spaces, but add back in `v` to tag, which is needed for standard-version 27 | - run: echo "RELEASE_VERSION=v$(echo $RELEASE_VERSION | tr -d '[:space:]')" >> $GITHUB_ENV 28 | - run: echo $RELEASE_VERSION 29 | - run: git tag $RELEASE_VERSION 30 | 31 | - name: Setup git for push 32 | run: | 33 | git remote add github "$REPO" 34 | git config --local user.email "action@github.com" 35 | git config --local user.name "GitHub Action" 36 | - name: Push tags to master 37 | run: git push "https://$GITHUB_ACTOR:$GITHUB_TOKEN@github.com/$GITHUB_REPOSITORY" HEAD:master --tags 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### [1.0.39](https://github.com/maidsafe/resource_proof/compare/v1.0.38...v1.0.39) (2022-07-06) 6 | 7 | ### [1.0.38](https://github.com/maidsafe/resource_proof/compare/v1.0.37...v1.0.38) (2021-06-09) 8 | 9 | ### [1.0.37](https://github.com/maidsafe/resource_proof/compare/v1.0.36...v1.0.37) (2021-02-25) 10 | 11 | ### [1.0.36](https://github.com/maidsafe/resource_proof/compare/v1.0.35...v1.0.36) (2021-02-11) 12 | 13 | ### [1.0.35](https://github.com/maidsafe/resource_proof/compare/v1.0.34...v1.0.35) (2021-02-10) 14 | 15 | ### [1.0.34](https://github.com/maidsafe/resource_proof/compare/v1.0.33...v1.0.34) (2021-02-03) 16 | 17 | ### [1.0.33](https://github.com/maidsafe/resource_proof/compare/v1.0.32...v1.0.33) (2021-01-20) 18 | 19 | ### [1.0.32](https://github.com/maidsafe/resource_proof/compare/v1.0.31...v1.0.32) (2021-01-19) 20 | 21 | ### [1.0.31](https://github.com/maidsafe/resource_proof/compare/v0.8.0...v1.0.31) (2021-01-19) 22 | 23 | ### [0.8.0](https://github.com/maidsafe/resource_proof/compare/0.7.0...v0.8.0) (2019-09-18) 24 | - Update tiny-keccak dependency 25 | - Use rust edition 2018 26 | 27 | ### [0.7.0](https://github.com/maidsafe/resource_proof/compare/0.6.0...0.7.0) (2019-05-29) 28 | - Update clap dependency to the latest version 29 | 30 | ### [0.6.0](https://github.com/maidsafe/resource_proof/compare/0.5.0...0.6.0) (2018-01-06) 31 | - Use rust 1.22.1 stable / 2017-12-02 nightly 32 | - rustfmt 0.9.0 and clippy-0.0.175 33 | 34 | ### [0.5.0](https://github.com/maidsafe/resource_proof/compare/0.4.0...0.5.0) (2017-07-25) 35 | - Use rust 1.19 stable / 2017-07-20 nightly 36 | - rustfmt 0.9.0 and clippy-0.0.144 37 | - Replace -Zno-trans with cargo check 38 | - Make appveyor script using fixed version of stable 39 | 40 | ### [0.4.0] (2017-04-05) 41 | - Allow trying one step at a time and getting the expected number. 42 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ "MaidSafe Developers " ] 3 | description = "A 'proof' of bandwidth, cpu and storage for nodes in a decentralised network." 4 | documentation = "https://docs.rs/resource_proof" 5 | homepage = "https://maidsafe.net" 6 | license = "MIT OR BSD-3-Clause" 7 | name = "resource_proof" 8 | readme = "README.md" 9 | repository = "https://github.com/maidsafe/resource_proof" 10 | version = "1.0.39" 11 | edition = "2021" 12 | 13 | [dependencies.clap] 14 | version = "3.0" 15 | features = [ "derive" ] 16 | 17 | [dependencies.tiny-keccak] 18 | version = "2.0.2" 19 | features = [ "sha3" ] 20 | 21 | [target."cfg(unix)".dependencies] 22 | termion = "~1.5.1" 23 | -------------------------------------------------------------------------------- /LICENSE-BSD: -------------------------------------------------------------------------------- 1 | Copyright 2018 MaidSafe.net limited. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright 2018 MaidSafe.net limited. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # resource_proof 2 | 3 | |Crate|Documentation|CI| 4 | |:---:|:-----------:|:--------:| 5 | |[![](http://meritbadge.herokuapp.com/resource_proof)](https://crates.io/crates/resource_proof)|[![Documentation](https://docs.rs/resource_proof/badge.svg)](https://docs.rs/resource_proof)|![](https://github.com/maidsafe/resource_proof/workflows/Master/badge.svg)| 6 | 7 | | [MaidSafe website](https://maidsafe.net) | [Safe Dev Forum](https://forum.safedev.org) | [Safe Network Forum](https://safenetforum.org) | 8 | |:----------------------------------------:|:-------------------------------------------:|:----------------------------------------------:| 9 | 10 | ## Summary 11 | 12 | This crate hopes to combine mechanisms that attempt to validate resources on remote machines. This 13 | validation though, is a spot check and also best effort. It is not guaranteed to be accurate over 14 | time and this consideration must be clear to users of the crate. 15 | 16 | The purpose is to provide **some** indication that a machine has **some** capabilities. 17 | 18 | ## Motivation 19 | 20 | In decentralised networks where trust is absent a node intending to join has to prove it can meet 21 | the minimum requirements of the network. These resource requirements are decided by either the 22 | network itself (best) or set by the programmer. 23 | 24 | In such networks, one must assume the node joining is not running the same software that existing 25 | nodes are running. 26 | 27 | Even if nodes offset this proof by using another resource to aid the proof, it's unlikely to help as 28 | the network should use continual monitoring of capability in parallel with these "spot checks". 29 | 30 | ## Current state 31 | 32 | At version 0.2.x this crate carries out some rudimentary checks that requires a node has some 33 | computing ability and also the ability to transfer a certain amount of data (bandwith check). 34 | 35 | Based on a variant of [Hashcash](https://en.wikipedia.org/wiki/Hashcash) with the addition of the 36 | requirement to transfer an amount of data, this library does provide a "proof of work" like 37 | algorithm. This work requirement forces joining nodes to perform some calculation and data transfer. 38 | The expected use case is to require the work is done and data transferred within a time duration. It 39 | is possible to supply two proofs, one to focus on a large amount of work (difficulty) and another to 40 | focus on a bandwidth requirement (size). These are combined in the API but do not necessarily need 41 | to be used as a single proof, unless this requirement can be calculated. 42 | 43 | The current hashing mechanism used is sha3 (keccak), this provides some requirement on the machine 44 | to "work" but is not ASIC resistant. This algorithm will likely be upgraded to something like 45 | [Equihash](https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf) 46 | which will likely resist ASIC type workarounds, but importantly will allow better requirements on 47 | the memory requirements of a node (this is not measured in this crate yet). 48 | 49 | Disk space measurements may also be added in future. 50 | 51 | ## Analysis 52 | 53 | There is an example to test any values and allow measurements on different architectures. This can 54 | be run as 55 | ```cargo run --release --example analyse -- -h``` 56 | Which will allow users to play with settings for difficulty and size. Difficulty is the setting that 57 | asks the machine to continually push zeros to the beginning of any data until the number of leading 58 | bits of the hash of the data are zero. Similar to a common proof of work algorithm. 59 | 60 | The size parameter forces the nonce provided to be repeated until it reaches a certain size in 61 | bytes. This is then transferred back to the network as a proof. 62 | 63 | To find the proof the node must continually push a zero to the beginning of the data (not at the end 64 | as this is easily optimised). This forces the continuous reading of a large data segment in each 65 | hash iteration. 66 | 67 | Some figures on a desktop linux machine are below : 68 | 69 | Small data element (36 bytes) 70 | 71 | ``` 72 | cargo run --release -- -d 1 -s1024 -i 73 | Running analysis .... 74 | Difficulty = 1 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 1 75 | Difficulty = 2 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 4 76 | Difficulty = 3 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 4 77 | Difficulty = 4 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 19 78 | Difficulty = 5 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 85 79 | Difficulty = 6 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 85 80 | Difficulty = 7 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 85 81 | Difficulty = 8 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 474 82 | Difficulty = 9 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 474 83 | Difficulty = 10 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 474 84 | Difficulty = 11 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 1017 85 | Difficulty = 12 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 1017 86 | Difficulty = 13 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 4367 87 | Difficulty = 14 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 4367 88 | Difficulty = 15 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 4367 89 | Difficulty = 16 size = 1024 create = 0 seconds check = 0 seconds num of attempts = 4367 90 | Difficulty = 17 size = 1024 create = 155 seconds check = 0 seconds num of attempts = 248184 91 | Difficulty = 18 size = 1024 create = 154 seconds check = 0 seconds num of attempts = 248184 92 | Difficulty = 19 size = 1024 create = 1508 seconds check = 0 seconds num of attempts = 787761 93 | Difficulty = 20 size = 1024 create = 6087 seconds check = 0 seconds num of attempts = 1587092 94 | ``` 95 | 96 | Circa 100Mb data 97 | 98 | ``` 99 | cargo run --release -- -d=1 -s=102400000 -i 100 | Running analysis .... 101 | Difficulty = 1 size = 10485760 create = 0 seconds check = 0 seconds num of attempts = 0 102 | Difficulty = 2 size = 10485760 create = 0 seconds check = 0 seconds num of attempts = 0 103 | Difficulty = 3 size = 10485760 create = 0 seconds check = 0 seconds num of attempts = 0 104 | Difficulty = 4 size = 10485760 create = 0 seconds check = 0 seconds num of attempts = 0 105 | Difficulty = 5 size = 10485760 create = 3 seconds check = 0 seconds num of attempts = 61 106 | Difficulty = 6 size = 10485760 create = 3 seconds check = 0 seconds num of attempts = 61 107 | Difficulty = 7 size = 10485760 create = 3 seconds check = 0 seconds num of attempts = 61 108 | Difficulty = 8 size = 10485760 create = 3 seconds check = 0 seconds num of attempts = 61 109 | Difficulty = 9 size = 10485760 create = 25 seconds check = 0 seconds num of attempts = 478 110 | Difficulty = 10 size = 10485760 create = 25 seconds check = 0 seconds num of attempts = 478 111 | Difficulty = 11 size = 10485760 create = 66 seconds check = 0 seconds num of attempts = 1268 112 | Difficulty = 12 size = 10485760 create = 210 seconds check = 0 seconds num of attempts = 4032 113 | Difficulty = 13 size = 10485760 create = 755 seconds check = 0 seconds num of attempts = 14860 114 | Difficulty = 14 size = 10485760 create = 1039 seconds check = 0 seconds num of attempts = 20484 115 | Difficulty = 15 size = 10485760 create = 1035 seconds check = 0 seconds num of attempts = 20484 116 | Difficulty = 16 size = 10485760 create = 1849 seconds check = 0 seconds num of attempts = 36453 117 | Difficulty = 17 size = 10485760 create = 2594 seconds check = 0 seconds num of attempts = 51130 118 | ``` 119 | 120 | The important point is that checking the proof is very fast and given enough difficulty, creating 121 | the proof is work intensive. This is a critical consideration that will mitigate some attack vectors 122 | on decentralised/p2p networks. It is by no means a security solution and should not be considered 123 | withouth continaul ongoing checks on a nodes "behaviour". 124 | 125 | 126 | ## License 127 | 128 | This SAFE Network library is dual-licensed under the Modified BSD ([LICENSE-BSD](LICENSE-BSD) https://opensource.org/licenses/BSD-3-Clause) or the MIT license ([LICENSE-MIT](LICENSE-MIT) http://opensource.org/licenses/MIT) at your option. 129 | 130 | ## Contribution 131 | 132 | Want to contribute? Great :tada: 133 | 134 | There are many ways to give back to the project, whether it be writing new code, fixing bugs, or just reporting errors. All forms of contributions are encouraged! 135 | 136 | For instructions on how to contribute, see our [Guide to contributing](https://github.com/maidsafe/QA/blob/master/CONTRIBUTING.md). 137 | -------------------------------------------------------------------------------- /codeowners: -------------------------------------------------------------------------------- 1 | * @maidsafe/backend_codeowners 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_try_shorthand = true 2 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under the MIT license or the Modified BSD license , at your option. This file may not be copied, 6 | // modified, or distributed except according to those terms. Please review the Licences for the 7 | // specific language governing permissions and limitations relating to use of the SAFE Network 8 | // Software. 9 | 10 | //! # Resource proof 11 | //! 12 | //! A mechanism to test resource availability (CPU and bandwidth) of a machine prior to it joining 13 | //! a network. This crate provides the creation and validation algorithms. 14 | //! 15 | //! Validation has some CPU and memory requirements but far less than proof creation. Bandwidth 16 | //! tests (data transfer) affect the machine being proved and the machine doing validation equally; 17 | //! it is suggested that multiple machines test any new machine to apply an asymmetric load. 18 | //! 19 | //! [GitHub repository](https://github.com/maidsafe/resource_proof) 20 | 21 | #![doc( 22 | html_logo_url = "https://raw.githubusercontent.com/maidsafe/QA/master/Images/maidsafe_logo.png", 23 | html_favicon_url = "https://maidsafe.net/img/favicon.ico", 24 | test(attr(forbid(warnings))) 25 | )] 26 | // For explanation of lint checks, run `rustc -W help` or see 27 | // https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md 28 | #![forbid( 29 | bad_style, 30 | arithmetic_overflow, 31 | mutable_transmutes, 32 | no_mangle_const_items, 33 | unknown_crate_types 34 | )] 35 | #![deny( 36 | deprecated, 37 | improper_ctypes, 38 | missing_docs, 39 | non_shorthand_field_patterns, 40 | overflowing_literals, 41 | stable_features, 42 | unconditional_recursion, 43 | unknown_lints, 44 | unsafe_code, 45 | unused, 46 | unused_allocation, 47 | unused_attributes, 48 | unused_comparisons, 49 | unused_features, 50 | unused_parens, 51 | while_true, 52 | warnings 53 | )] 54 | #![warn( 55 | trivial_casts, 56 | trivial_numeric_casts, 57 | unused_extern_crates, 58 | unused_import_braces, 59 | unused_qualifications, 60 | unused_results 61 | )] 62 | #![allow( 63 | box_pointers, 64 | missing_copy_implementations, 65 | missing_debug_implementations, 66 | variant_size_differences 67 | )] 68 | 69 | use std::collections::VecDeque; 70 | use tiny_keccak::{Hasher, Sha3}; 71 | 72 | /// Holds the prover requirements 73 | pub struct ResourceProof { 74 | min_size: usize, 75 | /// minimum size of proof in bytes 76 | difficulty: u8, 77 | } 78 | 79 | impl ResourceProof { 80 | /// Configure a new prover. 81 | /// 82 | /// `min_size` is target data size in bytes. It may be small or large to test bandwidth 83 | /// (although it may be compressible). 84 | /// 85 | /// `difficulty` is the number of leading binary zeros required in the hash. Each extra zero 86 | /// doubles the difficulty. 87 | pub fn new(min_size: usize, difficulty: u8) -> ResourceProof { 88 | ResourceProof { 89 | min_size, 90 | difficulty, 91 | } 92 | } 93 | 94 | /// Create the proof data with a given nonce. 95 | pub fn create_proof_data(&self, nonce: &[u8]) -> VecDeque { 96 | nonce.iter().cloned().cycle().take(self.min_size).collect() 97 | } 98 | 99 | /// Create a prover object. Requires a copy of the data (from `create_proof_data`) to be 100 | /// passed in. 101 | pub fn create_prover(&self, data: VecDeque) -> ResourceProver { 102 | ResourceProver { 103 | difficulty: self.difficulty, 104 | count: 0, 105 | data, 106 | } 107 | } 108 | 109 | /// Validate the proof data and key (this is the number of zeros to be pushed onto the data). 110 | pub fn validate_all(&self, nonce: &[u8], received_data: &VecDeque, key: u64) -> bool { 111 | let mut data = self.create_proof_data(nonce); 112 | if data != *received_data { 113 | return false; 114 | } 115 | for _ in 0..key { 116 | data.push_front(0u8); 117 | } 118 | self.check_hash(&data) >= self.difficulty 119 | } 120 | 121 | /// Validate the data for the given `nonce` and size data. 122 | pub fn validate_data(&self, nonce: &[u8], data: &VecDeque) -> bool { 123 | self.create_proof_data(nonce) == *data 124 | } 125 | 126 | /// Validate the proof key (this must recreate the data, hence `validate_all` is faster when 127 | /// both must be checked). 128 | pub fn validate_proof(&self, nonce: &[u8], key: u64) -> bool { 129 | let mut data = self.create_proof_data(nonce); 130 | for _ in 0..key { 131 | data.push_front(0u8); 132 | } 133 | self.check_hash(&data) >= self.difficulty 134 | } 135 | 136 | fn check_hash(&self, data: &VecDeque) -> u8 { 137 | ResourceProof::leading_zeros(&hash(&data.as_slices())) 138 | } 139 | 140 | fn leading_zeros(data: &[u8]) -> u8 { 141 | let mut zeros = 0u8; 142 | for (count, i) in data.iter().enumerate() { 143 | zeros = i.leading_zeros() as u8 + (count as u8 * 8); 144 | if i.leading_zeros() < 8 { 145 | break; 146 | } 147 | } 148 | zeros 149 | } 150 | } 151 | 152 | /// Object used to compute a result 153 | pub struct ResourceProver { 154 | difficulty: u8, 155 | count: u64, 156 | data: VecDeque, 157 | } 158 | 159 | impl ResourceProver { 160 | /// The expected number of steps is `pow(2, difficulty)`. 161 | /// The process is probabilistic, so the actual number of steps required may be more or less. 162 | /// 163 | /// The length of each step depends on data size. Total expected time is proportional to 164 | /// `length * pow(2, difficulty)`. 165 | pub fn expected_steps(&self) -> u64 { 166 | 2u64.pow(u32::from(self.difficulty)) 167 | } 168 | 169 | /// Try one step; if successful return the proof result. 170 | /// 171 | /// (This does not invalidate the prover. Continuing might find another valid solution.) 172 | pub fn try_step(&mut self) -> Option { 173 | if self.check_hash() >= self.difficulty { 174 | return Some(self.count); 175 | } 176 | 177 | self.data.push_front(0u8); 178 | self.count += 1; 179 | None 180 | } 181 | 182 | /// Keep stepping until a solution is found. Expected time can be calculated roughly (see 183 | /// `expected_steps`) but there is no upper bound (besides `u64::MAX`). 184 | pub fn solve(&mut self) -> u64 { 185 | loop { 186 | if let Some(solution) = self.try_step() { 187 | return solution; 188 | } 189 | } 190 | } 191 | 192 | fn check_hash(&self) -> u8 { 193 | ResourceProof::leading_zeros(&hash(&self.data.as_slices())) 194 | } 195 | } 196 | 197 | /// Hashes given seed into a nonce for use in proofs 198 | pub fn nonce_from_seed(seed: &[u8]) -> [u8; 32] { 199 | let mut hasher = Sha3::v256(); 200 | hasher.update(seed); 201 | 202 | let mut nonce = [0u8; 32]; 203 | hasher.finalize(&mut nonce); 204 | nonce 205 | } 206 | 207 | /// Simple wrapper around tiny-keccak for use with deques 208 | fn hash(data: &(&[u8], &[u8])) -> [u8; 32] { 209 | let mut hasher = Sha3::v256(); 210 | let mut res = [0u8; 32]; 211 | hasher.update(data.0); 212 | hasher.update(data.1); 213 | hasher.finalize(&mut res); 214 | res 215 | } 216 | 217 | #[cfg(test)] 218 | mod tests { 219 | use super::*; 220 | 221 | #[test] 222 | fn valid_proof() { 223 | for i in 0..20 { 224 | let nonce = nonce_from_seed(&[i]); 225 | let rp = ResourceProof::new(1024, 3); 226 | let data = rp.create_proof_data(&nonce); 227 | let proof = rp.create_prover(data.clone()).solve(); 228 | assert!(rp.validate_proof(&nonce, proof)); 229 | assert!(rp.validate_data(&nonce, &data)); 230 | assert!(rp.validate_all(&nonce, &data, proof)); 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under the MIT license or the Modified BSD license , at your option. This file may not be copied, 6 | // modified, or distributed except according to those terms. Please review the Licences for the 7 | // specific language governing permissions and limitations relating to use of the SAFE Network 8 | // Software. 9 | 10 | //! Command line tool for generating and validating resource proofs. 11 | 12 | // For explanation of lint checks, run `rustc -W help` or see 13 | // https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md 14 | #![forbid( 15 | arithmetic_overflow, 16 | mutable_transmutes, 17 | no_mangle_const_items, 18 | unknown_crate_types 19 | )] 20 | #![deny( 21 | deprecated, 22 | improper_ctypes, 23 | missing_docs, 24 | non_shorthand_field_patterns, 25 | overflowing_literals, 26 | stable_features, 27 | unconditional_recursion, 28 | unknown_lints, 29 | unsafe_code, 30 | unused, 31 | unused_allocation, 32 | unused_attributes, 33 | unused_comparisons, 34 | unused_features, 35 | unused_parens, 36 | while_true, 37 | warnings 38 | )] 39 | #![warn( 40 | trivial_casts, 41 | trivial_numeric_casts, 42 | unused_extern_crates, 43 | unused_import_braces, 44 | unused_qualifications, 45 | unused_results 46 | )] 47 | #![allow( 48 | box_pointers, 49 | missing_copy_implementations, 50 | missing_debug_implementations, 51 | variant_size_differences 52 | )] 53 | 54 | use clap::Parser; 55 | use resource_proof::ResourceProof; 56 | use std::time::Instant; 57 | #[cfg(not(windows))] 58 | use termion::color; 59 | 60 | fn test_it(dif: u8, size: usize, nonce: [u8; 32]) { 61 | let create = Instant::now(); 62 | let rp = ResourceProof::new(size, dif); 63 | let data = rp.create_proof_data(&nonce); 64 | let mut prover = rp.create_prover(data.clone()); 65 | let expected_steps = prover.expected_steps(); 66 | let proof = prover.solve(); 67 | let create_time = create.elapsed().as_secs(); 68 | let check = Instant::now(); 69 | if !rp.validate_proof(&nonce, proof) { 70 | println!("FAILED TO CONFIRM PROOF - POSSIBLE VIOLATION"); 71 | } 72 | 73 | if !rp.validate_data(&nonce, &data) { 74 | println!("FAILED TO CONFIRM PROOF DATA - POSSIBLE VIOLATION"); 75 | } 76 | 77 | if !rp.validate_all(&nonce, &data, proof) { 78 | println!("FAILED TO CONFIRM PROOF & DATA - POSSIBLE VIOLATION"); 79 | } 80 | 81 | println!( 82 | "Difficulty = {} expected_steps = {} size = {} create = {} seconds check = {} \ 83 | seconds num of steps = {:?}", 84 | dif, 85 | expected_steps, 86 | size, 87 | create_time, 88 | check.elapsed().as_secs(), 89 | proof 90 | ); 91 | } 92 | 93 | #[cfg(not(windows))] 94 | fn print_red(message: &str) { 95 | println!(); 96 | println!(); 97 | println!( 98 | "{}{}{}", 99 | color::Fg(color::Red), 100 | message, 101 | color::Fg(color::Reset) 102 | ); 103 | } 104 | 105 | #[cfg(windows)] 106 | fn print_red(message: &str) { 107 | println!(); 108 | println!(); 109 | println!("{}", message); 110 | } 111 | 112 | #[derive(Parser, Debug)] 113 | #[clap(author, version)] 114 | #[clap(name = "Simple Resource Proof example")] 115 | #[clap(about = "Please set the size and difficulty to test", long_about = None)] 116 | #[clap( 117 | after_help = "Several proofs may be chained, i.e. a large difficulty and small size or vice versa to check CPU And BW seperately" 118 | )] 119 | struct Config { 120 | #[clap(short, long)] 121 | #[clap(help = "The number of leading zeros of the proof when hashed with SHA3")] 122 | difficulty: u8, 123 | 124 | #[clap(short, long)] 125 | #[clap(help = "The minimum size of the proof in bytes")] 126 | size: usize, 127 | 128 | #[clap(long, default_value = "A long long time ago..")] 129 | #[clap(help = "Initial nonce seed")] 130 | seed: String, 131 | 132 | #[clap(short, long, action)] 133 | #[clap( 134 | help = "Will run continuously, increasing difficulty with every invocation. Note this will likley not stop in your lifetime :-)" 135 | )] 136 | increase: bool, 137 | } 138 | 139 | fn main() { 140 | let config = Config::parse(); 141 | 142 | print_red("Running analysis ...."); 143 | 144 | let nonce = resource_proof::nonce_from_seed(config.seed.as_bytes()); 145 | 146 | if config.increase { 147 | for i in config.difficulty.. { 148 | test_it(i, config.size, nonce); 149 | } 150 | } else { 151 | test_it(config.difficulty, config.size, nonce); 152 | } 153 | } 154 | --------------------------------------------------------------------------------