├── .envrc ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── audit.yml │ ├── bench.yml │ ├── coverage.yml │ ├── release.yml │ └── tests_and_checks.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .release-please-manifest.json ├── .rustfmt.toml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE ├── README.md ├── assets └── a_logo.png ├── codecov.yml ├── deny.toml ├── deterministic-bloom-benches ├── Cargo.toml └── benches │ └── bloom_filter_benchmark.rs ├── deterministic-bloom-wasm ├── Cargo.toml ├── LICENSE ├── README.md ├── src │ └── lib.rs └── tests │ └── web.rs ├── deterministic-bloom ├── .dockerignore ├── Cargo.toml ├── LICENSE ├── README.md ├── proptest-regressions │ └── lib.txt └── src │ ├── common.rs │ ├── const_size.rs │ ├── lib.rs │ ├── runtime_size.rs │ └── utils.rs ├── examples ├── Cargo.toml └── counterparts.rs ├── flake.lock ├── flake.nix ├── release-please-config.json └── rust-toolchain.toml /.envrc: -------------------------------------------------------------------------------- 1 | use_flake 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default 2 | * @expede 3 | * @matheus23 4 | * @appcypher 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: "\U0001F41B bug" 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Summary 11 | 12 | ## Problem 13 | 14 | Describe the immediate problem. 15 | 16 | ### Impact 17 | 18 | What's the impact of this bug? 19 | 20 | ## Solution 21 | 22 | Describe the sort of fix that would solve the issue. 23 | 24 | # Detail 25 | 26 | **Describe the bug** 27 | 28 | A clear and concise description of what the bug is. 29 | 30 | **To Reproduce** 31 | 32 | Steps to reproduce the behavior: 33 | 1. Go to '...' 34 | 2. Click on '....' 35 | 3. Scroll down to '....' 36 | 4. See error 37 | 38 | **Expected behavior** 39 | 40 | A clear and concise description of what you expected to happen. 41 | 42 | **Screenshots** 43 | 44 | If applicable, add screenshots to help explain your problem. 45 | 46 | **Desktop (please complete the following information):** 47 | 48 | - OS: [e.g. iOS] 49 | - Browser [e.g. chrome, safari] 50 | - Version [e.g. 22] 51 | 52 | **Smartphone (please complete the following information):** 53 | 54 | - Device: [e.g. iPhone6] 55 | - OS: [e.g. iOS8.1] 56 | - Browser [e.g. stock browser, safari] 57 | - Version [e.g. 22] 58 | 59 | **Additional context** 60 | 61 | Add any other context about the problem here. 62 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: "\U0001F497 enhancement" 6 | assignees: '' 7 | 8 | --- 9 | 10 | NB: Feature requests will only be considered if they solve a pain or present a useful refactoring of the code. 11 | 12 | # Summary 13 | 14 | ## Problem 15 | 16 | Describe the pain that this feature will solve. 17 | 18 | ### Impact 19 | 20 | Describe the impact of not having this feature. 21 | 22 | ## Solution 23 | 24 | Describe the solution. 25 | 26 | # Detail 27 | 28 | **Is your feature request related to a problem? Please describe.** 29 | 30 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 31 | 32 | **Describe the solution you'd like** 33 | 34 | A clear and concise description of what you want to happen. 35 | 36 | **Describe alternatives you've considered** 37 | 38 | A clear and concise description of any alternative solutions or features you've considered. 39 | 40 | **Additional context** 41 | 42 | Add any other context or screenshots about the feature request here. 43 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | ## Link to issue 6 | 7 | Please add a link to any relevant issues/tickets. 8 | 9 | ## Type of change 10 | 11 | - [ ] Bug fix (non-breaking change that fixes an issue) 12 | - [ ] New feature (non-breaking change that adds functionality) 13 | - [ ] Refactor (non-breaking change that updates existing functionality) 14 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 15 | - [ ] This change requires a documentation update 16 | - [ ] Comments have been added/updated 17 | 18 | Please delete options that are not relevant. 19 | 20 | ## Test plan (required) 21 | 22 | Demonstrate the code is solid. Which commands did you test with and what are the expected results? 23 | Which tests have you added or updated? Do the tests cover all of the changes included in this PR? 24 | 25 | ## Screenshots/Screencaps 26 | 27 | Please add previews of any UI Changes. 28 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | 8 | updates: 9 | - package-ecosystem: "cargo" 10 | directory: "/deterministic-bloom" 11 | commit-message: 12 | prefix: "chore(rust)" 13 | include: "scope" 14 | target-branch: "main" 15 | schedule: 16 | interval: "weekly" 17 | 18 | - package-ecosystem: "npm" 19 | directory: "/deterministic-bloom-wasm" 20 | commit-message: 21 | prefix: "chore(npm)" 22 | include: "scope" 23 | target-branch: "main" 24 | schedule: 25 | interval: "weekly" 26 | 27 | - package-ecosystem: "github-actions" 28 | directory: "/" 29 | commit-message: 30 | prefix: "chore(ci)" 31 | include: "scope" 32 | target-branch: "main" 33 | schedule: 34 | interval: "weekly" 35 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: 🛡 Audit-Check 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | jobs: 8 | security-audit: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Checkout Repository 13 | uses: actions/checkout@v3 14 | 15 | - name: Run Audit-Check 16 | uses: rustsec/audit-check@v1.4.1 17 | with: 18 | token: ${{ secrets.GITHUB_TOKEN }} 19 | -------------------------------------------------------------------------------- /.github/workflows/bench.yml: -------------------------------------------------------------------------------- 1 | name: 📈 Benchmark 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | pull_request: 8 | branches: [ '*' ] 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | benchmark: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout Repository 20 | uses: actions/checkout@v3 21 | 22 | - name: Install Rust Toolchain 23 | uses: actions-rs/toolchain@v1 24 | with: 25 | override: true 26 | toolchain: stable 27 | 28 | - name: Cache Project 29 | uses: Swatinem/rust-cache@v2 30 | 31 | - name: Run Benchmark 32 | run: cargo bench -p deterministic-bloom-benches -- --output-format bencher | tee output.txt 33 | 34 | - name: Upload Benchmark Result Artifact 35 | uses: actions/upload-artifact@v3 36 | with: 37 | name: bench_result 38 | path: output.txt 39 | 40 | - name: Create gh-pages Branch 41 | uses: peterjgrainger/action-create-branch@v2.4.0 42 | env: 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | with: 45 | branch: gh-pages 46 | 47 | - name: Store Benchmark Result 48 | uses: benchmark-action/github-action-benchmark@v1 49 | with: 50 | name: Rust Benchmark 51 | tool: 'cargo' 52 | output-file-path: output.txt 53 | github-token: ${{ secrets.GITHUB_TOKEN }} 54 | auto-push: ${{ github.event_name == 'push' && github.repository == 'wnfs-wg/deterministic-bloom' && github.ref == 'refs/heads/main' }} 55 | alert-threshold: '200%' 56 | comment-on-alert: true 57 | fail-on-alert: true 58 | alert-comment-cc-users: '@expede' 59 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: ☂ Code Coverage 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | pull_request: 8 | branches: [ '*' ] 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | coverage: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout Repository 20 | uses: actions/checkout@v3 21 | 22 | - name: Install Rust Toolchain 23 | uses: actions-rs/toolchain@v1 24 | with: 25 | override: true 26 | toolchain: nightly 27 | components: llvm-tools-preview 28 | profile: minimal 29 | 30 | - name: Cache Project 31 | uses: Swatinem/rust-cache@v2 32 | 33 | - name: Generate Code coverage 34 | env: 35 | CARGO_INCREMENTAL: '0' 36 | LLVM_PROFILE_FILE: "deterministic-bloom-%p-%m.profraw" 37 | RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' 38 | RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' 39 | ## currently just runs coverage on rust project 40 | run: cargo test -p deterministic-bloom --all-features 41 | 42 | - name: Install grcov 43 | run: "curl -L https://github.com/mozilla/grcov/releases/download/v0.8.12/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf -" 44 | 45 | - name: Run grcov 46 | run: "./grcov . --llvm --binary-path target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore '/*' -o lcov.info" 47 | 48 | - name: Install covfix 49 | uses: actions-rs/install@v0.1 50 | with: 51 | crate: rust-covfix 52 | use-tool-cache: true 53 | 54 | - name: Run covfix 55 | run: rust-covfix lcov.info -o lcov.info --verbose 56 | 57 | - name: Upload to codecov.io 58 | uses: codecov/codecov-action@v3 59 | with: 60 | token: ${{ secrets.CODECOV_TOKEN }} 61 | fail_ci_if_error: true 62 | files: lcov.info 63 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: 𝌚 Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | force-publish: 7 | required: true 8 | type: boolean 9 | description: Publish Releases at Anytime 10 | 11 | workflow_run: 12 | workflows: [ 🧪 Tests and Checks ] 13 | branches: [main] 14 | types: [ completed ] 15 | 16 | concurrency: 17 | group: ${{ github.workflow }}-${{ github.ref }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | release-please: 22 | runs-on: ubuntu-latest 23 | if: > 24 | github.ref == 'refs/heads/main' && 25 | github.repository_owner == 'wnfs-wg' && 26 | github.event.workflow_run.conclusion == 'success' 27 | 28 | outputs: 29 | releases_created: ${{ steps.release.outputs['deterministic-bloom--release_created'] || steps.release.outputs['deterministic-bloom-wasm--release_created'] }} 30 | wasm_release_created: ${{ steps.release.outputs['deterministic-bloom-wasm--release_created'] }} 31 | 32 | steps: 33 | - name: Run release-please 34 | id: release 35 | uses: google-github-actions/release-please-action@v3 36 | with: 37 | token: ${{ secrets.GITHUB_TOKEN }} 38 | default-branch: main 39 | command: manifest 40 | extra-files: | 41 | README.md 42 | 43 | 44 | publish-release-crates: 45 | if: ${{ needs.release-please.outputs.releases_created || github.event.inputs.force-publish }} 46 | 47 | runs-on: ubuntu-latest 48 | needs: [ release-please ] 49 | 50 | permissions: 51 | contents: write 52 | 53 | steps: 54 | - name: Checkout Repository 55 | uses: actions/checkout@v3 56 | 57 | - name: Cache Project 58 | uses: Swatinem/rust-cache@v2 59 | 60 | - name: Install Rust Toolchain 61 | uses: actions-rs/toolchain@v1 62 | with: 63 | override: true 64 | profile: minimal 65 | toolchain: stable 66 | 67 | - name: Install Cargo Workspaces 68 | env: 69 | RUSTFLAGS: '-Copt-level=1' 70 | uses: actions-rs/cargo@v1 71 | with: 72 | args: --force cargo-workspaces 73 | command: install 74 | 75 | - name: Cargo Publish to crates.io 76 | env: 77 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 78 | run: cargo workspaces publish --from-git 79 | 80 | publish-release-npm: 81 | if: ${{ needs.release-please.outputs.wasm_release_created || github.event.inputs.force-publish }} 82 | 83 | runs-on: ubuntu-latest 84 | needs: [ release-please ] 85 | 86 | permissions: 87 | contents: write 88 | 89 | defaults: 90 | run: 91 | working-directory: ./deterministic-bloom-wasm 92 | 93 | steps: 94 | - name: Checkout Repository 95 | uses: actions/checkout@v3 96 | 97 | - name: Cache Project 98 | uses: Swatinem/rust-cache@v2 99 | 100 | - name: Install Rust Toolchain 101 | uses: actions-rs/toolchain@v1 102 | with: 103 | override: true 104 | profile: minimal 105 | toolchain: stable 106 | 107 | - name: Setup Node 108 | uses: actions/setup-node@v3 109 | with: 110 | node-version: '16.x' 111 | registry-url: 'https://registry.npmjs.org' 112 | 113 | - name: Install wasm-pack 114 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 115 | 116 | # Builds output via wasm-pack and renames package.json name to remove 117 | # `-wasm`. 118 | - name: Build 119 | run: | 120 | wasm-pack build --target web --out-name deterministic-bloom 121 | sed -i -e 's/"name": "deterministic-bloom-wasm"/"name": "deterministic-bloom",\n "type": "module"/g' pkg/package.json 122 | 123 | - name: Publish to npm 124 | run: wasm-pack publish 125 | working-directory: pkg 126 | env: 127 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 128 | -------------------------------------------------------------------------------- /.github/workflows/tests_and_checks.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Tests and Checks 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | pull_request: 8 | branches: [ '*' ] 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | run-checks: 16 | runs-on: ubuntu-latest 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | rust-toolchain: 21 | - stable 22 | - nightly 23 | # minimum version 24 | - 1.64 25 | steps: 26 | - name: Checkout Repository 27 | uses: actions/checkout@v3 28 | 29 | # Smarter caching action, speeds up build times compared to regular cache: 30 | # https://github.com/Swatinem/rust-cache 31 | - name: Cache Project 32 | uses: Swatinem/rust-cache@v2 33 | 34 | # Widely adopted suite of Rust-specific boilerplate actions, especially 35 | # toolchain/cargo use: https://actions-rs.github.io/ 36 | - name: Install Rust Toolchain 37 | uses: actions-rs/toolchain@v1 38 | with: 39 | override: true 40 | components: rustfmt, clippy 41 | toolchain: ${{ matrix.rust-toolchain }} 42 | 43 | - name: Check Format 44 | uses: actions-rs/cargo@v1 45 | with: 46 | args: --all -- --check 47 | command: fmt 48 | toolchain: ${{ matrix.rust-toolchain }} 49 | 50 | - name: Run Linter 51 | uses: actions-rs/cargo@v1 52 | with: 53 | args: --all -- -D warnings 54 | command: clippy 55 | toolchain: ${{ matrix.rust-toolchain }} 56 | 57 | # Check for security advisories. 58 | - name: Check Advisories 59 | if: ${{ matrix.rust-toolchain == 'stable' }} 60 | uses: EmbarkStudios/cargo-deny-action@v1 61 | with: 62 | command: check advisories 63 | continue-on-error: true 64 | 65 | # Audit licenses, unreleased crates, and unexpected duplicate versions. 66 | - name: Check Bans, Licenses, and Sources 67 | if: ${{ matrix.rust-toolchain == 'stable' }} 68 | uses: EmbarkStudios/cargo-deny-action@v1 69 | with: 70 | command: check bans licenses sources 71 | 72 | - name: Install wasm-pack 73 | if: ${{ matrix.rust-toolchain == 'stable' }} && github.event_name == 'push' }} 74 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 75 | 76 | # Only "test" release build on push event. 77 | - name: Test Release 78 | if: ${{ matrix.rust-toolchain == 'stable' }} && github.event_name == 'push' }} 79 | run: | 80 | cargo build --manifest-path deterministic-bloom/Cargo.toml --release 81 | cd deterministic-bloom-wasm && wasm-pack build --target web --release 82 | 83 | run-cargo-tests: 84 | runs-on: ubuntu-latest 85 | strategy: 86 | fail-fast: false 87 | matrix: 88 | rust-toolchain: 89 | - stable 90 | - nightly 91 | 92 | steps: 93 | - name: Checkout Repository 94 | uses: actions/checkout@v3 95 | 96 | - name: Cache Project 97 | uses: Swatinem/rust-cache@v2 98 | 99 | - name: Install Rust Toolchain 100 | uses: actions-rs/toolchain@v1 101 | with: 102 | override: true 103 | toolchain: ${{ matrix.rust-toolchain }} 104 | 105 | - name: Run Tests 106 | run: cargo test --manifest-path deterministic-bloom/Cargo.toml --all-features 107 | 108 | run-headless-tests: 109 | runs-on: ${{ matrix.os }} 110 | strategy: 111 | fail-fast: false 112 | matrix: 113 | os: [ ubuntu-latest ] 114 | browser: [ firefox, chrome ] 115 | 116 | # include: 117 | # bug w/ wasm-bindgen: https://github.com/rustwasm/wasm-bindgen/issues/3004 118 | # - os: macos-latest 119 | # browser: safari 120 | 121 | defaults: 122 | run: 123 | working-directory: ./deterministic-bloom-wasm 124 | 125 | steps: 126 | - name: Checkout Repository 127 | uses: actions/checkout@v3 128 | 129 | - name: Install wasm-pack 130 | run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh 131 | 132 | - name: Cache Project 133 | uses: Swatinem/rust-cache@v2 134 | 135 | - name: Install Rust Toolchain 136 | uses: actions-rs/toolchain@v1 137 | with: 138 | override: true 139 | toolchain: stable 140 | 141 | - name: Run Rust Headless Browser Tests 142 | run: wasm-pack test --headless --${{ matrix.browser }} 143 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | Cargo.lock 5 | # These are backup files generated by rustfmt 6 | **/*.rs.bk 7 | 8 | # Ignore local environment settings 9 | .envrc.custom 10 | .direnv 11 | 12 | **/package-lock.json 13 | **/pkg/ 14 | wasm-pack.log 15 | **/node_modules/ 16 | 17 | # Other files + dirs 18 | private 19 | *.temp 20 | *.tmp 21 | **/.history 22 | **/.DS_Store 23 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # See https://pre-commit.com for more information 2 | # pre-commit install 3 | # pre-commit install --hook-type commit-msg 4 | exclude: ^(LICENSE|LICENSE*) 5 | repos: 6 | - repo: local 7 | hooks: 8 | - id: fmt 9 | name: fmt 10 | description: Format rust files. 11 | entry: cargo fmt 12 | language: system 13 | types: [rust] 14 | args: ["--all", "--", "--check"] 15 | - id: cargo-check 16 | name: cargo check 17 | description: Check the package for errors. 18 | entry: cargo check 19 | language: system 20 | types: [rust] 21 | pass_filenames: false 22 | - id: clippy 23 | name: clippy 24 | description: Lint via clippy 25 | entry: cargo clippy 26 | language: system 27 | args: ["--workspace", "--", "-D", "warnings"] 28 | types: [rust] 29 | pass_filenames: false 30 | 31 | - repo: https://github.com/DevinR528/cargo-sort 32 | rev: v1.0.9 33 | hooks: 34 | - id: cargo-sort 35 | args: ["-w"] 36 | 37 | - repo: https://github.com/compilerla/conventional-pre-commit 38 | rev: v2.1.1 39 | hooks: 40 | - id: conventional-pre-commit 41 | stages: 42 | - commit-msg 43 | 44 | - repo: https://github.com/pre-commit/pre-commit-hooks 45 | rev: v4.3.0 46 | hooks: 47 | - id: no-commit-to-branch 48 | args: ["-b", "main"] 49 | - id: check-merge-conflict 50 | - id: trailing-whitespace 51 | - id: end-of-file-fixer 52 | - id: check-yaml 53 | - id: check-json 54 | - id: check-added-large-files 55 | - id: detect-private-key 56 | - id: check-executables-have-shebangs 57 | - id: check-toml 58 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "deterministic-bloom": "0.1.0", 3 | "deterministic-bloom-wasm": "0.1.0" 4 | } 5 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | imports_granularity = "Crate" 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wnfs-wg/deterministic-bloom/62849fa6b3d1731ea4f4cb18b8d81e845404dc10/CHANGELOG.md -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | **TL;DR Be kind, inclusive, and considerate.** 4 | 5 | In the interest of fostering an open, inclusive, and welcoming environment, all 6 | members, contributors, and maintainers interacting within our online community 7 | (including Discord, Discourse, etc.), on affiliated projects and repositories 8 | (including issues, pull requests, and discussions on Github), and/or involved 9 | with associated events pledge to accept and observe the following Code of 10 | Conduct. 11 | 12 | As members, contributors, and maintainers, we pledge to make participation in 13 | our projects and community a harassment-free experience, ensuring a safe 14 | environment for all, regardless of background, gender, gender identity and 15 | expression, age, sexual orientation, disability, physical appearance, body size, 16 | race, ethnicity, religion (or lack thereof), or any other dimension of 17 | diversity. 18 | 19 | Sexual language and imagery will not be accepted in any way. Be kind to others. 20 | Do not insult or put down people within the community. Behave professionally. 21 | Remember that harassment and sexist, racist, or exclusionary jokes are not 22 | appropriate in any form. Participants violating these rules may be sanctioned or 23 | expelled from the community and related projects. 24 | 25 | ## Spelling it out. 26 | 27 | Harassment includes offensive verbal comments or actions related to or involving 28 | 29 | - background 30 | - gender 31 | - gender identity and expression 32 | - age 33 | - sexual orientation 34 | - disability 35 | - physical appearance 36 | - body size 37 | - race 38 | - ethnicity 39 | - religion (or lack thereof) 40 | - economic status 41 | - geographic location 42 | - technology choices 43 | - sexual imagery 44 | - deliberate intimidation 45 | - violence and threats of violence 46 | - stalking 47 | - doxing 48 | - inappropriate or unwelcome physical contact in public spaces 49 | - unwelcomed sexual attention 50 | - influencing unacceptable behavior 51 | - any other dimension of diversity 52 | 53 | ## Our Responsibilities 54 | 55 | Maintainers of the community and associated projects are not only subject to the 56 | anti-harassment policy, but also responsible for executing the policy, 57 | moderating related forums, and for taking appropriate and fair corrective action 58 | in response to any instances of unacceptable behavior that breach the policy. 59 | 60 | Maintainers have the right to remove and reject comments, threads, commits, 61 | code, documentation, pull requests, issues, and contributions not aligned with 62 | this Code of Conduct. 63 | 64 | ## Scope 65 | 66 | This Code of Conduct applies within all project and community spaces, as well as 67 | in any public spaces where an individual representing the community is involved. 68 | This covers 69 | 70 | - Interactions on the Github repository, including discussions, issues, pull 71 | requests, commits, and wikis 72 | - Interactions on any affiliated Discord, Slack, IRC, or related online 73 | communities and forums like Discourse, etc. 74 | - Any official project emails and social media posts 75 | - Individuals representing the community at public events like meetups, talks, 76 | and presentations 77 | 78 | ## Enforcement 79 | 80 | All instances of abusive, harassing, or otherwise unacceptable behavior should 81 | be reported by contacting the project and community maintainers at 82 | [brooklyn@fission.codes][support-email]. All complaints will be reviewed and 83 | investigated and will result in a response that is deemed necessary and 84 | appropriate to the circumstances. 85 | 86 | Maintainers of the community and associated projects are obligated to maintain 87 | confidentiality with regard to the reporter of an incident. Further details of 88 | specific enforcement policies may be posted separately. 89 | 90 | Anyone asked to stop abusive, harassing, or otherwise unacceptable behavior are 91 | expected to comply immediately and accept the response decided on by the 92 | maintainers of the community and associated projects. 93 | 94 | ## Need help? 95 | 96 | If you are experiencing harassment, witness an incident or have concerns about 97 | content please contact us at [brooklyn@fission.codes][support-email]. 98 | 99 | ## Attribution 100 | 101 | This Code of Conduct is adapted from the [Contributor Covenant, v2.1][contributor-cov], 102 | among other sources like [!!con’s Code of Conduct][!!con] and 103 | [Mozilla’s Community Participation Guidelines][mozilla]. 104 | 105 | [!!con]: https://bangbangcon.com/conduct.html 106 | [contributor-cov]: https://www.contributor-covenant.org/version/2/1/code_of_conduct/ 107 | [mozilla]: https://www.mozilla.org/en-US/about/governance/policies/participation/ 108 | [support-email]: mailto:brooklyn@fission.codes 109 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to deterministic-bloom 2 | 3 | We welcome everyone to contribute what and where they can. Whether you are brand 4 | new, just want to contribute a little bit, or want to contribute a lot there is 5 | probably something you can help out with. Check out our 6 | [good first issues][good-first-issues] label for in the issues tab to see a list 7 | of issue that good for those new to the project. 8 | 9 | ## Where to Get Help 10 | 11 | The main way to get help is on our [discord server](https://discord.gg/fissioncodes).server][https://discord.gg/fissioncodes]. 12 | Though, this guide should help you get started. It may be slightly lengthy, but it's 13 | designed for those who are new so please don't let length intimidate you. 14 | 15 | ## Code of Conduct 16 | 17 | Please be kind, inclusive, and considerate when interacting when interacting 18 | with others and follow our [code of conduct](./CODE_OF_CONDUCT.md). 19 | 20 | ## How to Contribute 21 | 22 | If the code adds a feature that is not already present in an issue, you can 23 | create a new issue for the feature and add the pull request to it. If the code 24 | adds a feature that is not already present in an issue, you can create a new 25 | issue for the feature and add the pull request to it. 26 | 27 | ### Contributing by Adding a Topic for Discussion 28 | 29 | #### Issues 30 | 31 | If you have found a bug and would like to report it or if you have a feature 32 | that you feel we should add, then we'd love it if you opened an issue! ❤️ 33 | Before you do, please search the other issues to avoid creating a duplicate 34 | issue. 35 | 36 | To submit a new issue just hit the issue button and a choice between two 37 | templates should appear. Then, follow along with the template you chose. If you 38 | don't know how to fill in all parts of the template go ahead and skip those 39 | parts. You can edit the issue later. 40 | 41 | #### Discussion 42 | 43 | If you have a new discussion you want to start but it isn't a bug or feature 44 | add, then you can start a [GitHub discussion][gh-discussions]. Some examples of 45 | what kinds of things that are good discussion topics can include, but are not 46 | limited to the following: 47 | 48 | - Community announcements and/or asking the community for feedback 49 | - Discussing a new release 50 | - Asking questions, Q&A that isn't for sure a bug report 51 | 52 | ### Contributing through Code 53 | 54 | In order to contribute through code follow the steps below. Note that you don't 55 | need to be the best programmer to contribute. Our discord is open for questions 56 | 1. **Pick a feature** you would like to add or a bug you would like to fix 57 | - If you wish to contribute but what you want to fix/add is not already 58 | covered in an existing issue, please open a new issue. 59 | 60 | 2. **Discuss** the issue with the rest of the community 61 | - Before you write any code, it is recommended that you discuss your 62 | intention to write the code on the issue you are attempting to edit. 63 | - This helps to stop you from wasting your time duplicating the work of 64 | others that maybe working on the same issue; at the same time. 65 | - This step also allows you to get helpful pointers on the community on some 66 | problems they may have encountered on similar issues. 67 | 68 | 3. **Fork** the repository 69 | - A fork creates a copy of the code on your Github, so you can work on it 70 | separately from everyone else. 71 | - You can learn more about forking [here][forking]. 72 | 73 | 4. Ensure that you have **commit signing** enabled 74 | - This ensures that the code you submit was committed by you and not someone 75 | else who claims to be you. 76 | - You can learn more about how to setup commit signing [here][commit-signing]. 77 | - If you have already made some commits that you wish to put in a pull 78 | request without signing them, then you can follow [this guide][post-signing] 79 | on how to fix that. 80 | 81 | 5. **Clone** the repository to your local computer 82 | - This puts a copy of your fork on your computer so you can edit it 83 | - You can learn more about cloning repositories [here][git-clone]. 84 | 85 | 6. **Build** the project 86 | - For a detailed look on how to build deterministic-bloom look at our 87 | [README file](./README.md). 88 | 89 | 7. **Start writing** your code 90 | - Open up your favorite code editor and make the changes that you wanted to 91 | make to the repository. 92 | - Make sure to test your code with the test command(s) found in our 93 | [README file](./README.md). 94 | 95 | 8. **Write tests** for your code 96 | - If you are adding a new feature, you should write tests that ensure that 97 | if someone make changes to the code it cannot break your new feature 98 | without breaking the test. 99 | - If your code adds a new feature, you should also write at least one 100 | documentation test. The documentation test's purpose is to demonstrate and 101 | document how to use the API feature. 102 | - If your code fixes a bug, you should write tests that ensure that if 103 | someone makes code changes in the future the bug does not re-emerge 104 | without breaking test. 105 | - Please create integration tests, if the addition is large enough to 106 | warrant them, and unit tests. 107 | * Unit tests are tests that ensure the functionality of a single 108 | function or small section of code. 109 | * Integration tests test large large sections of code. 110 | * Read more about the differences [here][unit-and-integration]. 111 | - For more information on test organization, take a look [here][test-org]. 112 | 113 | 9. Ensure that the code that you made follows our Rust **coding guidelines** 114 | - You can find a list of some Rust guidelines [here][rust-style-guide]. This 115 | is a courtesy to the programmers that come after you. The easier your code 116 | is to read, the easier it will be for the next person to make modifications. 117 | - If you find it difficult to follow the guidelines or if the guidelines or 118 | unclear, please reach out to us through our discord linked above, or you 119 | can just continue and leave a comment at the pull request stage. 120 | 121 | 10. **Commit and Push** your code 122 | - This sends your changes to your repository branch. 123 | - You can learn more about committing code [here][commiting-code] and 124 | pushing it to a remote repository [here][push-remote]. 125 | - We use conventional commits for the names and description of commits. 126 | You can find out more about them [here][conventional-commits]. 127 | 128 | 11. The final step is to create **pull request** to our main branch 🎉 129 | - A pull request is how you merge the code you just worked so hard on with 130 | the code everyone else has access to. 131 | - Once you have submitted your pull request, we will review your code and 132 | check to make sure the code implements the feature or fixes the bug. We 133 | may leave some feedback and suggest edits. You can make the changes we 134 | suggest by committing more code to your fork. 135 | - You can learn more about pull requests [here][prs]. 136 | 137 | 138 | [conventional-commits]: https://www.conventionalcommits.org/en/v1.0.0/ 139 | [commiting-code]: https://docs.github.com/en/desktop/contributing-and-collaborating-using-github-desktop/making-changes-in-a-branch/committing-and-reviewing-changes-to-your-project 140 | [commit-signing]: https://www.freecodecamp.org/news/what-is-commit-signing-in-git/ 141 | [forking]: https://docs.github.com/en/get-started/quickstart/fork-a-repo 142 | [gh-discussions]: https://docs.github.com/en/discussions 143 | [git-clone]: https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository 144 | [good-first-issues]: [https://build.prestashop-project.org/news/a-definition-of-the-good-first-issue-label/] 145 | [post-signing]: https://dev.to/jmarhee/signing-existing-commits-with-gpg-5b58 146 | [prs]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests 147 | [push-remote]: https://docs.github.com/en/get-started/using-git/pushing-commits-to-a-remote-repository 148 | [rust-style-guide]: https://rust-lang.github.io/api-guidelines/about.html 149 | [test-org]: https://doc.rust-lang.org/book/ch11-03-test-organization.html 150 | [unit-and-integration]: https://www.geeksforgeeks.org/difference-between-unit-testing-and-integration-testing/ 151 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "deterministic-bloom", 4 | "deterministic-bloom-benches", 5 | "deterministic-bloom-wasm", 6 | "examples" 7 | ] 8 | 9 | [profile.release] 10 | # Tell `rustc` to optimize for small code size. 11 | opt-level = "s" 12 | 13 | # Speedup build on macOS 14 | # See https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html#splitting-debug-information 15 | [profile.dev] 16 | split-debuginfo = "unpacked" 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2022 Expede 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Deterministic Bloom Filter

3 | 4 |

5 | 6 | Crate 7 | 8 | 9 | Npm 10 | 11 | 12 | Code Coverage 13 | 14 | 15 | Build Status 16 | 17 | 18 | License 19 | 20 | 21 | Docs 22 | 23 | 24 | Discord 25 | 26 |

27 |
28 | 29 |
:warning: Work in progress :warning:
30 | 31 | ## Outline 32 | 33 | - [Crates](#crates) 34 | - [Usage](#usage) 35 | - [Testing the Project](#testing-the-project) 36 | - [Benchmarking the Project](#benchmarking-the-project) 37 | - [Setting-up deterministic-bloom-wasm](#setting-up-deterministic-bloom-wasm) 38 | - [Contributing](#contributing) 39 | - [Getting Help](#getting-help) 40 | - [External Resources](#external-resources) 41 | - [License](#license) 42 | 43 | ## Crates 44 | 45 | - [deterministic-bloom](https://github.com/wnfs-wg/deterministic-bloom/tree/main/deterministic-bloom) 46 | - [deterministic-bloom-wasm](https://github.com/wnfs-wg/deterministic-bloom/tree/main/deterministic-bloom-wasm) 47 | 48 | ## Usage 49 | 50 | - Add the following to the `[dependencies]` section of your `Cargo.toml` file 51 | for using the rust-only `deterministic-bloom` crate/workspace: 52 | 53 | ```toml 54 | deterministic-bloom = "0.1.0" 55 | ``` 56 | 57 | - Add the following to the `[dependencies]` section of your `Cargo.toml` file 58 | for using `deterministic-bloom-wasm` crate/workspace: 59 | 60 | ```toml 61 | deterministic-bloom-wasm = "0.1.0" 62 | ``` 63 | 64 | ## Testing the Project 65 | 66 | - Run tests for crate/workspace `deterministic-bloom`: 67 | 68 | ```console 69 | cd deterministic-bloom && cargo test 70 | ``` 71 | 72 | - To run tests for crate/workspace `deterministic-bloom-wasm`, follow 73 | the instructions in [deterministic-bloom-wasm](./deterministic-bloom-wasm#testing-the-project), 74 | which leverages [wasm-pack][wasm-pack]. 75 | 76 | ## Benchmarking the Project 77 | 78 | For benchmarking and measuring performance, this workspaces provides 79 | a Rust-specific benchmarking package leveraging [criterion][criterion] and a 80 | `test_utils` feature flag for integrating [proptest][proptest] within the 81 | suite for working with [strategies][strategies] and sampling from randomly 82 | generated values. 83 | 84 | - Run benchmarks 85 | 86 | ```console 87 | cargo bench -p deterministic-bloom-benches 88 | ``` 89 | 90 | *Note*: Currently, this workspace only supports Rust-native benchmarking, as 91 | `wasm-bindgen` support for criterion is still [an open issue][criterion-bindgen]. 92 | However, with some extra work, benchmarks can be compiled to [wasi][wasi] and 93 | run with [wasmer][wasmer]/[wasmtime][wasmtime] or in the brower with 94 | [webassembly.sh][wasmsh]. Please catch-up with wasm support for criterion on their 95 | [user-guide][criterion-user-guide]. 96 | 97 | ## Setting-up deterministic-bloom-wasm 98 | 99 | The Wasm targetted version of this project relies on [wasm-pack][wasm-pack] 100 | for building, testing, and publishing artifacts sutiable for 101 | [Node.js][node-js], web broswers, or bundlers like [webpack][webpack]. 102 | 103 | Please read more on working with `wasm-pack` directly in 104 | [deterministic-bloom-wasm](./deterministic-bloom-wasm#set-up). 105 | 106 | ## Contributing 107 | 108 | :balloon: We're thankful for any feedback and help in improving our project! 109 | We have a [contributing guide](./CONTRIBUTING.md) to help you get involved. We 110 | also adhere to our [Code of Conduct](./CODE_OF_CONDUCT.md). 111 | 112 | ### Nix 113 | This repository contains a [Nix flake][nix-flake] that initiates both the Rust 114 | toolchain set in [rust-toolchain.toml](./rust-toolchain.toml) and a 115 | [pre-commit hook](#pre-commit-hook). It also installs helpful cargo binaries for 116 | development. Please install [nix][nix] and [direnv][direnv] to get started. 117 | 118 | Run `nix develop` or `direnv allow` to load the `devShell` flake output, 119 | according to your preference. 120 | 121 | ### Formatting 122 | 123 | For formatting Rust in particular, please use `cargo +nightly fmt` as it uses 124 | specific nightly features we recommend by default. 125 | 126 | ### Pre-commit Hook 127 | 128 | This library recommends using [pre-commit][pre-commit] for running pre-commit 129 | hooks. Please run this before every commit and/or push. 130 | 131 | - If you are doing interim commits locally, and for some reason if you _don't_ 132 | want pre-commit hooks to fire, you can run 133 | `git commit -a -m "Your message here" --no-verify`. 134 | 135 | ### Recommended Development Flow 136 | 137 | - We recommend leveraging [cargo-watch][cargo-watch], 138 | [cargo-expand][cargo-expand] and [irust][irust] for Rust development. 139 | - We recommend using [cargo-udeps][cargo-udeps] for removing unused dependencies 140 | before commits and pull-requests. 141 | 142 | ### Conventional Commits 143 | 144 | This project *lightly* follows the [Conventional Commits 145 | convention][commit-spec-site] to help explain 146 | commit history and tie in with our release process. The full specification 147 | can be found [here][commit-spec]. We recommend prefixing your commits with 148 | a type of `fix`, `feat`, `docs`, `ci`, `refactor`, etc..., structured like so: 149 | 150 | ``` 151 | [optional scope]: 152 | 153 | [optional body] 154 | 155 | [optional footer(s)] 156 | ``` 157 | 158 | ## Getting Help 159 | 160 | For usage questions, usecases, or issues reach out to us in our [Discord channel](https://discord.gg/fissioncodes). 161 | 162 | We would be happy to try to answer your question or try opening a new issue on Github. 163 | 164 | ## External Resources 165 | 166 | These are references to specifications, talks and presentations, etc. 167 | 168 | ## License 169 | 170 | This project is licensed under the [Apache License 2.0](./LICENSE), or 171 | [http://www.apache.org/licenses/LICENSE-2.0][apache]. 172 | 173 | 174 | [apache]: https://www.apache.org/licenses/LICENSE-2.0 175 | [cargo-expand]: https://github.com/dtolnay/cargo-expand 176 | [cargo-udeps]: https://github.com/est31/cargo-udeps 177 | [cargo-watch]: https://github.com/watchexec/cargo-watch 178 | [commit-spec]: https://www.conventionalcommits.org/en/v1.0.0/#specification 179 | [commit-spec-site]: https://www.conventionalcommits.org/ 180 | [criterion]: https://github.com/bheisler/criterion.rs 181 | [criterion-bindgen]: https://github.com/bheisler/criterion.rs/issues/270 182 | [direnv]:https://direnv.net/ 183 | [irust]: https://github.com/sigmaSd/IRust 184 | [mit]: http://opensource.org/licenses/MIT 185 | [nix]:https://nixos.org/download.html 186 | [nix-flake]: https://nixos.wiki/wiki/Flakes 187 | [node-js]: https://nodejs.dev/en/ 188 | [pre-commit]: https://pre-commit.com/ 189 | [proptest]: https://github.com/proptest-rs/proptest 190 | [strategies]: https://docs.rs/proptest/latest/proptest/strategy/trait.Strategy.html 191 | [criterion-user-guide]: https://github.com/bheisler/criterion.rs/blob/version-0.4/book/src/user_guide/wasi.md 192 | [wasi]: https://wasi.dev/ 193 | [wasmer]: https://wasmer.io/ 194 | [wasmtime]: https://docs.wasmtime.dev/ 195 | [wasmsh]: https://webassembly.sh/ 196 | [wasm-pack]: https://rustwasm.github.io/docs/wasm-pack/ 197 | [webpack]: https://webpack.js.org/ 198 | -------------------------------------------------------------------------------- /assets/a_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wnfs-wg/deterministic-bloom/62849fa6b3d1731ea4f4cb18b8d81e845404dc10/assets/a_logo.png -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "deterministic-bloom/tests" 3 | - "deterministic-bloom-wasm" 4 | - "deterministic-bloom-benches" 5 | - "examples" 6 | 7 | comment: 8 | layout: "reach, diff, flags, files" 9 | require_changes: true 10 | 11 | github_checks: 12 | annotations: false 13 | 14 | coverage: 15 | status: 16 | project: 17 | default: 18 | threshold: 5% 19 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | # This template contains all of the possible sections and their default values 2 | 3 | # Note that all fields that take a lint level have these possible values: 4 | # * deny - An error will be produced and the check will fail 5 | # * warn - A warning will be produced, but the check will not fail 6 | # * allow - No warning or error will be produced, though in some cases a note 7 | # will be 8 | 9 | # The values provided in this template are the default values that will be used 10 | # when any section or field is not specified in your own configuration 11 | 12 | # If 1 or more target triples (and optionally, target_features) are specified, 13 | # only the specified targets will be checked when running `cargo deny check`. 14 | # This means, if a particular package is only ever used as a target specific 15 | # dependency, such as, for example, the `nix` crate only being used via the 16 | # `target_family = "unix"` configuration, that only having windows targets in 17 | # this list would mean the nix crate, as well as any of its exclusive 18 | # dependencies not shared by any other crates, would be ignored, as the target 19 | # list here is effectively saying which targets you are building for. 20 | targets = [ 21 | # The triple can be any string, but only the target triples built in to 22 | # rustc (as of 1.40) can be checked against actual config expressions 23 | #{ triple = "x86_64-unknown-linux-musl" }, 24 | # You can also specify which target_features you promise are enabled for a 25 | # particular target. target_features are currently not validated against 26 | # the actual valid features supported by the target architecture. 27 | #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, 28 | ] 29 | 30 | # This section is considered when running `cargo deny check advisories` 31 | # More documentation for the advisories section can be found here: 32 | # https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html 33 | [advisories] 34 | # The path where the advisory database is cloned/fetched into 35 | db-path = "~/.cargo/advisory-db" 36 | # The url(s) of the advisory databases to use 37 | db-urls = ["https://github.com/rustsec/advisory-db"] 38 | # The lint level for security vulnerabilities 39 | vulnerability = "deny" 40 | # The lint level for unmaintained crates 41 | unmaintained = "warn" 42 | # The lint level for crates that have been yanked from their source registry 43 | yanked = "deny" 44 | # The lint level for crates with security notices. Note that as of 45 | # 2019-12-17 there are no security notice advisories in 46 | # https://github.com/rustsec/advisory-db 47 | notice = "warn" 48 | # A list of advisory IDs to ignore. Note that ignored advisories will still 49 | # output a note when they are encountered. 50 | ignore = [ 51 | #"RUSTSEC-0000-0000" 52 | ] 53 | # Threshold for security vulnerabilities, any vulnerability with a CVSS score 54 | # lower than the range specified will be ignored. Note that ignored advisories 55 | # will still output a note when they are encountered. 56 | # * None - CVSS Score 0.0 57 | # * Low - CVSS Score 0.1 - 3.9 58 | # * Medium - CVSS Score 4.0 - 6.9 59 | # * High - CVSS Score 7.0 - 8.9 60 | # * Critical - CVSS Score 9.0 - 10.0 61 | #severity-threshold = 62 | 63 | # This section is considered when running `cargo deny check licenses` 64 | # More documentation for the licenses section can be found here: 65 | # https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html 66 | [licenses] 67 | # The lint level for crates which do not have a detectable license 68 | unlicensed = "deny" 69 | # List of explicitly allowed licenses 70 | # See https://spdx.org/licenses/ for list of possible licenses 71 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. 72 | allow = [ 73 | "Apache-2.0", 74 | "BSD-2-Clause", 75 | "BSD-3-Clause", 76 | "BSL-1.0", 77 | "CC0-1.0", 78 | "MIT" 79 | ] 80 | # List of explicitly disallowed licenses 81 | # See https://spdx.org/licenses/ for list of possible licenses 82 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. 83 | deny = [ 84 | #"Nokia", 85 | ] 86 | # Lint level for licenses considered copyleft 87 | copyleft = "deny" 88 | # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses 89 | # * both - The license will be approved if it is both OSI-approved *AND* FSF 90 | # * either - The license will be approved if it is either OSI-approved *OR* FSF 91 | # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF 92 | # * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved 93 | # * neither - This predicate is ignored and the default lint level is used 94 | allow-osi-fsf-free = "neither" 95 | # Lint level used when no other predicates are matched 96 | # 1. License isn't in the allow or deny lists 97 | # 2. License isn't copyleft 98 | # 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" 99 | default = "deny" 100 | # The confidence threshold for detecting a license from license text. 101 | # The higher the value, the more closely the license text must be to the 102 | # canonical license text of a valid SPDX license file. 103 | # [possible values: any between 0.0 and 1.0]. 104 | confidence-threshold = 0.8 105 | # Allow 1 or more licenses on a per-crate basis, so that particular licenses 106 | # aren't accepted for every possible crate as with the normal allow list 107 | exceptions = [ 108 | # The Unicode-DFS-2016 license is necessary for unicode-ident because they 109 | # use data from the unicode tables to generate the tables which are 110 | # included in the application. We do not distribute those data files so 111 | # this is not a problem for us. See https://github.com/dtolnay/unicode-ident/pull/9/files 112 | { allow = ["Unicode-DFS-2016"], name = "unicode-ident", version = "*"}, 113 | ] 114 | 115 | # Some crates don't have (easily) machine readable licensing information, 116 | # adding a clarification entry for it allows you to manually specify the 117 | # licensing information 118 | #[[licenses.clarify]] 119 | # The name of the crate the clarification applies to 120 | #name = "ring" 121 | # The optional version constraint for the crate 122 | #version = "*" 123 | # The SPDX expression for the license requirements of the crate 124 | #expression = "MIT AND ISC AND OpenSSL" 125 | # One or more files in the crate's source used as the "source of truth" for 126 | # the license expression. If the contents match, the clarification will be used 127 | # when running the license check, otherwise the clarification will be ignored 128 | # and the crate will be checked normally, which may produce warnings or errors 129 | # depending on the rest of your configuration 130 | #license-files = [ 131 | # Each entry is a crate relative path, and the (opaque) hash of its contents 132 | #{ path = "LICENSE", hash = 0xbd0eed23 } 133 | #] 134 | 135 | [licenses.private] 136 | # If true, ignores workspace crates that aren't published, or are only 137 | # published to private registries 138 | ignore = true 139 | # One or more private registries that you might publish crates to, if a crate 140 | # is only published to private registries, and ignore is true, the crate will 141 | # not have its license(s) checked 142 | registries = [ 143 | #"https://sekretz.com/registry 144 | ] 145 | 146 | # This section is considered when running `cargo deny check bans`. 147 | # More documentation about the 'bans' section can be found here: 148 | # https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html 149 | [bans] 150 | # Lint level for when multiple versions of the same crate are detected 151 | multiple-versions = "warn" 152 | # Lint level for when a crate version requirement is `*` 153 | wildcards = "allow" 154 | # The graph highlighting used when creating dotgraphs for crates 155 | # with multiple versions 156 | # * lowest-version - The path to the lowest versioned duplicate is highlighted 157 | # * simplest-path - The path to the version with the fewest edges is highlighted 158 | # * all - Both lowest-version and simplest-path are used 159 | highlight = "all" 160 | # List of crates to deny 161 | deny = [ 162 | # Each entry the name of a crate and a version range. If version is 163 | # not specified, all versions will be matched. 164 | #{ name = "ansi_term", version = "=0.11.0" }, 165 | ] 166 | # Certain crates/versions that will be skipped when doing duplicate detection. 167 | skip = [ 168 | #{ name = "ansi_term", version = "=0.11.0" }, 169 | ] 170 | # Similarly to `skip` allows you to skip certain crates during duplicate 171 | # detection. Unlike skip, it also includes the entire tree of transitive 172 | # dependencies starting at the specified crate, up to a certain depth, which is 173 | # by default infinite 174 | skip-tree = [ 175 | #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, 176 | ] 177 | 178 | # This section is considered when running `cargo deny check sources`. 179 | # More documentation about the 'sources' section can be found here: 180 | # https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html 181 | [sources] 182 | # Lint level for what to happen when a crate from a crate registry that is not 183 | # in the allow list is encountered 184 | unknown-registry = "deny" 185 | # Lint level for what to happen when a crate from a git repository that is not 186 | # in the allow list is encountered 187 | unknown-git = "deny" 188 | # List of URLs for allowed crate registries. Defaults to the crates.io index 189 | # if not specified. If it is specified but empty, no registries are allowed. 190 | allow-registry = ["https://github.com/rust-lang/crates.io-index"] 191 | # List of URLs for allowed Git repositories 192 | allow-git = [] 193 | 194 | #[sources.allow-org] 195 | # 1 or more github.com organizations to allow git sources for 196 | #github = [""] 197 | # 1 or more gitlab.com organizations to allow git sources for 198 | #gitlab = [""] 199 | # 1 or more bitbucket.org organizations to allow git sources for 200 | #bitbucket = [""] 201 | -------------------------------------------------------------------------------- /deterministic-bloom-benches/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deterministic-bloom-benches" 3 | version = "0.1.0" 4 | publish = false 5 | edition = "2021" 6 | authors = ["Brooklyn Zelenka "] 7 | 8 | [dependencies] 9 | deterministic-bloom = { path = "../deterministic-bloom", version = "0.1" } 10 | rand = "0.8" 11 | 12 | [dev-dependencies] 13 | criterion = { version = "0.4", default-features = false } 14 | 15 | [[bench]] 16 | name = "bloom_filter_benchmark" 17 | harness = false 18 | -------------------------------------------------------------------------------- /deterministic-bloom-benches/benches/bloom_filter_benchmark.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use deterministic_bloom::const_size::BloomFilter; 3 | use rand::Rng; 4 | 5 | pub fn add_benchmark(crit: &mut Criterion) { 6 | let mut rng = rand::thread_rng(); 7 | let mut bloom = BloomFilter::<256, 30>::new(); 8 | 9 | crit.bench_function("add", |bench| { 10 | bench.iter(|| { 11 | let new_val: u8 = rng.gen_range(0..10); 12 | bloom.insert(&[new_val; 32]); 13 | }) 14 | }); 15 | } 16 | 17 | pub fn count_ones_benchmark(crit: &mut Criterion) { 18 | let mut rng = rand::thread_rng(); 19 | let mut bloom = BloomFilter::<256, 30>::new(); 20 | 21 | for _ in 1..50 { 22 | let new_val: u8 = rng.gen_range(0..10); 23 | bloom.insert(&[new_val; 32]); 24 | } 25 | 26 | crit.bench_function("count_ones", |bench| bench.iter(|| bloom.count_ones())); 27 | } 28 | 29 | criterion_group!(benches, add_benchmark, count_ones_benchmark); 30 | criterion_main!(benches); 31 | -------------------------------------------------------------------------------- /deterministic-bloom-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deterministic-bloom-wasm" 3 | version = "0.1.0" 4 | description = "Wasm bindings for the deterministic-bloom crate." 5 | keywords = [] 6 | categories = [] 7 | include = ["/src", "README.md", "LICENSE"] 8 | license = "Apache-2.0" 9 | readme = "README.md" 10 | edition = "2021" 11 | rust-version = "1.64" 12 | documentation = "https://docs.rs/deterministic-bloom-wasm" 13 | repository = "https://github.com/wnfs-wg/deterministic-bloom/tree/main/deterministic-bloom-wasm" 14 | authors = ["Brooklyn Zelenka "] 15 | 16 | [lib] 17 | crate-type = ["cdylib", "rlib"] 18 | path = "src/lib.rs" 19 | 20 | [dependencies] 21 | # The `console_error_panic_hook` crate provides better debugging of panics by 22 | # logging them with `console.error`. This is great for development, but requires 23 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for 24 | # code size when deploying. 25 | console_error_panic_hook = { version = "0.1", optional = true } 26 | derive_more = "0.99" 27 | deterministic-bloom = { path = "../deterministic-bloom", version = "0.1" } 28 | js-sys = { version = "0.3", optional = true } 29 | once_cell = "1.16" 30 | thiserror = "1.0" 31 | tracing = "0.1" 32 | wasm-bindgen = { version = "0.2", optional = true, features = ["serde-serialize"] } 33 | wasm-bindgen-futures = { version = "0.4", optional = true } 34 | web-sys = { version = "0.3", optional = true } 35 | 36 | [dev-dependencies] 37 | wasm-bindgen-test = "0.3" 38 | 39 | [features] 40 | default = ["js"] 41 | full = ["js", "web"] 42 | js = [ 43 | "console_error_panic_hook", 44 | "js-sys", 45 | "wasm-bindgen", 46 | "wasm-bindgen-futures" 47 | ] 48 | web = ["web-sys"] 49 | 50 | [profile.release] 51 | lto = true 52 | -------------------------------------------------------------------------------- /deterministic-bloom-wasm/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /deterministic-bloom-wasm/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | deterministic-bloom Logo 4 | 5 | 6 |

deterministic-bloom-wasm

7 | 8 |

9 | 10 | Crate 11 | 12 | 13 | npm 14 | 15 | 16 | Code Coverage 17 | 18 | 19 | Build Status 20 | 21 | 22 | License 23 | 24 | 25 | Docs 26 | 27 | 28 | Discord 29 | 30 |

31 |
32 | 33 |
:warning: Work in progress :warning:
34 | 35 | ## 36 | 37 | Description. 38 | 39 | ## Outline 40 | 41 | - [Set-up](#set-up) 42 | - [Build for Javascript](#build-for-javascript) 43 | - [Testing the Project](#testing-the-project) 44 | - [Publishing a Package](#publishing-a-package) 45 | - [License](#license) 46 | 47 | ## Set-up 48 | 49 | We'll use [`wasm-pack`][wasm-pack] for building, testing, and publishing 50 | our Wasm project. 51 | 52 | ### Build for Javascript 53 | 54 | The `wasm-pack build` command will compile the code in this directory into 55 | Wasm and generate a `pkg` folder by default, containing the Wasm binary, a 56 | Javascript-wrapper file, the deterministic-bloom-wasm README (and version), and a 57 | `package.json` file. 58 | 59 | - Targetting node: 60 | 61 | ```console 62 | wasm-pack build --target nodejs 63 | ``` 64 | 65 | - Targetting browswers: 66 | 67 | ```console 68 | wasm-pack build --target web 69 | ``` 70 | 71 | - Targetting bundlers like [webpack][webpack]: 72 | 73 | ```console 74 | wasm-pack build --target bundler 75 | ``` 76 | 77 | ## Testing the Project 78 | 79 | For running tests in the current directory, use one of these commands: 80 | 81 | - Run tests expected to execute in [Node.js][node-js]: 82 | 83 | ```console 84 | wasm-pack test --node 85 | ``` 86 | 87 | - Run browser tests in a headless browwer: 88 | 89 | ```console 90 | wasm-pack test --headless --firefox --chrome --safari 91 | ``` 92 | 93 | *Note*: Make sure you have the appropriate browser installed when running 94 | locally. 95 | 96 | ## Publishing a Package 97 | 98 | Once you've [built the package](#build-for-javascript), which lives under 99 | `pkg` by default (or a sub-directory of your choosing), you can pack and 100 | publish it to [npm][npm] via (given credentials): 101 | 102 | ```console 103 | wasm-pack publish 104 | ``` 105 | 106 | ## License 107 | 108 | This project is licensed under the [Apache License 2.0](./LICENSE), or 109 | [http://www.apache.org/licenses/LICENSE-2.0][apache]. 110 | 111 | 112 | [apache]: https://www.apache.org/licenses/LICENSE-2.0 113 | [mit]: http://opensource.org/licenses/MIT 114 | [node-js]: https://nodejs.dev/en/ 115 | [npm]: https://www.npmjs.com/ 116 | [wasm-pack]: https://rustwasm.github.io/docs/wasm-pack/ 117 | [webpack]: https://webpack.js.org/ 118 | -------------------------------------------------------------------------------- /deterministic-bloom-wasm/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(docsrs, feature(doc_cfg))] 2 | #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)] 3 | #![deny(unreachable_pub, private_in_public)] 4 | 5 | //! Wasm/JS bindings for [BloomFilter] 6 | 7 | use derive_more::{From, Into}; 8 | use deterministic_bloom::const_size::BloomFilter; 9 | use std::boxed::Box; 10 | use wasm_bindgen::prelude::{wasm_bindgen, JsError}; 11 | 12 | //------------------------------------------------------------------------------ 13 | // Utilities 14 | //------------------------------------------------------------------------------ 15 | 16 | /// Panic hook lets us get better error messages if our Rust code ever panics. 17 | /// 18 | /// For more details see 19 | /// 20 | #[wasm_bindgen(js_name = "setPanicHook")] 21 | pub fn set_panic_hook() { 22 | #[cfg(feature = "console_error_panic_hook")] 23 | console_error_panic_hook::set_once(); 24 | } 25 | 26 | //------------------------------------------------------------------------------ 27 | // Macros 28 | //------------------------------------------------------------------------------ 29 | 30 | /// Generate a monomorphic [BloomFilter] newtypes. 31 | macro_rules! gen_bloom { 32 | ($name: ident, $n: expr, $k: expr) => { 33 | #[doc = concat!("A monomorphic wrapper for [BloomFilter]`s with a size of ", stringify!($n), " bytes and ", stringify!($k), " hash functions.")] 34 | #[wasm_bindgen] 35 | #[derive(Debug, Default, From, Into)] 36 | pub struct $name { 37 | pub(crate) boxed: Box>, 38 | } 39 | 40 | #[wasm_bindgen] 41 | impl $name { 42 | #[doc = concat!("Initialize a blank [", stringify!($name), "] (i.e. contains no elements).")] 43 | /// 44 | /// # Examples 45 | /// 46 | /// ``` 47 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")] 48 | /// 49 | #[doc = concat!("let blank = ", stringify!($name), "::new();")] 50 | #[doc = concat!("assert_eq!(", stringify!($name), "::count_ones(&blank), 0);")] 51 | /// ``` 52 | #[wasm_bindgen(constructor)] 53 | pub fn new() -> Self { 54 | Default::default() 55 | } 56 | 57 | #[doc = concat!("Attempt to initialize a [", stringify!($name), "] with a starting array.")] 58 | #[doc = concat!("Fails with a [JsError] if the [Vec] is not exactly ", stringify!($n), " bytes long.")] 59 | pub fn try_from_vec(vec: Vec) -> Result<$name, JsError> { 60 | $name::try_from(vec).map_err(|e| JsError::new(&e.to_string())) 61 | } 62 | 63 | /// The (constant) size of the underlying [BloomFilter] in bytes. 64 | /// 65 | /// # Examples 66 | /// 67 | /// ``` 68 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")] 69 | /// 70 | #[doc = concat!("let size = ", stringify!($name), "::byte_count();")] 71 | #[doc = concat!("assert_eq!(size, ", stringify!($k), ");")] 72 | /// ``` 73 | pub fn byte_count() -> usize { 74 | $k 75 | } 76 | 77 | /// The number of hashes used in the underlying [BloomFilter]. 78 | /// 79 | /// # Examples 80 | /// 81 | /// ``` 82 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")] 83 | /// 84 | #[doc = concat!("assert_eq!(", stringify!($name), "::hash_count(), ", stringify!($n), ");")] 85 | /// ``` 86 | pub fn hash_count() -> usize { 87 | $n 88 | } 89 | 90 | /// Insert a new elememt into the underlying [BloomFilter]. 91 | /// This [Vec] can be of any length. 92 | /// 93 | /// # Examples 94 | /// 95 | /// ``` 96 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")] 97 | /// 98 | #[doc = concat!("let mut bloom = ", stringify!($name), "::new();")] 99 | /// let item = vec![1, 2, 3, 4, 5]; 100 | /// bloom.insert_vec(item.clone()); 101 | /// 102 | /// assert!(bloom.contains(item.clone())); 103 | /// ``` 104 | pub fn insert_vec(&mut self, new_val: Vec) -> () { 105 | self.boxed.insert(&new_val); 106 | } 107 | 108 | /// Check if some [Vec] is in the underlying [BloomFilter]. 109 | /// 110 | /// # Examples 111 | /// 112 | /// ``` 113 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")] 114 | /// 115 | #[doc = concat!("let mut bloom = ", stringify!($name), "::new();")] 116 | /// let item = vec![1, 2, 3, 4, 5]; 117 | /// bloom.insert_vec(item.clone()); 118 | /// 119 | /// assert!(bloom.contains(item.clone())); 120 | /// ``` 121 | pub fn contains(&self, item: Vec) -> bool { 122 | self.boxed.contains(&item) 123 | } 124 | 125 | /// Count how many bits are set to 1 (sometimes called a `popcount`). 126 | /// 127 | /// # Examples 128 | /// 129 | /// ``` 130 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")] 131 | /// 132 | #[doc = concat!("let mut bloom = ", stringify!($name), "::new();")] 133 | /// let item1 = vec![1, 2, 3, 4, 5]; 134 | /// bloom.insert_vec(item1.clone()); 135 | /// bloom.insert_vec(item1.clone()); 136 | /// bloom.insert_vec(item1.clone()); 137 | /// 138 | /// let item2 = vec![6, 7]; 139 | /// bloom.insert_vec(item2.clone()); 140 | /// 141 | /// let item3 = vec![8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; 142 | /// bloom.insert_vec(item2.clone()); 143 | /// 144 | #[doc = concat!("assert!(bloom.count_ones() >= ", $k, ");")] 145 | #[doc = concat!("assert!(bloom.count_ones() <= 3 * ", $k, ");")] 146 | /// ``` 147 | pub fn count_ones(&self) -> usize { 148 | self.boxed.count_ones() 149 | } 150 | 151 | /// Retreive the underlying byte array. 152 | /// 153 | /// # Examples 154 | /// 155 | /// ``` 156 | #[doc = concat!("use deterministic_bloom_wasm::", stringify!($name), ";")] 157 | /// 158 | #[doc = concat!("let mut bloom = ", stringify!($name), "::new();")] 159 | /// bloom.insert_vec(vec![1, 2, 3, 4, 5]); 160 | /// 161 | #[doc = concat!("assert_ne!(bloom.as_bytes(), vec![0; ", $n, "]);")] 162 | #[doc = concat!("assert_eq!(bloom.as_bytes().len(), ", $n, ");")] 163 | /// ``` 164 | pub fn as_bytes(&self) -> Vec { 165 | self.boxed.as_bytes().to_vec() 166 | } 167 | } 168 | 169 | impl From> for $name { 170 | fn from(bloom: BloomFilter<$n, $k>) -> Self { 171 | $name { 172 | boxed: Box::new(bloom) 173 | } 174 | } 175 | } 176 | 177 | impl TryFrom> for $name { 178 | type Error = deterministic_bloom::common::Error; 179 | 180 | fn try_from(vec: Vec) -> Result { 181 | >::try_from(vec).map($name::from) 182 | } 183 | } 184 | }; 185 | } 186 | 187 | gen_bloom!(SmallBloomFilter, 256, 13); 188 | gen_bloom!(MediumBloomFilter, 4096, 17); 189 | gen_bloom!(LargeBloomFilter, 1048576, 23); 190 | -------------------------------------------------------------------------------- /deterministic-bloom-wasm/tests/web.rs: -------------------------------------------------------------------------------- 1 | //! Test suite for the Web and headless browsers. 2 | 3 | #![cfg(target_arch = "wasm32")] 4 | 5 | // use deterministic_bloom::{test_utils::Rvg, BloomFilter}; 6 | use deterministic_bloom_wasm::SmallBloomFilter; 7 | use wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure}; 8 | wasm_bindgen_test_configure!(run_in_browser); 9 | 10 | #[wasm_bindgen_test] 11 | fn test_contains() { 12 | let mut bloom = SmallBloomFilter::new(); 13 | // let mut rvg = Rvg::new(); 14 | 15 | let new_val: Vec = vec![1, 2, 3]; // rvg.sample(&(..255u8))]; 16 | bloom.insert_vec(new_val.clone()); 17 | 18 | for _ in 1..25 { 19 | bloom.insert_vec(vec![4, 5, 6]); // rvg.sample(&(..255u8))]); 20 | } 21 | 22 | assert!(bloom.contains(new_val.clone())); 23 | } 24 | -------------------------------------------------------------------------------- /deterministic-bloom/.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | 3 | !Cargo.lock 4 | !Cargo.toml 5 | !src 6 | 7 | src/bin 8 | -------------------------------------------------------------------------------- /deterministic-bloom/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "deterministic-bloom" 3 | version = "0.1.0" 4 | description = "A deterministic Bloom filter with support for saturation. Suitable for distributed use cases and as a cryptographic primitive." 5 | keywords = ["bloom"] 6 | categories = [] 7 | license = "Apache-2.0" 8 | include = ["/src", "README.md", "LICENSE"] 9 | readme = "README.md" 10 | edition = "2021" 11 | rust-version = "1.64" 12 | documentation = "https://docs.rs/deterministic-bloom" 13 | repository = "https://github.com/wnfs-wg/deterministic-bloom/tree/main/deterministic-bloom" 14 | authors = ["Philipp Krüger ", "Stephen Akinyemi ", "Brooklyn Zelenka "] 15 | 16 | [lib] 17 | path = "src/lib.rs" 18 | 19 | [dependencies] 20 | bitvec = { version = "1.0", features = ["serde"] } 21 | miette = "5.5" 22 | proptest = { version = "1.0", optional = true } 23 | rand_core = "0.6" 24 | serde = { version = "1.0", features = ["rc"] } 25 | thiserror = "1.0" 26 | tracing = "0.1" 27 | xxhash-rust = { version = "0.8", features = ["xxh3"] } 28 | 29 | [dev-dependencies] 30 | libipld = { version = "0.16", features = ["serde-codec"] } 31 | proptest = "1.0" 32 | rand = "0.8" 33 | test-strategy = "0.3" 34 | 35 | [features] 36 | default = [] 37 | test_utils = ["proptest"] 38 | -------------------------------------------------------------------------------- /deterministic-bloom/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /deterministic-bloom/README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | deterministic-bloom Logo 4 | 5 | 6 |

deterministic-bloom

7 | 8 |

9 | 10 | Crate 11 | 12 | 13 | Code Coverage 14 | 15 | 16 | Build Status 17 | 18 | 19 | License 20 | 21 | 22 | Docs 23 | 24 | 25 | Discord 26 | 27 |

28 |
29 | 30 |
:warning: Work in progress :warning:
31 | 32 | ## 33 | 34 | Description. 35 | 36 | ## License 37 | 38 | This project is licensed under the [Apache License 2.0](./LICENSE), or 39 | [http://www.apache.org/licenses/LICENSE-2.0][apache]. 40 | 41 | 42 | [apache]: https://www.apache.org/licenses/LICENSE-2.0 43 | [mit]: http://opensource.org/licenses/MIT 44 | -------------------------------------------------------------------------------- /deterministic-bloom/proptest-regressions/lib.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 291d42539f7fb8127033c08ad7c09dba8c1b74a026ba9af55d7c1cbfb3673e80 # shrinks to input = _BloomParamsFprCalcRoundTripsArgs { bloom_bytes: 1, n_elems: 1, fpr: 0.4123907350873679 } 8 | cc a4e3f11f5e30029214f9bca6d33a195cbeeeac10f7c128da8f2124cc5ac1920b # shrinks to input = _BloomParamsFprCalcRoundTripsArgs { n_elems: 548, fpr: 0.8964377231312348 } 9 | -------------------------------------------------------------------------------- /deterministic-bloom/src/common.rs: -------------------------------------------------------------------------------- 1 | use std::{f64::consts::LN_2, fmt::Debug}; 2 | use xxhash_rust::xxh3; 3 | 4 | /// An iterator that generates indices into some bloom filter based on deterministic hashing of specified item. 5 | /// 6 | /// # Examples 7 | /// 8 | /// ``` 9 | /// use deterministic_bloom::const_size::BloomFilter; 10 | /// 11 | /// let filter = BloomFilter::<256, 30>::default(); 12 | /// let indices = filter.hash_indices(&[0xF5u8; 32]); 13 | /// let indices = indices.collect::>(); 14 | /// 15 | /// assert_eq!(indices.len(), 30); 16 | /// ``` 17 | #[derive(Debug, Clone, PartialEq, Eq)] 18 | pub struct HashIndexIterator<'a, T: AsRef<[u8]>> { 19 | item: &'a T, 20 | bit_size: usize, 21 | index: u64, 22 | } 23 | 24 | /// Optimal bloom parameters for some false positive rate at a maximum number of 25 | /// elements added, or for some byte size with target element count, etc. 26 | /// 27 | /// Captures the bloom filter byte size needed as well as the number of hash function 28 | /// evaluations needed per item to insert. 29 | /// 30 | /// To construct this, use 31 | /// - [`BloomParams::new_from_fpr`] for constructing this from a given false positive rate and desired capacity, 32 | /// - similarly [`BloomParams::new_from_fpr_po2`], but with power-of-two sizes, 33 | /// - [`BloomParams::new_from_size`] for constructing from desired size and capacity. 34 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] 35 | pub struct BloomParams { 36 | /// size of the bloom filter in bytes, non-zero 37 | pub byte_size: usize, 38 | /// hashing functions used/number of bits set per element, non-zero 39 | pub k_hashes: usize, 40 | } 41 | 42 | /// Errors for [BloomFilter] operations. 43 | #[derive(thiserror::Error, miette::Diagnostic, Debug)] 44 | pub enum Error { 45 | /// Report a size mismatch when importing a Bloom filter from a [Vec]. 46 | #[error("Cannot convert vector to BloomFilter: expected {expected}, but got {actual}")] 47 | #[diagnostic(url(docsrs))] 48 | VectorImportSizeMismatch { 49 | /// The expected size in the [BloomFilter]. 50 | expected: usize, 51 | 52 | /// The actual size of the [Vec]. 53 | actual: usize, 54 | }, 55 | } 56 | 57 | //------------------------------------------------------------------------------ 58 | // Implementations 59 | //------------------------------------------------------------------------------ 60 | 61 | impl<'a, T: AsRef<[u8]>> HashIndexIterator<'a, T> { 62 | /// Creates a new iterator. 63 | pub fn new(item: &'a T, bit_size: usize) -> Self { 64 | Self { 65 | item, 66 | index: 0, 67 | bit_size, 68 | } 69 | } 70 | } 71 | 72 | impl> Iterator for HashIndexIterator<'_, T> { 73 | type Item = usize; 74 | 75 | fn next(&mut self) -> Option { 76 | if self.bit_size == 0 { 77 | // This avoids an infinite loop in rejection sampling. 78 | return None; 79 | } 80 | 81 | let bit_size_po2 = self.bit_size.next_power_of_two(); 82 | loop { 83 | let hash = xxh3::xxh3_64_with_seed(self.item.as_ref(), self.index) as usize; 84 | self.index += 1; 85 | 86 | // Rejection sampling for non-power-of-two bit sizes 87 | let value = hash % bit_size_po2; 88 | if value < self.bit_size { 89 | return Some(value); 90 | } 91 | } 92 | } 93 | } 94 | 95 | impl BloomParams { 96 | /// Construct optimal bloom parameters for given number maximum elements 97 | /// that the bloom filter will hold as well as the approximate 98 | /// false positive rate it should have at that capacity. 99 | /// 100 | /// `n_elems` must be non-zero, and `fpr` must be between 0 and 1, exclusive. 101 | /// 102 | /// This will generate non-power-of-two sizes for bloom filters. 103 | /// For a variant that power-of-two (po2) sizes, see [`BloomParams::new_from_fpr_po2`]. 104 | /// 105 | /// # Example 106 | /// 107 | /// ``` 108 | /// use deterministic_bloom::common::BloomParams; 109 | /// 110 | /// // figure out bloom parameters for 47 elements with a one in a billion false positive rate: 111 | /// let params = BloomParams::new_from_fpr(47, 1.0 / 1_000_000_000.0); 112 | /// assert_eq!(params, BloomParams { 113 | /// byte_size: 254, 114 | /// k_hashes: 30, 115 | /// }) 116 | /// ``` 117 | pub fn new_from_fpr(n_elems: u64, fpr: f64) -> Self { 118 | let byte_size = Self::optimal_byte_size(n_elems, fpr); 119 | let k_hashes = Self::optimal_k_hashes(byte_size * 8, n_elems); 120 | 121 | Self { 122 | byte_size, 123 | k_hashes, 124 | } 125 | } 126 | 127 | /// Construct optimal bloom parameters for given capacity `n_elems` and false positive rate, 128 | /// where the target size will always be a power-of-two. 129 | /// 130 | /// `n_elems` must be non-zero, and `fpr` must be between 0.0 and 1.0, exclusive. 131 | /// 132 | /// It is often desirable to go for power-of-two sizes, since that simplifies generating 133 | /// bit indices by not requiring rejection sampling. 134 | /// 135 | /// # Example 136 | /// 137 | /// ``` 138 | /// use deterministic_bloom::common::BloomParams; 139 | /// 140 | /// // Generate some bloom parameters 141 | /// let params = BloomParams::new_from_fpr_po2(1_000_000, 0.0001); 142 | /// assert_eq!(params.byte_size, params.byte_size.next_power_of_two()); 143 | /// ``` 144 | pub fn new_from_fpr_po2(n_elems: u64, fpr: f64) -> Self { 145 | let byte_size = Self::optimal_byte_size(n_elems, fpr).next_power_of_two(); 146 | let k_hashes = Self::optimal_k_hashes(byte_size * 8, n_elems); 147 | 148 | Self { 149 | byte_size, 150 | k_hashes, 151 | } 152 | } 153 | 154 | /// Construct optimal bloom parameters for given bloom filter `byte_size` and capacity `n_elems`. 155 | pub fn new_from_size(byte_size: usize, n_elems: u64) -> Self { 156 | Self { 157 | byte_size, 158 | k_hashes: Self::optimal_k_hashes(byte_size * 8, n_elems), 159 | } 160 | } 161 | 162 | /// Compute the approximate false positive rate at `n_elems`. 163 | /// `n_elems` must be non-zero. 164 | /// 165 | /// Returns the false positive rate as a number between 0.0 and 1.0. 166 | pub fn false_positive_rate_at(&self, n_elems: u64) -> f64 { 167 | debug_assert!(n_elems != 0); 168 | 169 | let k = self.k_hashes as f64; 170 | let ki = self.k_hashes as i32; 171 | let m = (self.byte_size * 8) as f64; 172 | let n = n_elems as f64; 173 | 174 | // see https://hur.st/bloomfilter/ 175 | (1.0 - (-k / (m / n)).exp()).powi(ki) 176 | } 177 | 178 | fn optimal_byte_size(n_elems: u64, fpr: f64) -> usize { 179 | debug_assert!(n_elems != 0); 180 | debug_assert!(fpr > 0.0 && fpr < 1.0); 181 | 182 | let n = n_elems as f64; 183 | let bit_size = n * fpr.ln() / -(LN_2 * LN_2); 184 | (bit_size / 8.0).ceil() as usize 185 | } 186 | 187 | fn optimal_k_hashes(bloom_bits: usize, n_elems: u64) -> usize { 188 | debug_assert!(bloom_bits != 0); 189 | debug_assert!(n_elems != 0); 190 | 191 | let m = bloom_bits as f64; 192 | let n = n_elems as f64; 193 | let k_hashes = ((m / n) * LN_2).ceil() as usize; 194 | std::cmp::max(k_hashes, 1) 195 | } 196 | } 197 | 198 | #[cfg(test)] 199 | mod tests { 200 | use super::HashIndexIterator; 201 | 202 | #[test] 203 | fn test_zero_bit_size() { 204 | let mut iterator = HashIndexIterator::new(&[1, 2, 3], 0); 205 | assert_eq!(iterator.next(), None); 206 | } 207 | } 208 | 209 | #[cfg(test)] 210 | mod proptests { 211 | use super::BloomParams; 212 | use proptest::prop_assert; 213 | use test_strategy::proptest; 214 | 215 | #[proptest(cases = 10_000)] 216 | fn bloom_params_fpr_calc_round_trips( 217 | #[strategy(100u64..1_000_000)] n_elems: u64, 218 | #[strategy(0.0..0.1)] fpr: f64, 219 | ) { 220 | if fpr == 0.0 { 221 | return Ok(()); 222 | } 223 | 224 | let params = BloomParams::new_from_fpr(n_elems, fpr); 225 | let fpr_computed = params.false_positive_rate_at(n_elems); 226 | 227 | // The computed FPR can differ from the target FPR due to 228 | // rounding errors and the fact that only multiple-of-8 229 | // bloom sizes are allowed. 230 | let fpr_diff = (fpr_computed - fpr).abs(); 231 | // We're fine if it's within 15% of a margin-of-error. 232 | prop_assert!(fpr_diff < fpr * 0.15); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /deterministic-bloom/src/const_size.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | common::{Error, HashIndexIterator}, 3 | utils::{ByteArrayVisitor, HexFieldDebug}, 4 | }; 5 | use bitvec::prelude::BitArray; 6 | use serde::{Deserialize, Serialize}; 7 | use std::{fmt::Debug, ops::Index}; 8 | 9 | //------------------------------------------------------------------------------ 10 | // Type Definitions 11 | //------------------------------------------------------------------------------ 12 | 13 | /// The bloom filter is a probabilistic data structure that can be used to store a set of hashes. 14 | /// 15 | /// `N` is the size of the bloom filter in bytes. 16 | /// 17 | /// `K` is the number of bits to be set with each insert operation. 18 | /// 19 | /// # Examples 20 | /// 21 | /// ``` 22 | /// use deterministic_bloom::const_size::BloomFilter; 23 | /// 24 | /// let mut filter = BloomFilter::<256, 30>::default(); 25 | /// filter.insert(&[0xF5u8; 32]); 26 | /// 27 | /// assert!(filter.contains(&[0xF5u8; 32])); 28 | /// ``` 29 | #[derive(Clone, PartialEq, Eq, PartialOrd)] 30 | pub struct BloomFilter { 31 | /// The underlying `BitArray` 32 | pub bits: BitArray<[u8; N]>, 33 | } 34 | 35 | //------------------------------------------------------------------------------ 36 | // Implementations 37 | //------------------------------------------------------------------------------ 38 | 39 | impl BloomFilter { 40 | /// Creates a new bloom filter with all bits unset. 41 | /// 42 | /// # Examples 43 | /// 44 | /// ``` 45 | /// use deterministic_bloom::const_size::BloomFilter; 46 | /// 47 | /// let mut filter = BloomFilter::<256, 30>::new(); 48 | /// filter.insert(&[0xF5u8; 32]); 49 | /// 50 | /// assert!(filter.contains(&[0xF5u8; 32])); 51 | /// ``` 52 | pub fn new() -> Self { 53 | Self { 54 | bits: Default::default(), 55 | } 56 | } 57 | 58 | /// Inserts an item to the bloom filter. 59 | /// 60 | /// # Examples 61 | /// 62 | /// ``` 63 | /// use deterministic_bloom::const_size::BloomFilter; 64 | /// 65 | /// let mut filter = BloomFilter::<256, 30>::default(); 66 | /// filter.insert(&[0xF5u8; 32]); 67 | /// 68 | /// assert!(filter.contains(&[0xF5u8; 32])); 69 | /// ``` 70 | pub fn insert(&mut self, item: &T) 71 | where 72 | T: AsRef<[u8]>, 73 | { 74 | for i in self.hash_indices(item) { 75 | self.bits.set(i, true); 76 | } 77 | } 78 | 79 | /// Returns the number of hash iterations the bloom filter uses to set bits. 80 | /// 81 | /// # Examples 82 | /// 83 | /// ``` 84 | /// use deterministic_bloom::const_size::BloomFilter; 85 | /// 86 | /// let mut filter = BloomFilter::<256, 30>::default(); 87 | /// 88 | /// assert_eq!(filter.hash_count(), 30); 89 | /// ``` 90 | pub const fn hash_count(&self) -> usize { 91 | K 92 | } 93 | 94 | /// Checks if the item is in the bloom filter. 95 | /// 96 | /// # Examples 97 | /// 98 | /// ``` 99 | /// use deterministic_bloom::const_size::BloomFilter; 100 | /// 101 | /// let mut filter = BloomFilter::<256, 30>::default(); 102 | /// filter.insert(&[0xF5u8; 32]); 103 | /// 104 | /// assert!(filter.contains(&[0xF5u8; 32])); 105 | /// ``` 106 | pub fn contains(&self, item: &T) -> bool 107 | where 108 | T: AsRef<[u8]>, 109 | { 110 | self.hash_indices(item).all(|i| self.bits[i]) 111 | } 112 | 113 | /// Counts the number of bits set in the bloom filter. 114 | /// 115 | /// # Examples 116 | /// 117 | /// ``` 118 | /// use deterministic_bloom::const_size::BloomFilter; 119 | /// 120 | /// let mut filter = BloomFilter::<256, 30>::default(); 121 | /// filter.insert(&[0xF5u8; 32]); 122 | /// 123 | /// assert_eq!(filter.count_ones(), 30); 124 | /// ``` 125 | pub fn count_ones(&self) -> usize { 126 | self.bits.count_ones() 127 | } 128 | 129 | /// Returns the indices of the bits that would be set if the item was inserted to the bloom filter. 130 | /// 131 | /// # Examples 132 | /// 133 | /// ``` 134 | /// use deterministic_bloom::const_size::BloomFilter; 135 | /// 136 | /// let filter = BloomFilter::<256, 30>::default(); 137 | /// let indices = filter.hash_indices(&[0xF5u8; 32]); 138 | /// let indices = indices.collect::>(); 139 | /// 140 | /// assert_eq!(indices.len(), 30); 141 | /// ``` 142 | #[inline] 143 | pub fn hash_indices<'a, T>(&self, item: &'a T) -> impl Iterator + 'a 144 | where 145 | T: AsRef<[u8]>, 146 | { 147 | HashIndexIterator::new(item, N * 8).take(self.hash_count()) 148 | } 149 | 150 | /// Get the bytes of the bloom filter. 151 | /// 152 | /// # Examples 153 | /// 154 | /// ``` 155 | /// use deterministic_bloom::const_size::BloomFilter; 156 | /// 157 | /// let mut filter = BloomFilter::<256, 30>::default(); 158 | /// filter.insert(&[0xF5u8; 32]); 159 | /// 160 | /// let bytes = filter.as_bytes(); 161 | /// assert_eq!(bytes.len(), 256); 162 | /// ``` 163 | #[inline] 164 | pub fn as_bytes(&self) -> &[u8] { 165 | self.bits.as_raw_slice() 166 | } 167 | } 168 | 169 | impl TryFrom> for BloomFilter { 170 | type Error = Error; 171 | 172 | fn try_from(bytes: Vec) -> Result { 173 | let bits = BitArray::<[u8; N]>::new(bytes.try_into().map_err(|vec: Vec| { 174 | Error::VectorImportSizeMismatch { 175 | expected: N, 176 | actual: vec.len(), 177 | } 178 | })?); 179 | 180 | Ok(Self { bits }) 181 | } 182 | } 183 | 184 | impl Index for BloomFilter { 185 | type Output = bool; 186 | 187 | fn index(&self, index: usize) -> &Self::Output { 188 | &self.bits[index] 189 | } 190 | } 191 | 192 | impl Default for BloomFilter { 193 | #[inline] 194 | fn default() -> Self { 195 | Self::new() 196 | } 197 | } 198 | 199 | impl Serialize for BloomFilter { 200 | fn serialize(&self, serializer: S) -> Result 201 | where 202 | S: serde::Serializer, 203 | { 204 | serializer.serialize_bytes(self.bits.as_raw_slice()) 205 | } 206 | } 207 | 208 | impl<'de, const N: usize, const K: usize> Deserialize<'de> for BloomFilter { 209 | fn deserialize(deserializer: D) -> Result 210 | where 211 | D: serde::Deserializer<'de>, 212 | { 213 | Ok(BloomFilter:: { 214 | bits: BitArray::<[u8; N]>::new(deserializer.deserialize_bytes(ByteArrayVisitor::)?), 215 | }) 216 | } 217 | } 218 | 219 | impl AsRef<[u8]> for &BloomFilter { 220 | fn as_ref(&self) -> &[u8] { 221 | self.as_bytes() 222 | } 223 | } 224 | 225 | impl Debug for BloomFilter { 226 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 227 | f.debug_tuple("BloomFilter") 228 | .field(&HexFieldDebug(self)) 229 | .finish() 230 | } 231 | } 232 | 233 | //------------------------------------------------------------------------------ 234 | // Tests 235 | //------------------------------------------------------------------------------ 236 | 237 | #[cfg(test)] 238 | mod tests { 239 | use super::*; 240 | 241 | #[test] 242 | fn bloom_filter_can_insert_and_validate_item_existence() { 243 | let mut bloom = BloomFilter::<256, 30>::new(); 244 | let items: Vec = vec!["first".into(), "second".into(), "third".into()]; 245 | items.iter().for_each(|item| { 246 | bloom.insert(item); 247 | }); 248 | 249 | items.iter().for_each(|item| { 250 | assert!(bloom.contains(item)); 251 | }); 252 | 253 | assert!(!bloom.contains(b"irst")); 254 | assert!(!bloom.contains(b"secnd")); 255 | assert!(!bloom.contains(b"tird")); 256 | } 257 | 258 | #[test] 259 | fn serialized_bloom_filter_can_be_deserialized_correctly() { 260 | let mut bloom = BloomFilter::<256, 30>::new(); 261 | let items: Vec = vec!["first".into(), "second".into(), "third".into()]; 262 | items.iter().for_each(|item| { 263 | bloom.insert(item); 264 | }); 265 | 266 | let ipld = libipld::serde::to_ipld(&bloom).unwrap(); 267 | let deserialized: BloomFilter<256, 30> = libipld::serde::from_ipld(ipld).unwrap(); 268 | 269 | assert_eq!(deserialized, bloom); 270 | } 271 | } 272 | 273 | #[cfg(test)] 274 | mod proptests { 275 | use super::BloomFilter; 276 | use crate::common::HashIndexIterator; 277 | use proptest::collection::vec; 278 | use test_strategy::proptest; 279 | 280 | #[proptest] 281 | fn iterator_can_give_unbounded_number_of_indices(#[strategy(0usize..500)] count: usize) { 282 | let iter = HashIndexIterator::new(&"hello", 200); 283 | 284 | let indices = (0..20) 285 | .map(|_| (iter.clone().take(count).collect::>(), count)) 286 | .collect::>(); 287 | 288 | for (indices, count) in indices { 289 | assert_eq!(indices.len(), count); 290 | } 291 | } 292 | 293 | #[proptest(cases = 1000)] 294 | fn test_contains(#[strategy(vec(vec(0..255u8, 0..100), 26))] values: Vec>) { 295 | let mut bloom = BloomFilter::<256, 30>::new(); 296 | 297 | for v in values.iter() { 298 | bloom.insert(v); 299 | } 300 | 301 | for v in values.iter() { 302 | assert!(bloom.contains(v)); 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /deterministic-bloom/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(docsrs, feature(doc_cfg))] 2 | #![warn(missing_debug_implementations, missing_docs, rust_2018_idioms)] 3 | #![deny(unreachable_pub, private_in_public)] 4 | 5 | //! Deterministic Bloom filters 6 | //! 7 | //! This Crate is intented as a solid basis for cache reproducability 8 | //! and for underlying certain cryptographic primitives. 9 | 10 | /// Some structs and implementations that multiple bloom implementations can depend on 11 | pub mod common; 12 | /// Bloom filters with compile-time-determinted parameters (size & hash count) 13 | pub mod const_size; 14 | /// Bloom filters with runtime-determined parameters. Their size can be chosen 15 | /// arbitrarily at runtime, but not be modified during use (they're not resizable). 16 | pub mod runtime_size; 17 | 18 | mod utils; 19 | -------------------------------------------------------------------------------- /deterministic-bloom/src/runtime_size.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | common::{BloomParams, HashIndexIterator}, 3 | utils::HexFieldDebug, 4 | }; 5 | use bitvec::{prelude::Lsb0, view::BitView}; 6 | use std::fmt::Debug; 7 | 8 | //------------------------------------------------------------------------------ 9 | // Type Definitions 10 | //------------------------------------------------------------------------------ 11 | 12 | /// An implementation of a basic [bloom filter]. 13 | /// 14 | /// Its size can be chosen (or made optimal for given parameters) at creation time, 15 | /// but its size will have to stay the same during usage. I.e. you need to know 16 | /// what your target capacity and false positive rates should be in advance. 17 | /// 18 | /// Unlike the [`const_size::BloomFilter`](crate::const_size::BloomFilter) however, 19 | /// this implementation doesn't require you to know the parameters at compile time. 20 | /// 21 | /// # Example 22 | /// 23 | /// ``` 24 | /// use deterministic_bloom::runtime_size::BloomFilter; 25 | /// 26 | /// let mut filter = BloomFilter::new_from_fpr(1_000, 1.0 / 1_000_000.0); 27 | /// filter.insert(b"Hello, World!"); 28 | /// 29 | /// assert!(filter.contains(b"Hello, World!")); 30 | /// assert!(!filter.contains(b"Hello?")); // true in all but 1 in a million cases 31 | /// ``` 32 | /// 33 | /// [bloom filter]: https://en.wikipedia.org/wiki/Bloom_filter 34 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] 35 | pub struct BloomFilter { 36 | k_hashes: usize, 37 | bytes: Box<[u8]>, 38 | } 39 | 40 | impl BloomFilter { 41 | /// Construct a bloom filter with optimal parameters for given maximum capacity `n_elems` 42 | /// and false positive rate `fpr`. 43 | /// 44 | /// `n_elems` must be non-zero and `fpr` must be a number between 0 and 1 exclusive. 45 | /// 46 | /// # Example 47 | /// 48 | /// ``` 49 | /// use deterministic_bloom::runtime_size::BloomFilter; 50 | /// 51 | /// let mut filter = BloomFilter::new_from_fpr(1_000_000, 1.0 / 1_000_000.0); 52 | /// 53 | /// assert_eq!(filter.as_bytes().len(), 3594397); 54 | /// assert_eq!(filter.hash_count(), 20); 55 | /// ``` 56 | pub fn new_from_fpr(n_elems: u64, fpr: f64) -> Self { 57 | let params = BloomParams::new_from_fpr(n_elems, fpr); 58 | let bits = Box::from(vec![0u8; params.byte_size].as_ref()); 59 | Self { 60 | k_hashes: params.k_hashes, 61 | bytes: bits, 62 | } 63 | } 64 | 65 | /// Construct an optimal power-of-two (po2) sized bloom filter for given maximum capacity 66 | /// `n_elems` and false positive rate `fpr`. 67 | /// 68 | /// `n_elems` must be non-zero and `fpr` must be a number between 0 and 1 exclusive. 69 | /// 70 | /// Using a power-of-two size can be beneficial due to not requiring rejection sampling 71 | /// when generating the hash indicies for items inserted into the filter. 72 | /// 73 | /// # Example 74 | /// 75 | /// ``` 76 | /// use deterministic_bloom::runtime_size::BloomFilter; 77 | /// 78 | /// let mut filter = BloomFilter::new_from_fpr_po2(10_000_000, 0.01); 79 | /// 80 | /// assert_eq!(filter.as_bytes().len(), 16_777_216); 81 | /// assert_eq!(filter.as_bytes().len().count_ones(), 1); // size is a power of two 82 | /// assert_eq!(filter.hash_count(), 10); 83 | /// ``` 84 | pub fn new_from_fpr_po2(n_elems: u64, fpr: f64) -> Self { 85 | let params = BloomParams::new_from_fpr_po2(n_elems, fpr); 86 | let bits = Box::from(vec![0u8; params.byte_size].as_ref()); 87 | Self { 88 | k_hashes: params.k_hashes, 89 | bytes: bits, 90 | } 91 | } 92 | 93 | /// Construct a bloom filter with given target size and target capacity, both must 94 | /// be non-zero. 95 | /// 96 | /// This will compute the optimal number of hash evaluations per item inserted, but the 97 | /// false positive rate completely depends on the given filter size to maximum capacity 98 | /// ratio. 99 | /// 100 | /// # Example 101 | /// 102 | /// ``` 103 | /// use deterministic_bloom::runtime_size::BloomFilter; 104 | /// 105 | /// let mut filter = BloomFilter::new_from_size(1000, 1000); 106 | /// 107 | /// // False positive rate isn't controlled though: 108 | /// assert!((filter.false_positive_rate_at(1000) - 0.0215).abs() < 1e-4); 109 | /// ``` 110 | pub fn new_from_size(bloom_bytes: usize, n_elems: u64) -> Self { 111 | let params = BloomParams::new_from_size(bloom_bytes, n_elems); 112 | let bits = Box::from(vec![0u8; params.byte_size].as_ref()); 113 | Self { 114 | k_hashes: params.k_hashes, 115 | bytes: bits, 116 | } 117 | } 118 | 119 | /// Construct the bloom filter from existing components. 120 | /// 121 | /// This is useful when e.g. deserializing a bloom filter. 122 | /// 123 | /// # Example 124 | /// 125 | /// ``` 126 | /// use deterministic_bloom::runtime_size::BloomFilter; 127 | /// 128 | /// let mut filter = BloomFilter::new_from_fpr_po2(100_000, 0.01); 129 | /// 130 | /// filter.insert(b"Hello, World!"); 131 | /// 132 | /// // Serialize 133 | /// let k_hashes = filter.hash_count(); 134 | /// let bytes = Box::from(filter.as_bytes()); 135 | /// 136 | /// // Deserialize 137 | /// let filter2 = BloomFilter::new_with(k_hashes, bytes); 138 | /// assert_eq!(filter, filter2); 139 | /// ``` 140 | pub fn new_with(k_hashes: usize, bytes: Box<[u8]>) -> Self { 141 | Self { k_hashes, bytes } 142 | } 143 | 144 | /// Compute the bloom parameters for this bloom filter. 145 | /// This contains information about its size and hash function evaluations per 146 | /// item (`k_hashes`). 147 | pub fn get_bloom_params(&self) -> BloomParams { 148 | BloomParams { 149 | k_hashes: self.k_hashes, 150 | byte_size: self.bytes.len(), 151 | } 152 | } 153 | 154 | /// Get the approximate false positive rate at given capacity for this bloom filter. 155 | /// Returns a number between 0 and 1. 156 | pub fn false_positive_rate_at(&self, n_elems: u64) -> f64 { 157 | self.get_bloom_params().false_positive_rate_at(n_elems) 158 | } 159 | 160 | /// Get the approximate false positive rate at the current capacity of this bloom filter. 161 | /// Returns a number between 0 and 1. 162 | pub fn current_false_positive_rate(&self) -> f64 { 163 | let m = (self.bytes.len() * 8) as f64; 164 | let m_set = self.count_ones() as f64; 165 | let load = m_set / m; 166 | load.powi(self.hash_count() as i32) 167 | } 168 | 169 | /// Counts the amount of bits set in the bloom filter. 170 | pub fn count_ones(&self) -> usize { 171 | self.bytes.view_bits::().count_ones() 172 | } 173 | 174 | /// Insert an element into the bloom filter. 175 | /// 176 | /// The element will be hashed, thus it needs to be representable as bytes. 177 | /// 178 | /// Note: If you're using the bloom filter in a non-trusted 179 | /// environment, so e.g. the items can be chosen by an adversary, please 180 | /// make sure to pre-hash your items with a cryptographic hashing function 181 | /// like SHA-256 or BLAKE3. 182 | /// Otherwise an adversary will be able to generate elements that cause 183 | /// the bloom filter to e.g. be unusually full with an unusually high false 184 | /// positive rate or cheaply generate elements that are false positives. 185 | /// 186 | /// # Example 187 | /// 188 | /// ``` 189 | /// use deterministic_bloom::runtime_size::BloomFilter; 190 | /// 191 | /// let mut filter = BloomFilter::new_from_fpr(1000, 0.0001); 192 | /// 193 | /// for i in 0u32..1000 { 194 | /// filter.insert(&i.to_le_bytes()); 195 | /// } 196 | /// 197 | /// // Slightly more than half filled with zeros 198 | /// assert_eq!(filter.as_bytes().len() / 2 * 8, filter.count_ones() - 322); 199 | /// 200 | /// assert!(filter.contains(&10u32.to_le_bytes())); 201 | /// assert!(!filter.contains(&1001u32.to_le_bytes())); // Except in 0.01% 202 | /// ``` 203 | pub fn insert(&mut self, item: &impl AsRef<[u8]>) { 204 | for i in self.hash_indices(item) { 205 | self.bytes.view_bits_mut::().set(i, true); 206 | } 207 | } 208 | 209 | /// Check whether an element was added into the bloom filter. 210 | /// 211 | /// This will always return true if the element was indeed added before, 212 | /// but it *may* sometimes return true, even if it wasn't. 213 | /// This is called a false positive and the false positive rate 214 | /// at certain capacities can be checked with [`false_positive_rate_at`](BloomFilter::false_positive_rate_at) 215 | /// and a desired false positive rate can be configured on creation with 216 | /// [`new_from_fpr`](BloomFilter::new_from_fpr) or [`new_from_fpr_po2`](BloomFilter::new_from_fpr_po2). 217 | /// 218 | /// # Example 219 | /// 220 | /// ``` 221 | /// use deterministic_bloom::runtime_size::BloomFilter; 222 | /// 223 | /// let mut filter = BloomFilter::new_from_fpr(100, 0.1); // very high false-positive rate 224 | /// 225 | /// for i in 0u32..100 { 226 | /// filter.insert(&i.to_le_bytes()); 227 | /// } 228 | /// 229 | /// // Inserted items will always return true 230 | /// assert!(filter.contains(&50u32.to_le_bytes())); 231 | /// // Non-inserted items mostly return false, but sometimes true 232 | /// assert!(!filter.contains(&101u32.to_le_bytes())); 233 | /// // But sometimes there exist false positives (in this case 10% of the time) 234 | /// assert!(filter.contains(&106u32.to_le_bytes())); 235 | /// ``` 236 | pub fn contains(&self, item: &impl AsRef<[u8]>) -> bool { 237 | for i in self.hash_indices(item) { 238 | if !self.bytes.view_bits::()[i] { 239 | return false; 240 | } 241 | } 242 | true 243 | } 244 | 245 | /// Returns how many hash function invocations are used pre item inserted 246 | pub fn hash_count(&self) -> usize { 247 | self.k_hashes 248 | } 249 | 250 | /// Return the underlying array used to store the bloom bits (always on the heap) 251 | pub fn as_bytes(&self) -> &[u8] { 252 | &self.bytes 253 | } 254 | 255 | /// Return the indices that a given element would set in the filter 256 | pub fn hash_indices<'a>(&self, item: &'a impl AsRef<[u8]>) -> impl Iterator + 'a { 257 | HashIndexIterator::new(item, self.bytes.len() * 8).take(self.hash_count()) 258 | } 259 | } 260 | 261 | impl Debug for BloomFilter { 262 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 263 | f.debug_struct("BloomFilter") 264 | .field("k_hashes", &self.k_hashes) 265 | .field("bytes", &HexFieldDebug(&self.bytes)) 266 | .finish() 267 | } 268 | } 269 | 270 | #[cfg(test)] 271 | mod tests { 272 | use super::BloomFilter; 273 | 274 | #[test] 275 | fn serialization_round_trip() { 276 | let mut filter = BloomFilter::new_from_fpr(100, 0.001); 277 | filter.insert(b"Hello"); 278 | filter.insert(b"World!"); 279 | let serialized_bytes = filter.as_bytes(); 280 | let serialized_k = filter.hash_count(); 281 | let deserialized = BloomFilter::new_with(serialized_k, Box::from(serialized_bytes)); 282 | assert!(deserialized.contains(b"Hello")); 283 | assert!(!deserialized.contains(b"abc")); 284 | assert_eq!(deserialized, filter); 285 | } 286 | 287 | #[test] 288 | fn empty_bloom_filter() { 289 | let filter = BloomFilter::new_with(3, Box::new([])); 290 | // Technically an empty bloom "contains" anything, since everything is a false positive. 291 | assert!(filter.contains(&[1, 2, 3])); 292 | } 293 | } 294 | 295 | #[cfg(test)] 296 | mod proptests { 297 | use super::BloomFilter; 298 | use proptest::prop_assert; 299 | use test_strategy::proptest; 300 | 301 | #[proptest] 302 | fn inserted_always_contained(items: Vec, #[strategy(100usize..10_000)] size: usize) { 303 | let capacity = std::cmp::max(items.len() as u64, 1); 304 | let mut filter = BloomFilter::new_from_size(size, capacity); 305 | 306 | for item in items.iter() { 307 | filter.insert(&item.to_le_bytes()); 308 | } 309 | 310 | for item in items.iter() { 311 | prop_assert!(filter.contains(&item.to_le_bytes())); 312 | } 313 | } 314 | 315 | #[proptest] 316 | fn false_positive_rate_as_predicted( 317 | #[strategy(100u64..1_000)] n_elems: u64, 318 | #[strategy(100.0..10_000.0)] inv_fpr: f64, 319 | ) { 320 | let fpr = 1.0 / inv_fpr; 321 | let mut filter = BloomFilter::new_from_fpr(n_elems, fpr); 322 | 323 | for i in 0..n_elems { 324 | filter.insert(&i.to_le_bytes()); 325 | } 326 | 327 | let measurements = 100_000; 328 | let mut false_positives = 0; 329 | 330 | for i in n_elems..n_elems + measurements { 331 | if filter.contains(&i.to_le_bytes()) { 332 | false_positives += 1; 333 | } 334 | } 335 | 336 | let computed_fpr = false_positives as f64 / measurements as f64; 337 | // The actual FPR should be pretty close 338 | prop_assert!((computed_fpr - fpr).abs() < 1.5e-3); 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /deterministic-bloom/src/utils.rs: -------------------------------------------------------------------------------- 1 | //! Internally-used Utilities 2 | 3 | use serde::de::Visitor; 4 | use std::fmt::{self, Debug}; 5 | 6 | //-------------------------------------------------------------------------------------------------- 7 | // Type Definitions 8 | //-------------------------------------------------------------------------------------------------- 9 | 10 | pub(crate) struct ByteArrayVisitor; 11 | 12 | /// Helper newtype for rendering given debug field as hex string 13 | pub(crate) struct HexFieldDebug>(pub(crate) A); 14 | 15 | //-------------------------------------------------------------------------------------------------- 16 | // Implementations 17 | //-------------------------------------------------------------------------------------------------- 18 | 19 | impl<'de, const N: usize> Visitor<'de> for ByteArrayVisitor { 20 | type Value = [u8; N]; 21 | 22 | fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { 23 | write!(formatter, "a byte array of length {N}") 24 | } 25 | 26 | fn visit_bytes(self, v: &[u8]) -> Result 27 | where 28 | E: serde::de::Error, 29 | { 30 | let bytes: [u8; N] = v.try_into().map_err(E::custom)?; 31 | Ok(bytes) 32 | } 33 | } 34 | 35 | impl> Debug for HexFieldDebug { 36 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 37 | write!(f, "0x")?; 38 | for byte in self.0.as_ref().iter() { 39 | write!(f, "{byte:02X}")?; 40 | } 41 | 42 | Ok(()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples" 3 | version = "0.1.0" 4 | publish = false 5 | edition = "2021" 6 | authors = ["Brooklyn Zelenka "] 7 | 8 | [dev-dependencies] 9 | deterministic-bloom = { path = "../deterministic-bloom", version = "0.1" } 10 | 11 | [[example]] 12 | name = "counterparts" 13 | path = "counterparts.rs" 14 | -------------------------------------------------------------------------------- /examples/counterparts.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | pub fn main() -> Result<(), Box> { 4 | println!("Alien Shore!"); 5 | Ok(()) 6 | } 7 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "locked": { 5 | "lastModified": 1667395993, 6 | "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", 7 | "owner": "numtide", 8 | "repo": "flake-utils", 9 | "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "numtide", 14 | "repo": "flake-utils", 15 | "type": "github" 16 | } 17 | }, 18 | "nixpkgs": { 19 | "locked": { 20 | "lastModified": 1671417167, 21 | "narHash": "sha256-JkHam6WQOwZN1t2C2sbp1TqMv3TVRjzrdoejqfefwrM=", 22 | "owner": "nixos", 23 | "repo": "nixpkgs", 24 | "rev": "bb31220cca6d044baa6dc2715b07497a2a7c4bc7", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "nixos", 29 | "ref": "nixpkgs-unstable", 30 | "repo": "nixpkgs", 31 | "type": "github" 32 | } 33 | }, 34 | "root": { 35 | "inputs": { 36 | "flake-utils": "flake-utils", 37 | "nixpkgs": "nixpkgs", 38 | "rust-overlay": "rust-overlay" 39 | } 40 | }, 41 | "rust-overlay": { 42 | "inputs": { 43 | "flake-utils": [ 44 | "flake-utils" 45 | ], 46 | "nixpkgs": [ 47 | "nixpkgs" 48 | ] 49 | }, 50 | "locked": { 51 | "lastModified": 1671416426, 52 | "narHash": "sha256-kpSH1Jrxfk2qd0pRPJn1eQdIOseGv5JuE+YaOrqU9s4=", 53 | "owner": "oxalica", 54 | "repo": "rust-overlay", 55 | "rev": "fbaaff24f375ac25ec64268b0a0d63f91e474b7d", 56 | "type": "github" 57 | }, 58 | "original": { 59 | "owner": "oxalica", 60 | "repo": "rust-overlay", 61 | "type": "github" 62 | } 63 | } 64 | }, 65 | "root": "root", 66 | "version": 7 67 | } 68 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "deterministic-bloom"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 6 | flake-utils.url = "github:numtide/flake-utils"; 7 | 8 | rust-overlay = { 9 | url = "github:oxalica/rust-overlay"; 10 | inputs.nixpkgs.follows = "nixpkgs"; 11 | inputs.flake-utils.follows = "flake-utils"; 12 | }; 13 | }; 14 | 15 | outputs = 16 | { self 17 | , nixpkgs 18 | , flake-utils 19 | , rust-overlay 20 | } @ inputs: 21 | flake-utils.lib.eachDefaultSystem (system: 22 | let 23 | overlays = [ (import rust-overlay) ]; 24 | pkgs = import nixpkgs { inherit system overlays; }; 25 | 26 | rust-toolchain = 27 | (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml).override { 28 | extensions = [ "cargo" "clippy" "rustfmt" "rust-src" "rust-std" ]; 29 | targets = [ "wasm32-unknown-unknown" ]; 30 | }; 31 | 32 | nightly-rustfmt = pkgs.rust-bin.nightly.latest.rustfmt; 33 | 34 | format-pkgs = with pkgs; [ 35 | nixpkgs-fmt 36 | ]; 37 | 38 | cargo-installs = with pkgs; [ 39 | binaryen 40 | cargo-deny 41 | cargo-expand 42 | cargo-sort 43 | cargo-udeps 44 | cargo-watch 45 | wasm-pack 46 | wasm-bindgen-cli 47 | ]; 48 | 49 | bin = { 50 | bash = "${pkgs.bash}/bin/bash"; 51 | cargo = "${pkgs.cargo}/bin/cargo"; 52 | wasm-pack = "${pkgs.wasm-pack}/bin/wasm-pack"; 53 | }; 54 | 55 | test-all = 56 | pkgs.writeScriptBin "test:all" '' 57 | #!${bin.bash} 58 | echo "⚙️ Running cargo test" 59 | ${bin.cargo} test 60 | 61 | printf %"$COLUMNS"s | tr " " "-" 62 | echo "⚙️ Running headless Wasm tests" 63 | ${bin.wasm-pack} test --headless --chrome deterministic-bloom-wasm 64 | ''; 65 | in 66 | rec 67 | { 68 | devShells.default = pkgs.mkShell { 69 | name = "deterministic-bloom"; 70 | nativeBuildInputs = with pkgs; [ 71 | # The ordering of these two items is important. For nightly rustfmt to be used instead of 72 | # the rustfmt provided by `rust-toolchain`, it must appear first in the list. This is 73 | # because native build inputs are added to $PATH in the order they're listed here. 74 | nightly-rustfmt 75 | rust-toolchain 76 | pre-commit 77 | direnv 78 | chromedriver 79 | geckodriver 80 | test-all 81 | self.packages.${system}.irust 82 | ] ++ format-pkgs ++ cargo-installs; 83 | 84 | shellHook = '' 85 | [ -e .git/hooks/pre-commit ] || pre-commit install --install-hooks && pre-commit install --hook-type commit-msg 86 | ''; 87 | }; 88 | 89 | packages.irust = pkgs.rustPlatform.buildRustPackage rec { 90 | pname = "irust"; 91 | version = "1.65.1"; 92 | src = pkgs.fetchFromGitHub { 93 | owner = "sigmaSd"; 94 | repo = "IRust"; 95 | rev = "v${version}"; 96 | sha256 = "sha256-AMOND5q1XzNhN5smVJp+2sGl/OqbxkGPGuPBCE48Hik="; 97 | }; 98 | 99 | doCheck = false; 100 | cargoSha256 = "sha256-A24O3p85mCRVZfDyyjQcQosj/4COGNnqiQK2a7nCP6I="; 101 | }; 102 | } 103 | ); 104 | } 105 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["cargo-workspace"], 3 | "changelog-path": "CHANGELOG.md", 4 | "release-type": "rust", 5 | "bump-minor-pre-major": true, 6 | "bump-patch-for-minor-pre-major": true, 7 | "packages": { 8 | "deterministic-bloom": {}, 9 | "deterministic-bloom-wasm": {} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | --------------------------------------------------------------------------------