Compare commits

...

86 Commits

Author SHA1 Message Date
11102a87e3 Release 0.10.4
fluence-keypair@0.10.4
trust-graph@0.10.4
trust-graph-distro@0.10.4

Generated by cargo-workspaces
2023-12-15 18:41:12 +04:00
5b372e7365 update marine and stuff 2023-12-15 17:55:46 +04:00
ed5bd2c0ec feat(fluence-keypair): bump ed25519-dalek to 0.2.0 (#127) 2023-10-10 15:01:07 +02:00
2f336f73cf chore: Use marine from checks (#126)
Use marine from checks
2023-09-04 14:57:50 +03:00
d378fe2509 chore: Do not publish package (#124)
Do not publish package
2023-08-02 14:21:16 +03:00
80bf4aa4ff chore: release master (#117) 2023-07-04 20:33:47 +03:00
80757e6352 chore(deps): update dependency @fluencelabs/aqua to ^0.10.0 (#71)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-04 19:51:30 +03:00
f2c3b2a13c fix(deps): update dependency @fluencelabs/trust-graph to v3.1.2 (#69)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-04 19:51:01 +03:00
4f657f9dae fix(deps): update dependency @fluencelabs/aqua-lib to ^0.6.0 (#67)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-04 19:49:20 +03:00
725d3f8f48 fix(deps): update rust crate marine-rs-sdk-test to 0.10.0 (#106)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-04 19:48:56 +03:00
1fb02e4827 chore(deps): update rust crate lazy_static to 1.4 (#108)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-04 19:48:12 +03:00
a37bda37a2 chore(deps): update rust crate built to 0.6.0 (#115)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-04 19:47:55 +03:00
ee330a715a fix(keypair): deserialize libp2p keypair from secret key (#116)
* fix: Ed25519 keypair

* Update keypair/src/key_pair.rs

Co-authored-by: folex <0xdxdy@gmail.com>

---------

Co-authored-by: folex <0xdxdy@gmail.com>
2023-07-04 19:47:26 +03:00
c22eab38c1 chore: release master (#113) 2023-06-30 11:15:20 +03:00
3692d6898d chore: Revert "chore: release master (#111)" (#114)
Revert "chore: release master (#111)"

This reverts commit 3a5f23741f.
2023-06-30 11:03:02 +03:00
3a5f23741f chore: release master (#111) 2023-06-29 20:35:37 +03:00
b10991501d fix: changed build.sh for test ci & release (#112) 2023-06-29 11:28:41 -06:00
d80a43bcff revert: release master (#110)
Revert "chore: release master (#104)"

This reverts commit a7ea41ed4d.
2023-06-29 19:51:42 +03:00
a7ea41ed4d chore: release master (#104) 2023-06-29 19:31:25 +03:00
d04120bacf feat: update libp2p identity (#109) 2023-06-29 19:21:14 +03:00
3ba3855892 feat: add distro crate [fixes NET-463] (#93)
* add distro crate

---------

Co-authored-by: Anatoly Laskaris <github_me@nahsi.dev>
2023-06-20 14:07:53 +02:00
f7ef0f8da0 fix(keypair): update description (#105) 2023-05-16 10:40:47 -06:00
2001f900fa fix(deps): update rust crate derivative to 2.2.0 (#88)
fix(deps): update rust crate derivative to 2.2.0

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-16 10:26:55 -06:00
412b8ba725 chore: update libp2p (#103)
deps: update libp2p
2023-05-16 19:24:03 +03:00
a7abe87c09 chore: release master (#102) 2023-05-09 17:59:08 +04:00
1a26a6809e feat: dummy to trigger release (#101) 2023-05-09 17:46:41 +04:00
93161afe0c feat: fix builtin package path (#100) 2023-05-09 16:37:48 +03:00
11fd2de7b6 chore: release master (#99) 2023-05-09 16:55:01 +04:00
a8fdb4472e feat: fix trust-graph package (#98) 2023-05-09 15:25:49 +04:00
56d0ea27bd chore: Fix update in rust-peer-distro (#97)
Fix update in rust-peer-distro
2023-05-08 18:38:53 +03:00
d0c6c62ca4 chore: release master (#96) 2023-05-08 18:21:23 +04:00
b6df3fe548 feat: dummy change for release (#95) 2023-05-08 17:08:41 +03:00
a747b9cc75 feat(builtin-package): use new blueprint (#94) 2023-05-08 16:48:39 +03:00
d567848cba chore: release master (#92) 2023-05-03 16:58:12 +04:00
9b942eacca feat(keypair): Make KeyFormat more convenient (#91)
* feat(keypair): Make `KeyFormat` more convenient

1. Make `KeyFormat` implement `Debug`, `Clone` and `Eq`.
2. Add `KeyPair::key_format(&self) -> KeyFormat` method.
2023-05-03 02:48:42 +07:00
c85fb16de3 chore: Fix build script (#90)
Fix build script
2023-04-18 16:42:51 +03:00
97ce5bbac7 chore(release-please): Fix publishing (#89)
Fix typo
2023-04-18 11:24:24 +03:00
864b7f5c13 chore: release master (#83) 2023-04-13 16:11:54 +03:00
da38a41ba7 fix(deps): Add trust-graph to workspace and bump sqlite-wasm version (#87)
* Add trust-graph to workspace

* Bump sqlite lite version

* Fix link
2023-04-13 15:03:08 +03:00
7493eed216 chore: skip cargo-workspace merge in release-please (#86)
Fix
2023-04-13 13:59:44 +03:00
e9399b7d0c chore: Move trust-graph to subdir (#85)
* Fix?

* Move trust-grap to subdir

* fix quickcheck update

* fix fmt

* fix service tests

* use marine 0.9.1

---------

Co-authored-by: Maria Kuklina <maria@fluence.one>
Co-authored-by: Valery Antopol <valery.antopol@gmail.com>
2023-04-13 13:55:35 +03:00
81eb924476 chore: Try workaround for release-please merge issue (#84) 2023-04-12 17:06:28 +03:00
fe902acc50 update aqua-lib (#81) 2023-04-12 15:22:47 +02:00
f5994b33d1 chore: Use setup-marine action (#78) 2023-03-20 12:19:48 +04:00
554bb60256 chore: release master (#63)
* chore: release master

* chore: bump fluence-keypair to 0.10.0 manually

---------

Co-authored-by: folex <0xdxdy@gmail.com>
2023-03-15 19:34:08 -03:00
080503dcfa feat(deps)!: update libp2p to 0.39.1 and other deps (#77)
Co-authored-by: folex <0xdxdy@gmail.com>
2023-03-15 19:17:21 -03:00
757145fffc chore: Add release-please, renovate (#61) 2023-01-30 15:32:57 +04:00
8e58f56190 chore: Update CI and fix clippy warnings [fixes FLU-251] (#62)
* Add release-please and renovate

* Update ci

* Revert "Add release-please and renovate"

This reverts commit a37a4e2a59.

* Allow unnecessary_lazy_evaluations
2023-01-30 18:43:16 +08:00
664552d4f9 chore: update cargo dependencies (#55) 2023-01-30 14:42:12 +04:00
c717e4dc73 chore(fluence-keypair): increment fluence-keypair version to 0.9.0 (#60)
* Increment version

* Increment version

* Increment version
2023-01-27 17:09:12 +03:00
015422efcc Update libp2p-core to 0.38 (#51) 2023-01-27 16:43:47 +03:00
0c1398b377 Create bin dir (#59) 2023-01-27 01:11:55 +04:00
bc638ddda3 chore(ci): remove jq install step (#58) 2023-01-26 23:08:24 +02:00
8cf640c9b5 chore(ci): make release with older marine (#57) 2023-01-27 01:04:05 +04:00
55c8cb72a2 chore: tmp hack to release TG (#56) 2023-01-27 00:38:18 +04:00
249f2f25f2 chore: update toolchains (#54) 2023-01-26 23:01:53 +04:00
789b7124e3 chore: Fix release (#53) 2023-01-26 20:34:00 +02:00
5a2c4aa1c1 chore: update fluence certificates (#52) 2023-01-26 22:21:06 +04:00
a5a36a672c chore: disable publishing for trust-graph-wasm 2022-10-06 16:28:13 +04:00
d45796b846 chore: add CHANGELOGs
chore: fluence-keypair v0.8.1
2022-10-06 16:24:52 +04:00
570428c8fe chore: set version of fluence-keypair to 0.8.0 2022-10-06 16:20:52 +04:00
017c134cf3 chore: update Cargo.lock 2022-10-06 16:12:37 +04:00
a6ce8d9eee feat(keypair): add KeyPair::from_secret_key (#50) 2022-10-06 16:10:16 +04:00
7db85cbece fluence-keypair 0.8.0 2022-09-19 19:21:55 +03:00
f860884da8 fluence-keypair 0.8.0 2022-09-19 16:56:05 +03:00
cd471cd683 fluence-keypair: libp2p-core 0.33.0 (#49) 2022-09-19 16:44:10 +03:00
47b65e277a Readme update (#48) 2022-08-02 12:53:43 +04:00
a162fa3583 Update README (#47) 2022-07-18 12:38:23 +02:00
2a5e324dd5 example: improved (#46) 2022-07-08 17:40:22 +04:00
edc7dc404f ci: fix publish (#45) 2022-07-06 14:38:55 +04:00
b9a996eba8 fix bug get_all_certs_from, update example (#44) 2022-07-06 14:24:33 +04:00
7fbaaaa8f9 ci: remove circle, update gh, add lints; remove warnings (#43) 2022-06-30 16:41:37 +04:00
aa72fdab64 Add memory leak temporary mitigation (#42) 2022-06-28 19:40:19 +04:00
b9fbbbcafb aqua: update sig service (#39) 2022-02-05 03:17:07 +03:00
7c72a59bef npm: aqua: fix build (#38) 2022-02-04 20:00:49 +03:00
5609740216 High-level Aqua API (#35) 2022-02-04 18:45:37 +03:00
58648d7037 fluence-keypair 0.6.0 2022-01-28 18:18:22 +03:00
048406aa02 CI: toolchain nightly-2022-01-16 2022-01-28 17:52:35 +03:00
b76954782b libp2p-core 0.31.0 (from crates.io) (#37) 2022-01-28 16:15:20 +03:00
c2f63cb41e Remove serde version lock (#15) 2022-01-13 19:37:23 +03:00
8214b1cc5c Fix revocations logic (#34) 2021-12-13 19:40:16 +03:00
c4019127e8 bump versions, update readme (#33) 2021-12-06 23:27:06 +03:00
24dbadaa7c Update READMEs, example, fix bug in aqua api (#32) 2021-12-06 20:17:45 +03:00
7528256e78 Remove mutex from TG instance (#31) 2021-11-24 18:35:38 +03:00
1f31ffecc7 refactoring (#30) 2021-11-24 16:54:45 +03:00
cb6e072231 fix db paths (#28) 2021-11-12 23:21:06 +03:00
92 changed files with 39116 additions and 7728 deletions

6
.cargo/config Normal file
View File

@ -0,0 +1,6 @@
[http]
timeout = 30 # timeout for each HTTP request, in seconds
multiplexing = false # HTTP/2 multiplexing
[net]
retry = 50 # network retries

View File

@ -1,38 +0,0 @@
version: 2.1
orbs:
docker: circleci/docker@1.5.0
jobs:
Build:
docker:
- image: circleci/rust:latest
resource_class: xlarge
environment:
RUST_BACKTRACE: 1
steps:
- checkout
- run: |
sudo bash .github/download_marine.sh
- restore_cache:
keys:
- trust-graph00-{{ checksum "./service/Cargo.lock" }}-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }}
- run: |
rustup target add wasm32-wasi
cargo test --no-fail-fast --release --all-features --
cd ./service
./build.sh
mkdir data
cargo test --no-fail-fast --release --all-features -- --test-threads=1
- save_cache:
paths:
- ~/.cargo
- ~/.rustup
key: trust-graph00-{{ checksum "./Cargo.lock" }-{{ checksum "./service/Cargo.lock" }}}-{{ checksum "./keypair/Cargo.lock" }}
workflows:
version: 2
CircleCI:
jobs:
- Build

3
.github/actionlint.yaml vendored Normal file
View File

@ -0,0 +1,3 @@
self-hosted-runner:
labels:
- builder

View File

@ -1,14 +0,0 @@
#!/bin/bash
set -o pipefail -o errexit -o nounset
set -x
MARINE_RELEASE="https://api.github.com/repos/fluencelabs/marine/releases/latest"
OUT_DIR=/usr/local/bin
# get metadata about release
curl -s -H "Accept: application/vnd.github.v3+json" $MARINE_RELEASE |
# extract url and name for asset with name "marine"
# also append $OUT_DIR to each name so file is saved to $OUT_DIR
jq -r ".assets | .[] | select(.name == \"marine\") | \"\(.browser_download_url) $OUT_DIR/\(.name)\"" |
# download assets
xargs -n2 bash -c 'curl -L $0 -o $1 && chmod +x $1'

40
.github/release-please/config.json vendored Normal file
View File

@ -0,0 +1,40 @@
{
"boostrap-sha": "015422efcce41530a6cd84a25091598bc459d2e6",
"release-type": "rust",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true,
"plugins": [
{
"type": "cargo-workspace",
"merge": false
},
{
"type": "linked-versions",
"groupName": "trust-graph, wasm and api",
"components": [
"trust-graph",
"trust-graph-api",
"trust-graph-wasm",
"distro"
]
}
],
"packages": {
"trust-graph": {
"component": "trust-graph"
},
"aqua": {
"release-type": "node",
"component": "trust-graph-api"
},
"service": {
"component": "trust-graph-wasm"
},
"keypair": {
"component": "keypair"
},
"distro": {
"component": "distro"
}
}
}

7
.github/release-please/manifest.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"trust-graph": "0.4.7",
"aqua": "0.4.7",
"service": "0.4.7",
"keypair": "0.10.3",
"distro": "0.4.7"
}

38
.github/renovate.json vendored Normal file
View File

@ -0,0 +1,38 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base",
":semanticCommitTypeAll(chore)"
],
"enabledManagers": ["cargo", "npm", "github-actions", "pip_requirements"],
"rangeStrategy": "pin",
"schedule": "every weekend",
"packageRules": [
{
"matchManagers": ["cargo", "npm"],
"matchPackagePatterns": [
"@fluencelabs/.*",
"fluence-.*",
"marine-.*"
],
"semanticCommitType": "fix",
"semanticCommitScope": "deps",
"schedule": "at any time"
},
{
"matchDepTypes": ["devDependencies"],
"prPriority": -1,
"semanticCommitType": "chore",
"semanticCommitScope": "deps"
},
{
"matchUpdateTypes": ["major"],
"prConcurrentLimit": 1
},
{
"matchManagers": ["github-actions"],
"groupName": "all github-actions",
"prPriority": -1
}
]
}

View File

@ -1,5 +0,0 @@
{
"template": "${{CHANGELOG}}\n\n${{UNCATEGORIZED}}",
"pr_template": "- #${{NUMBER}} ${{TITLE}}",
"empty_template": "- no changes"
}

35
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: lint
on:
pull_request:
types:
- opened
- edited
- synchronize
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
pr:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
reviewdog:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Lint actions
uses: reviewdog/action-actionlint@v1
env:
SHELLCHECK_OPTS: "-e SC2086 -e SC2207 -e SC2128"
with:
reporter: github-pr-check
fail_on_error: true

View File

@ -1,159 +1,153 @@
name: "publish-release"
name: "release"
on:
push:
tags:
- "v*"
branches:
- "master"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
jobs:
npm-publish:
name: "Publish"
release-please:
runs-on: ubuntu-latest
container: rust
defaults:
run:
shell: bash
outputs:
releases-created: ${{ steps.release.outputs['releases_created'] }}
trust-graph-api-release-created: ${{ steps.release.outputs['aqua--release_created'] }}
trust-graph-release-created: ${{ steps.release.outputs['trust-graph--release_created'] }}
trust-graph-tag-name: ${{ steps.release.outputs['trust-graph--tag_name'] }}
trust-graph-version: ${{ steps.release.outputs['trust-graph--version'] }}
steps:
### Setup
- name: Checkout repository
uses: actions/checkout@v2
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Download jq
run: |
curl -L https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 -o /usr/local/bin/jq
chmod +x /usr/local/bin/jq
- name: Download marine
run: bash $GITHUB_WORKSPACE/.github/download_marine.sh
- name: Cache npm
uses: actions/cache@v2
- name: Run release-please
id: release
uses: google-github-actions/release-please-action@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-v01-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-v01-
token: ${{ secrets.FLUENCEBOT_RELEASE_PLEASE_PAT }}
command: manifest
config-file: .github/release-please/config.json
manifest-file: .github/release-please/manifest.json
- uses: actions/setup-node@v2
- name: Show output from release-please
if: steps.release.outputs.releases_created
env:
RELEASE_PLEASE_OUTPUT: ${{ toJSON(steps.release.outputs) }}
run: echo "${RELEASE_PLEASE_OUTPUT}" | jq
publish:
runs-on: builder
needs: release-please
if: needs.release-please.outputs.releases-created
permissions:
contents: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Import secrets
uses: hashicorp/vault-action@v2.4.3
with:
node-version: "15"
registry-url: "https://registry.npmjs.org"
url: https://vault.fluence.dev
path: jwt/github
role: ci
method: jwt
jwtGithubAudience: "https://github.com/fluencelabs"
jwtTtl: 300
exportToken: false
secrets: |
kv/npmjs/fluencebot token | NODE_AUTH_TOKEN ;
kv/crates.io/fluencebot token | CARGO_REGISTRY_TOKEN
- uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Install toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly-2021-09-01
target: wasm32-wasi
override: true
- name: Setup marine
uses: fluencelabs/setup-marine@v1
### Build
- name: trust-graph.wasm
working-directory: ./service
- name: Build
run: ./build.sh
- name: Check Aqua compiles
working-directory: ./aqua
- name: Install cargo-workspaces
uses: baptiste0928/cargo-install@v1.3.0
with:
crate: cargo-workspaces
- name: Publish to crates.io
run: |
npm i
npm run build
cargo ws publish \
--no-git-commit \
--allow-dirty \
--from-git \
--skip-published \
--yes
- name: Create builtin distribution package
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: "16"
registry-url: "https://registry.npmjs.org"
cache-dependency-path: "aqua/package-lock.json"
cache: "npm"
- run: npm i
working-directory: aqua
- run: npm run build
working-directory: aqua
- name: Publish to NPM registry
if: needs.release-please.outputs.trust-graph-api-release-created
run: npm publish --access public
working-directory: aqua
slack:
if: always()
name: "Notify"
runs-on: ubuntu-latest
needs:
- release-please
- publish
permissions:
contents: read
id-token: write
steps:
- uses: lwhiteley/dependent-jobs-result-check@v1
id: status
with:
statuses: failure
dependencies: ${{ toJSON(needs) }}
- name: Log output
run: |
./builtin-package/package.sh
echo "statuses:" "${{ steps.status.outputs.statuses }}"
echo "jobs:" "${{ steps.status.outputs.jobs }}"
echo "found any?:" "${{ steps.status.outputs.found }}"
- name: Build Changelog
id: changelog
uses: mikepenz/release-changelog-builder-action@v1
- name: Import secrets
uses: hashicorp/vault-action@v2.4.3
with:
configuration: ".github/workflows/changelog_config.json"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
url: https://vault.fluence.dev
path: jwt/github
role: ci
method: jwt
jwtGithubAudience: "https://github.com/fluencelabs"
jwtTtl: 300
exportToken: false
secrets: |
kv/slack/release-please webhook | SLACK_WEBHOOK_URL
## Publish
- name: Release
id: release
uses: softprops/action-gh-release@v1
- uses: ravsamhq/notify-slack-action@v2
if: steps.status.outputs.found == 'true'
with:
name: trust-graph ${{ env.RELEASE_VERSION }}
tag_name: ${{ env.RELEASE_VERSION }}
files: |
trust-graph.tar.gz
body: ${{steps.changelog.outputs.changelog}}
draft: false
prerelease: false
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
### Publish Aqua API
- name: Publish Aqua API
run: |
npm version ${{ env.RELEASE_VERSION }} --allow-same-version
npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
working-directory: ./aqua
## Update node-distro repo
- name: Calculate SHA256
run: |
du -hs trust-graph.tar.gz
echo $(sha256sum trust-graph.tar.gz)
echo "SHA256=$(sha256sum trust-graph.tar.gz | awk '{ print $1 }')" >> $GITHUB_ENV
- name: Get tar.gz URL
id: package-url
uses: actions/github-script@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
result-encoding: string
script: |
try {
let assets = await github.repos.listReleaseAssets({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: "${{ steps.release.outputs.id }}",
});
console.dir(assets);
let package = assets.data.find((a) => a.name === 'trust-graph.tar.gz');
let url = package.browser_download_url;
console.log("URL: " + url);
return url;
} catch (e) {
console.log("Err: " + e);
throw e;
}
- name: Update version in node-distro repo
uses: benc-uk/workflow-dispatch@v1
with:
workflow: update_service
repo: fluencelabs/node-distro
ref: 'main'
token: ${{ secrets.PERSONAL_TOKEN }}
inputs: '{
"name": "trust-graph",
"version": "${{ env.RELEASE_VERSION }}",
"url": "${{ steps.package-url.outputs.result }}",
"sha256": "${{ env.SHA256 }}"
}'
- name: Log notice
uses: actions/github-script@v4
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
console.dir(core);
core.info("trust-graph was updated to ${{ env.RELEASE_VERSION }} in node-distro repo");
status: "failure"
notification_title: "*{workflow}* has {status_message}"
message_format: "${{ steps.status.outputs.jobs }} {status_message} in <{repo_url}|{repo}>"
footer: "<{run_url}>"

35
.github/workflows/run-tests.yml vendored Normal file
View File

@ -0,0 +1,35 @@
name: Run tests
on:
pull_request:
paths-ignore:
- "**.md"
push:
branches:
- "master"
concurrency:
group: "${{ github.workflow }}-${{ github.ref }}"
cancel-in-progress: true
jobs:
trust-graph:
uses: ./.github/workflows/tests.yml
lints:
name: lints
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v3
- name: Setup rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Run cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check

View File

@ -1,17 +0,0 @@
name: "tag"
on:
workflow_dispatch:
jobs:
tag:
name: "Tag"
runs-on: "ubuntu-latest"
steps:
- uses: actions/checkout@v2
- name: Bump version and push tag
id: tag_version
uses: mathieudutour/github-tag-action@v5.5
with:
github_token: ${{ secrets.PERSONAL_TOKEN }}

58
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,58 @@
name: Run tests with workflow_call
on:
workflow_call:
jobs:
trust-graph:
name: "cargo nextest"
runs-on: builder
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
- name: Setup marine
uses: fluencelabs/setup-marine@v1
with:
artifact-name: marine
- name: Build
run: ./build.sh
- name: Run cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: -Z unstable-options --all
- name: Setup nextest
uses: taiki-e/install-action@nextest
- name: Run cargo nextest
env:
NEXTEST_RETRIES: 10
NEXTEST_TEST_THREADS: 10
# exclude distro since at this point we don't have compiled wasms which are required for compilation
run: cargo nextest run --release --all-features --no-fail-fast --workspace --exclude trust-graph-distro
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: "16"
registry-url: "https://registry.npmjs.org"
cache-dependency-path: "aqua/package-lock.json"
cache: "npm"
- run: npm i
working-directory: aqua
- run: npm run build
working-directory: aqua

13
.gitignore vendored
View File

@ -2,6 +2,8 @@ service/target
service/artifacts
builtin-package/*.wasm
trust-graph.tar.gz
distro/trust-graph-service
distro/target
**/*.rs.bk
**/.idea
@ -16,3 +18,14 @@ example/src/generated/**
example/generated/**
admin/src/generated/**
admin/generated/**
target
# recommended by Fluence Labs:
.idea
.DS_Store
.fluence
Cargo.lock
**/target/
.vscode/settings.json
.repl_history

3529
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,10 @@
[package]
name = "trust-graph"
version = "0.3.0"
authors = ["Fluence Labs"]
edition = "2018"
description = "trust graph"
license = "Apache-2.0"
repository = "https://github.com/fluencelabs/trust-graph"
[dependencies]
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"] }
serde = { version = "=1.0.118", features = ["derive"] }
fluence-keypair = { path = "./keypair", version = "0.5.0" }
serde_json = "1.0.58"
bs58 = "0.3.1"
failure = "0.1.6"
log = "0.4.11"
ref-cast = "1.0.2"
derivative = "2.1.1"
signature = "1.3.0"
serde_with = "1.6.0"
thiserror = "1.0.23"
sha2 = "0.9.5"
rand = "0.7.0"
[workspace]
members = [
"trust-graph",
"keypair",
"service"
"service",
"distro"
]
[workspace.dependencies]
libp2p-identity = { version = "0.2.1", default-features = false }

321
README.md
View File

@ -1,13 +1,320 @@
### Trust Graph
# Trust Graph
The network-wide peer relationship layer is used to manage connectivity and permissions. Peers keep the distributed graph of relationships, basically a Web of Trust. That graph is used is used to prioritize connections from known peers and avoid Sybil attacks. Also, TrustGraph may be used at the application level in various ways such as prioritization of service execution on authorized peers or a tighter connection of a single companys peers.
- [Trust Graph](#trust-graph)
- [Overview](#overview)
- [Why is it important?](#why-is-it-important)
- [What is it?](#what-is-it)
- [How to Use it in Aqua](#how-to-use-it-in-aqua)
- [How to import](#how-to-import)
- [How to add roots](#how-to-add-roots)
- [How to issue and add trust](#how-to-issue-and-add-trust)
- [How to revoke the trust](#how-to-revoke-the-trust)
- [How to get certificates](#how-to-get-certificates)
- [How to get weights](#how-to-get-weights)
- [How to use it in TS/JS](#how-to-use-it-in-tsjs)
- [Use cases](#use-cases)
- [Create a trusted subnetwork](#create-a-trusted-subnetwork)
- [Service permission management](#service-permission-management)
- [Label trusted peers and execute computation only on this peers](#label-trusted-peers-and-execute-computation-only-on-this-peers)
- [FAQ](#faq)
- [API](#api)
- [Directory structure](#directory-structure)
- [Learn Aqua](#learn-aqua)
### Project structure
## Overview
The problem of access control and permissions is solved with centralized CAs (Certificate Authority) in web 2.0. However, such problem is urgent and becomes even more challenging considering a decentralized nature of web 3.0. TrustGraph is our point of view on the solution for this challenge.
`/.` is the main project with all trust graph logic and in-memory storage as a default
TrustGraph is a bottom layer of trust for open p2p networks: every peer may be provided with SSL-like certificates that promoted over the network. Service providers and peers can treat certificate holders differently based on their certificate set.
`keypair` directory is an abstracted cryptographical layer (key pairs, public keys, signatures, etc.)
TrustGraph is a basic component that allows storing and managing certificates without additional logic about how to decide whom to trust and whom to treat as unreliable.
`service` is a package that provides `marine` API and could be compiled to a Wasm file. It is uses `SQLite` as storage.
## Why is it important?
`example` is a `js` script that shows how to issue, sign trusts/revokes, get certificates
The problem of peer choice and prioritization is very urgent in p2p networks. We can't use the network reliably and predictably without trust to any network participant. We also should mark and avoid malicious peers. In addition we need to control our application access and permissions in runtime, so it performs continuously without interruptions and redeployments.
## What is it?
TrustGraph is basically a directed graph with one root at least, vertices are peer ids, and edges are one of the two types of cryptographic relations: trust and revocation.
**Root** is a peer id that we unconditionally trust until it is removed, and is defined by the node owner. Every root has characteristics that represent the maximum length for a chain of trusts.
As a **path to the root**, we consider a path with only trust edges, given the following rule: chain `R -> A -> ...-> C` is not a path if A revoked C.
**Trust** is a cryptographic relation representing that peer A trusts peer B until this trust expires or is revoked. Trust relation is transitive. If peer A trusts peer B, and peer B trusts peer C, it results in peer A trusts peer C transitively. Trust relation means that you trust to connect, compute or store based on your business logic and chosen metrics. For example, if you want to perform some computation and some well-known peers do that, and are trusted by others you trust, so you can safely use them for computing but not to store sensitive information (personal keys, etc).
Trust data structure contains the following fields:
- peer id, trust is issued to
- creation timestamp
- expiration timestamp
- a signature of the issuer that contains all of the previous fields signed
So the trust is signed and tamperproof by design.
**Certificate** is a chain of trusts started with a self-signed root trust. Considering Trust and Certificate data structures, it is possible to track the chain of trust relations: the `issued_for` field of the first trust in a chain indicates a root peer id, second — whom root trusts, etc. So if we have a chain `R->A->B->C` in the certificate it looks like a chain of the following trusts: `R->R`, `R->A`, `A->B`, `B->C`. A certificate is tamperproof since it is a composition of signed trusts.
![image](images/diagram.png)
So peerA is trusted by peerB if there is a path between them in the instance of TrustGraph. The selection of certificates is subjective and defined by a node owner by choice of roots and maximum chain lengths. For now, there are no default metrics for a general case.
**Revocation** is a cryptographic relation representing that a peer A considers a peer C malicious or unreliable. For example, all chains containing paths from A to C will not be treated as valid. So if A trusts a peer B, and B trusts C, a peer A has no trust to C transitively, it would have otherwise.
![image](images/revocation.png)
Every peer has a **weight**. A weight signifies a power of 2 or zero. If there is no path from any root to a peer, given revocations, its weight equals zero. The closer to the root, the bigger the weight. Weights are also subjective and relevant in the scope of a local TrustGraph.
![image](images/weights.png)
TrustGraph is a builtin meaning that every node is bundled with a TrustGraph instance and predefined certificates.
Trust is transitive in terms of cryptographic relations. On the other hand, a subset of trusts and certificates is subjective for each network participant because of the choice of roots.
## How to Use it in Aqua
### How to import
```
import "@fluencelabs/trust-graph/trust-graph-api.aqua"
import "@fluencelabs/trust-graph/trust-graph.aqua"
func my_function(peer_id: string) -> u32:
on HOST_PEER_ID:
result <- get_weight(peer_id)
<- result
```
### How to add roots
- `set_root(peer_id: PeerId, max_chain_len: u32) -> SetRootResult`
- `add_root_trust(node: PeerId, peer_id: PeerId, max_chain_len: u32) -> ?Error`
Let's set our peer id as a root on our relay and add self-signed trust:
```rust
func set_me_as_root(max_chain_len):
result <- add_root_trust(HOST_PEER_ID, INIT_PEER_ID, max_chain_len)
-- if you use peer_id different from INIT_PEER_ID
-- you should add keypair in your Sig service
if result.success:
-- do smth
Op.noop()
else:
-- handle failure
Op.noop()
```
- also you can use `set_root` + `add_trust` to achieve the same goal
- [how to add keypair to Sig service](https://doc.fluence.dev/docs/fluence-js/3_in_depth#signing-service)
- roots can be added only by the service owner
- `max_chain_len` specifies a number of trusts in a chain for the root. Zero for chains that contain only root trust.
### How to issue and add trust
- `issue_trust(issuer: PeerId, issued_for: PeerId, expires_at_sec: u64) -> ?Trust, ?Error`
- `import_trust(trust: Trust, issuer: PeerId) -> ?Error`
- `add_trust(node: PeerId, issuer: PeerId, issued_for: PeerId, expires_at_sec: u64) -> ?Error`
Let's issue trust, and import it to our relay:
```rust
func issue_trust_by_me(issued_for: PeerId, expires_at_sec: u64):
trust, error <- issue_trust(INIT_PEER_ID, issued_for, expires_at_sec)
if trust == nil:
-- handle failure
Op.noop()
else:
on HOST_PEER_ID:
error <- import_trust(trust!, INIT_PEER_ID)
-- handle error
```
- `add_trust` is a combination of `issue_trust` and `import_trust`
- if you want to issue trust not by `INIT_PEER_ID` check the Sig service [docs](https://doc.fluence.dev/docs/fluence-js/3_in_depth#signing-service)
### How to revoke the trust
- `issue_revocation(revoked_by: PeerId, revoked: PeerId) -> ?Revocation, ?Error`
- `import_revocation(revocation: Revocation) -> ?Error`
- `revoke(node: PeerId, revoked_by: PeerId, revoked: PeerId) -> ?Error`
Let's revoke some peers by our peer id:
```rust
func revoke_peer(revoked: PeerId):
revocation, error <- issue_revocation(INIT_PEER_ID, revoked)
if revocation == nil:
-- handle failure
Op.noop()
else:
on HOST_PEER_ID:
error <- import_revocation(revocation!)
-- handle error
```
- `revoke` is a combination of `issue_revocation` and `import_revocation`
- if you want to issue revocation not by `INIT_PEER_ID` check the Sig service [docs](https://doc.fluence.dev/docs/fluence-js/3_in_depth#signing-service)
### How to get certificates
- `get_all_certs(issued_for: PeerId) -> AllCertsResult`
- `get_all_certs_from(issued_for: PeerId, issuer: PeerId) -> AllCertsResult`
- `get_host_certs() -> AllCertsResult`
- `get_host_certs_from(issuer: PeerId) -> AllCertsResult`
Let's get all certificates issued by us to our relay peer id (HOST_PEER_ID):
```rust
func get_certs_issued_by_me() -> AllCertsResult:
on HOST_PEER_ID:
result <- get_host_certs_from(INIT_PEER_ID)
<- result
```
- `get_host_certs` is just an alias for `get_all_certs(HOST_PEER_ID)`
- `_from` calls results contain only certificates with trust issued by `issuer`
### How to get weights
- `get_weight(peer_id: PeerId) -> WeightResult`
- `get_weight_from(peer_id: PeerId, issuer: PeerId) -> WeightResult`
Let's get our weight for certificates which contain trust by our relay
```rust
func get_our_weight() -> ?u32, ?string:
weight: ?u32
error: ?string
on HOST_PEER_ID:
result <- get_weight_from(INIT_PEER_ID, HOST_PEER_ID)
if result.success:
weight <<- result.weight
else:
error <<- result.error
<- weight, error
```
- `get_weight` returns result among all the certificates, on the other hand, `get_weight_from` return certificates containing trust by the issuer only
## How to use it in TS/JS
1. Add `export.aqua` as in the Aqua [documentation](https://doc.fluence.dev/aqua-book/libraries#in-typescript-and-javascript)
2. Add the following to your dependencies
- `@fluencelabs/trust-graph`
- `@fluencelabs/aqua`
- `@fluencelabs/aqua-lib`
- `@fluencelabs/fluence`
- `@fluencelabs/fluence-network-environment`
3. Import dependencies
```typescript
import * as tg from "./generated/export";
import { Fluence, KeyPair } from "@fluencelabs/fluence";
import { krasnodar, Node } from "@fluencelabs/fluence-network-environment";
```
4. Create a client (specify keypair if you are node owner
[link](https://github.com/fluencelabs/node-distro/blob/main/fluence/Config.default.toml#L9))
```typescript
await Fluence.start({ connectTo: relay /*, KeyPair: builtins_keypair*/});
```
5. Add a root and issue root trust.
```typescript
let peer_id = Fluence.getStatus().peerId;
let relay = Fluence.getStatus().relayPeerId;
assert(peer_id !== null);
assert(relay !== null);
let max_chain_len = 2;
let far_future = 99999999999999;
let error = await tg.add_root_trust(relay, peer_id, max_chain_len, far_future);
if (error !== null) {
console.log(error);
}
```
6. By default, trusts/revocations signed with the client's private key. To sign with different keys see the Sig service [documentation](https://doc.fluence.dev/docs/fluence-js/3_in_depth#signing-service).
```typescript
// issue signed trust
let error = await tg.issue_trust(relay, peer_id, issued_for_peer_id, expires_at_sec);
if (error !== null) {
console.log(error);
}
```
## Use cases
### Create a trusted subnetwork
You can organize a subnetwork with peers trusted by your choice or chosen metrics. So you can treat trusts given by a peer (or a key) as evidence.
Let's consider we have peers A, B and C:
- Choose a peer A as an authority, set it as a root for the local TrustGraphs on all peers
- Issue and put self-signed by a peer A trust as a root trust
- Issue trusts by a peer A a to peer B and a peer C, and put them on all peers
- So for a call `get_weight_from(targetPeer, peerA)` will reflect whether targetPeer is in a subnetwork ABC
### Service permission management
You can specify in runtime who can access service functionality based on the local TrustGraph (certificates, weights). It's possible to check where the proof comes from based on [tetraplets](https://doc.fluence.dev/docs/knowledge_security). For example, only peers that have a non-zero weight can execute a service function `trusted_call(weight: WeightResult) -> u8`.
So if you want to have service permission management you should follow the steps:
- Pass `WeightResult` from TrustGraph to the function that you need to control:
```rust
...
weight_result <- get_weight(INIT_PEER_ID)
result <- MyService.trusted_call(weight_result)
...
```
- Inside your service you need to check tetraplets like [this](https://github.com/fluencelabs/registry/blob/main/service/src/misc.rs#L37) to be sure that they are resulted from the local TrustGraph
- Add `INIT_PEER_ID` or another choosen key as a root
- Issue trust to peers that can call this function:
```rust
func grant_access(issued_for: PeerId, expires_at_sec: u64):
error <- add_trust(INIT_PEER_ID, issued_for, expires_at_sec)
if error != nil
-- handle error
```
### Label trusted peers and execute computation only on this peers
See [example](./example):
- How to call [`trust-graph`](./example/index.ts) functions in TS/JS
- Step-by-step description [`README`](./example/README.md)
## FAQ
- Can a weight change during time?
- If the shortest path to a root changes, in case of trust expiration, importing or revocation, a weight also changes.
- What does a zero weight mean?
- A zero weight means there is no trust and path from any roots to the target peer.
- How we can interpret a certificate and/or a peer weight?
- A certificate contains a path from the root to the target peer we are looking for. A weight represents the presence of these certificates and peers' closeness to the root.
- How are weights calculated and based on what feedback?
- Weights are calculated based on the existence of a chain of trusts from the roots. For example, if we have a root with a maximum chain length equal 4 and have a chain `R -> A -> B -> C`, so the corresponding weights of peers are `8`, `4`, `2`, `1`. Weights are the same if there are no changes in the paths.
As long as we have no metrics, all trust/revocation logic is a TrustGraph user's responsibility.
- How do I set all weights to untrusted and then increase trust in a peer over time?
- All peers are untrusted by default. Trust is unmeasured, weight represents how far the peer is from the root, the bigger weight, the closer to the root, so if you want to increase the weight of the target peer, you should obtain the trust from the root or peers who are closer to the root than this peer.
- How do I know that other peers are using the same processes to update weights?
- Weights calculated **locally** based on certificates that contain immutable signed trusts. Weights are subjective and have meaning only locally to this exact peer.
- Can I start my own instance of a trust graph or is there only a global version available?
- Every Fluence node is bundled with a builtin TrustGraph instance, but you can change or remove any service you want if you're a node owner.
## API
High-level API is defined in the [trust-graph-api.aqua](./aqua/trust-graph-api.aqua) module. API Reference soon will be available in the documentation.
## Directory structure
- [`src`](./src) is the main project with all trust graph logic
- [`keypair`](./keypair) directory is an abstracted cryptographical layer (key pairs, public keys, signatures, etc.)
- [`service`](./service) is a package that provides `marine` API and could be compiled to a Wasm file. It uses` SQLite` as storage
- [`example`](./example) is a `js` script that shows how to use Trust Graph to label peers
- [`builtin-package`](./builtin-package) contains blueprint, configs and scripts for generation builtin package locally or via CI
- [`admin`](./admin) is a `js` script used to generate `builtin-package/on_start.json` which contains certificates for Fluence Labs nodes
## Learn Aqua
* [Aqua Book](https://fluence.dev/aqua-book/)
* [Aqua Playground](https://github.com/fluencelabs/aqua-playground)
* [Aqua repo](https://github.com/fluencelabs/aqua)

View File

@ -1,6 +1,14 @@
# How to generate export certificates
1. Go to `local-network`
2. Run `docker compose up -d` to start Fluence node
3. Go back to `../admin`
4. Put `root_secret_key.ed25519` and `issuer_secret_key.ed25519` to folder
5. Run `npm run start`
1. Go to `local-network` if you want to use local node
- Run `docker compose up -d` to start Fluence node
- Go back to `../admin`
2. Put `root_secret_key.ed25519` and `issuer_secret_key.ed25519` to folder
3. Run `npm i`
4. Run `npm run start {env}` where `{env}` should be `testnet`/`krasnodar`/`stage` or `local`
`root_secret_key.ed25519` and `issuer_secret_key.ed25519` are secret and owned by Fluence Labs team. Root key is for
all Fluence Labs relations. Trust from issuer key to any peer id means that this peer is official Fluence Labs peer.
isFluencePeer method from [trust-graph-api.aqua](./aqua/trust-graph-api.aqua) module checks these relations. You can build your own
structure of peers similarly.
`example_secret_key.ed25519` publicly available and used for test purposes.

View File

@ -1,4 +1,4 @@
import get_trust_bytes, issue_trust from "../../aqua/trust-graph-api.aqua"
import "@fluencelabs/trust-graph/trust-graph.aqua"
export get_trust_bytes, issue_trust
import "@fluencelabs/aqua-lib/builtin.aqua"
@ -7,3 +7,13 @@ func timestamp_sec(node: string) -> u64:
on node:
result <- Peer.timestamp_sec()
<- result
func get_trust_bytes(node: string, issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64) -> GetTrustBytesResult:
on node:
result <- TrustGraph.get_trust_bytes(issued_for_peer_id, expires_at_sec, issued_at_sec)
<- result
func issue_trust(node: string, issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64, trust_bytes: []u8) ->IssueTrustResult:
on node:
result <- TrustGraph.issue_trust(issued_for_peer_id, expires_at_sec, issued_at_sec, trust_bytes)
<- result

View File

@ -49,28 +49,34 @@ async function main(environment: Node[]) {
Fluence.getStatus().peerId,
Fluence.getStatus().relayPeerId
);
let root_sk_b58 = fs.readFileSync("./root_secret_key.ed25519").toString();
let issuer_sk_b58 = fs.readFileSync("./issuer_secret_key.ed25519").toString();
let example_sk_b58 = fs.readFileSync("../example_secret_key.ed25519").toString();
let root_kp = await KeyPair.fromEd25519SK(bs58.decode(root_sk_b58));
let issuer_kp = await KeyPair.fromEd25519SK(bs58.decode(issuer_sk_b58));
let example_kp = await KeyPair.fromEd25519SK(bs58.decode(example_sk_b58));
console.log("Root private key: %s", root_sk_b58);
console.log("Root peer id: %s", root_kp.Libp2pPeerId.toB58String());
console.log("Issuer private key: %s", issuer_sk_b58);
let cur_time = await timestamp_sec(node);
let expires_at = cur_time + 60 * 60 * 24 * 365;
let common_chain = [] as any;
// self-signed root trust
common_chain.push(await issue_trust_helper(node, root_kp, root_kp.Libp2pPeerId.toB58String(), root_kp.Libp2pPeerId.toB58String(), expires_at, cur_time));
// from root to issuer
common_chain.push(await issue_trust_helper(node, root_kp, root_kp.Libp2pPeerId.toB58String(), issuer_kp.Libp2pPeerId.toB58String(), expires_at, cur_time));
let expires_at = cur_time + 60 * 60 * 24 * 365 * 2;
let certificates = [];
// self-signed root trust
let root_trust = await issue_trust_helper(node, root_kp, root_kp.Libp2pPeerId.toB58String(), root_kp.Libp2pPeerId.toB58String(), expires_at, cur_time);
// from root to issuer
let issuer_trust = await issue_trust_helper(node, root_kp, root_kp.Libp2pPeerId.toB58String(), issuer_kp.Libp2pPeerId.toB58String(), expires_at, cur_time);
// from root to example
let example_trust = await issue_trust_helper(node, root_kp, root_kp.Libp2pPeerId.toB58String(), example_kp.Libp2pPeerId.toB58String(), expires_at, cur_time);
// cert for example key
certificates.push({chain: [root_trust, example_trust]});
for (let i = 0; i < krasnodar.length; i++) {
// from issuer to node
let trust = await issue_trust_helper(node, issuer_kp, issuer_kp.Libp2pPeerId.toB58String(), krasnodar[i].peerId, expires_at, cur_time);
let cert = {chain: [...common_chain, trust]};
let node_trust = await issue_trust_helper(node, issuer_kp, issuer_kp.Libp2pPeerId.toB58String(), krasnodar[i].peerId, expires_at, cur_time);
// cert for every krasnodar node
let cert = {chain: [root_trust, issuer_trust, node_trust]};
certificates.push(cert);
}

11603
admin/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,10 @@
"author": "Fluence Labs",
"license": "MIT",
"dependencies": {
"@fluencelabs/aqua": "0.4.1-240",
"@fluencelabs/aqua-lib": "0.2.0",
"@fluencelabs/fluence": "0.14.3",
"@fluencelabs/fluence-network-environment": "^1.0.10",
"@fluencelabs/aqua": "^0.10.0",
"@fluencelabs/aqua-lib": "^0.6.0",
"@fluencelabs/fluence": "^0.27.5",
"@fluencelabs/fluence-network-environment": "^1.0.13",
"@fluencelabs/trust-graph": "file:../aqua",
"bs58": "^4.0.1"
},

51
aqua/CHANGELOG.md Normal file
View File

@ -0,0 +1,51 @@
# Changelog
## [0.4.7](https://github.com/fluencelabs/trust-graph/compare/trust-graph-api-v0.4.6...trust-graph-api-v0.4.7) (2023-07-04)
### Miscellaneous Chores
* **trust-graph-api:** Synchronize trust-graph, wasm and api versions
## [0.4.6](https://github.com/fluencelabs/trust-graph/compare/trust-graph-api-v0.4.5...trust-graph-api-v0.4.6) (2023-06-30)
### Reverts
* release master ([#110](https://github.com/fluencelabs/trust-graph/issues/110)) ([d80a43b](https://github.com/fluencelabs/trust-graph/commit/d80a43bcff721aff8fadf3d2d5c252804ce27a6c))
## [0.4.5](https://github.com/fluencelabs/trust-graph/compare/trust-graph-api-v0.4.4...trust-graph-api-v0.4.5) (2023-05-09)
### Miscellaneous Chores
* **trust-graph-api:** Synchronize trust-graph, wasm and api versions
## [0.4.4](https://github.com/fluencelabs/trust-graph/compare/trust-graph-api-v0.4.3...trust-graph-api-v0.4.4) (2023-05-09)
### Miscellaneous Chores
* **trust-graph-api:** Synchronize trust-graph, wasm and api versions
## [0.4.3](https://github.com/fluencelabs/trust-graph/compare/trust-graph-api-v0.4.1...trust-graph-api-v0.4.3) (2023-05-08)
### Miscellaneous Chores
* **trust-graph-api:** Synchronize trust-graph, wasm and api versions
## [0.4.1](https://github.com/fluencelabs/trust-graph/compare/trust-graph-api-v0.4.0...trust-graph-api-v0.4.1) (2023-04-13)
### Bug Fixes
* **deps:** update aqua to 0.10.3 ([fe902ac](https://github.com/fluencelabs/trust-graph/commit/fe902acc50a6b4c6bf97c487f3e47ae0f5ef8a95))
* **deps:** update aqua-lib to 0.7.0 ([fe902ac](https://github.com/fluencelabs/trust-graph/commit/fe902acc50a6b4c6bf97c487f3e47ae0f5ef8a95))
## [0.4.0](https://github.com/fluencelabs/trust-graph/compare/trust-graph-api-v0.3.2...trust-graph-api-v0.4.0) (2023-03-15)
### Miscellaneous Chores
* **trust-graph-api:** Synchronize trust-graph, wasm and api versions

39
aqua/labelling.aqua Normal file
View File

@ -0,0 +1,39 @@
import "misc.aqua"
import get_host_certs_from from "trust-graph-api.aqua"
alias Error: string
-- TrustGraph builtin distributed with predefined certificates which used to identify Fluence Labs peers.
-- Each certificate contains 3 trusts: self-signed fluence root trust, trust for label trust and trust to target peer.
--
-- Usage:
-- on target_node:
-- result, error <- isFluencePeer()
--
-- Returns:
-- `true, nil` if `target_node` is identified as official Fluence Labs peer
-- `false, nil` otherwise
--
-- Errors:
-- if get_host_certs_from failed, `nil, error_msg` is returned
func isFluencePeer() -> ?bool, ?Error:
fluence_root_peer_id = "12D3KooWNbZKaPWRZ8wgjGvrxdJFz9Fq5uVwkR6ERV1f74HhPdyB"
label_peer_id = "12D3KooWM45u7AQxsb4MuQJNYT3NWHHMLU7JTbBV66RTfF3KSzdR"
result: ?bool
error: *Error
-- get all certs issued by `label_peer_id` to current host
certs_result <- get_host_certs_from(label_peer_id)
if certs_result.success:
for cert <- certs_result.certificates:
len <- TrustOp.array_length(cert.chain)
if len == 3:
if cert.chain!0.issued_for == fluence_root_peer_id:
if cert.chain!1.issued_for == label_peer_id:
result <<- true
if result == nil:
result <<- false
else:
error <<- certs_result.error
<- result, error

15
aqua/misc.aqua Normal file
View File

@ -0,0 +1,15 @@
import "trust-graph.aqua"
alias Error: string
-- helpers for isFluencePeer
service TrustOp("op"):
array_length(a: []Trust) -> u32
service BoolOp("op"):
array_length(a: []bool) -> u32
-- check if error is not nil and append to error_stream
func append_error(error_stream: *Error, error: ?Error):
if error != nil:
error_stream <<- error!

11629
aqua/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,12 +1,12 @@
{
"name": "@fluencelabs/trust-graph",
"version": "0.1.12",
"version": "0.4.7",
"description": "Aqua Trust Graph API library",
"files": [
"*.aqua"
],
"dependencies": {
"@fluencelabs/aqua-lib": "0.2.0"
"@fluencelabs/aqua-lib": "^0.7.0"
},
"scripts": {
"generate-aqua": "../service/build.sh",
@ -31,6 +31,6 @@
},
"homepage": "https://github.com/fluencelabs/trust-graph#readme",
"devDependencies": {
"@fluencelabs/aqua": "0.4.1-240"
"@fluencelabs/aqua": "^0.10.3"
}
}

View File

@ -1,101 +1,214 @@
import Sig, Peer, PeerId from "@fluencelabs/aqua-lib/builtin.aqua"
import "misc.aqua"
import "trust-graph.aqua"
import "@fluencelabs/aqua-lib/builtin.aqua"
func get_trust_bytes(node: string, issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64) -> GetTrustBytesResult:
on node:
result <- TrustGraph.get_trust_bytes(issued_for_peer_id, expires_at_sec, issued_at_sec)
alias Error: string
-- Call context: any node with registered `trust-graph` service
-- Set `peer_id` as a root
-- Self-signed trust should be added in next call for correct behaviour
-- `max_chain_len` specifies maximum chain length after root trust,
-- if `max_chain_len` is zero there is no trusts except self-signed root trust in certificates for this root
func set_root(peer_id: PeerId, max_chain_len: u32) -> SetRootResult:
result <- TrustGraph.set_root(peer_id, max_chain_len)
<- result
func issue_trust(node: string, issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64, trust_bytes: []u8) -> IssueTrustResult:
on node:
result <- TrustGraph.issue_trust(issued_for_peer_id, expires_at_sec, issued_at_sec, trust_bytes)
<- result
-- Call context: %init_peer_id%
-- Create on relay and sign trust on client
-- If `issuer` is not %init_peer_id%, Sig service with `issuer` peer id as service id should be defined
-- Errors:
-- If TrustGraph.get_trust_bytes or TrustGraph.issue_trust fails, (nil, error) is returned.
func issue_trust(issuer: PeerId, issued_for: PeerId, expires_at_sec: u64) -> ?Trust, ?Error:
-- after marine-web release this will be done on %init_peer_id%
on HOST_PEER_ID:
issued_at_sec <- Peer.timestamp_sec()
bytes <- TrustGraph.get_trust_bytes(issued_for, expires_at_sec, issued_at_sec)
func verify_trust(node: string, trust: Trust, issuer_peer_id: string) -> VerifyTrustResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.verify_trust(trust, issuer_peer_id, timestamp_sec)
<- result
result: ?Trust
error: *Error
if bytes.success:
Sig issuer
sig_res <- Sig.sign(bytes.result)
func add_trust(node: string, trust: Trust, issuer_peer_id: string) -> AddTrustResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.add_trust(trust, issuer_peer_id, timestamp_sec)
<- result
func add_root(node: string, peer_id: string, weight_factor: u32) -> AddRootResult:
on node:
result <- TrustGraph.add_root(peer_id, weight_factor)
<- result
func get_weight(node: string, peer_id: string) -> WeightResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_weight(peer_id, timestamp_sec)
<- result
func get_all_certs(node: string, issued_for: string) -> AllCertsResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_all_certs(issued_for, timestamp_sec)
<- result
func get_host_certs(node: string, issued_for: string) -> AllCertsResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_host_certs(timestamp_sec)
<- result
func get_host_certs_from(node: string, issuer: string) -> AllCertsResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_host_certs_from(issuer, timestamp_sec)
<- result
func insert_cert(node: string, certificate: Certificate) -> InsertResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.insert_cert(certificate, timestamp_sec)
<- result
func get_revoke_bytes(node: string, revoked_peer_id: string, revoked_at: u64) -> GetRevokeBytesResult:
on node:
result <- TrustGraph.get_revoke_bytes(revoked_peer_id, revoked_at)
<- result
func issue_revocation(node: string, revoked_peer_id: string, revoked_by_peer_id: string, revoked_at_sec: u64, signature_bytes: []u8) -> IssueRevocationResult:
on node:
result <- TrustGraph.issue_revocation(revoked_peer_id, revoked_by_peer_id, revoked_at_sec, signature_bytes)
<- result
func revoke(node: string, revoke: Revoke) -> RevokeResult:
on node:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.revoke(revoke, timestamp_sec)
<- result
service TrustOp("op"):
array_length(a: []Trust) -> u64
service BoolOp("op"):
array_length(a: []bool) -> u64
-- https://github.com/fluencelabs/trust-graph/issues/26
func isFluencePeer() -> bool:
certs_result <- get_host_certs_from(HOST_PEER_ID, "12D3KooWM45u7AQxsb4MuQJNYT3NWHHMLU7JTbBV66RTfF3KSzdR")
resultBox: *bool
if certs_result.success:
for cert <- certs_result.certificates:
len <- TrustOp.array_length(cert.chain)
if len == 3:
if cert.chain!0.issued_for == "12D3KooWNbZKaPWRZ8wgjGvrxdJFz9Fq5uVwkR6ERV1f74HhPdyB":
if cert.chain!1.issued_for == "12D3KooWM45u7AQxsb4MuQJNYT3NWHHMLU7JTbBV66RTfF3KSzdR":
resultBox <<- true
result_len <- BoolOp.array_length(resultBox)
result: *bool
if result_len == 0:
result <<- false
if sig_res.success:
on HOST_PEER_ID:
issue_result <- TrustGraph.issue_trust(issued_for, expires_at_sec, issued_at_sec, sig_res.signature!)
if issue_result.success:
result <<- issue_result.trust
else:
error <<- issue_result.error
else:
error <<- sig_res.error!
else:
result <<- true
<- result!
error <<- bytes.error
<- result, error
-- Call context: any node with registered `trust-graph` service
-- Add trust to TG
-- Errors:
-- If TrustGraph.add_trust fails, error is returned.
func import_trust(trust: Trust, issuer: PeerId) -> ?Error:
error: *Error
timestamp_sec <- Peer.timestamp_sec()
add_result <- TrustGraph.add_trust(trust, issuer, timestamp_sec)
if add_result.success != true:
error <<- add_result.error
<- error
-- Call context: %init_peer_id%
-- Issue trust and add to TG instance on `node`
-- If `issuer` is not %init_peer_id%, Sig service with `issuer` peer id as service id should be defined
-- Errors:
-- If issue_trust or import_trust fails, error is returned.
func add_trust(node: PeerId, issuer: PeerId, issued_for: PeerId, expires_at_sec: u64) -> ?Error:
trust, issue_error <- issue_trust(issuer, issued_for, expires_at_sec)
error: *Error
if issue_error != nil:
error <<- issue_error!
else:
on node:
import_error <- import_trust(trust!, issuer)
append_error(error, import_error)
<- error
-- Call context: %init_peer_id%
-- Set `peer_id` as a root and add self-signed trust to TG instance on `node`
-- If `peer_id` is not %init_peer_id%, Sig service with `peer_id` as service id should be defined
-- Errors:
-- If issue_trust, import_trust or set_root fails, error is returned.
func add_root_trust(node: PeerId, peer_id: PeerId, max_chain_len: u32, expires_at_sec: u64) -> ?Error:
trust, issue_error <- issue_trust(peer_id, peer_id, expires_at_sec)
error: *Error
if issue_error != nil:
error <<- issue_error!
else:
on node:
set_root_result <- set_root(peer_id, max_chain_len)
if set_root_result.success:
import_error <- import_trust(trust!, peer_id)
append_error(error, import_error)
else:
error <<- set_root_result.error
<- error
-- Call context: any node with registered `trust-graph` service
-- Check signature and expiration time of trust
func verify_trust(trust: Trust, issuer: PeerId) -> VerifyTrustResult:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.verify_trust(trust, issuer, timestamp_sec)
<- result
-- Call context: any node with registered `trust-graph` service
-- Get the maximum weight of trust for `peer_id`
-- Trust has weight if there is at least 1 trust chain from one of the roots
func get_weight(peer_id: PeerId) -> WeightResult:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_weight(peer_id, timestamp_sec)
<- result
-- Call context: any node with registered `trust-graph` service
-- Get maximum weight of trust for `peer_id` among all chains which contain trust from `issuer`
func get_weight_from(peer_id: PeerId, issuer: PeerId) -> WeightResult:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_weight_from(peer_id, issuer, timestamp_sec)
<- result
-- Call context: %init_peer_id%
-- Create revocation signed by %init_peer_id%
-- If `revoked_by` is not %init_peer_id%, Sig service with `revoked_by` peer id as service id should be defined
-- Errors:
-- If TrustGraph.get_revocation_bytes or TrustGraph.issue_revocation fails, (nil, error) is returned.
func issue_revocation(revoked_by: PeerId, revoked: PeerId) -> ?Revocation, ?Error:
-- after marine-web release this will be done on %init_peer_id%
on HOST_PEER_ID:
issued_at_sec <- Peer.timestamp_sec()
bytes <- TrustGraph.get_revocation_bytes(revoked, issued_at_sec)
result: ?Revocation
error: *Error
if bytes.success:
Sig revoked_by
sig_res <- Sig.sign(bytes.result)
if sig_res.success:
on HOST_PEER_ID:
issue_result <- TrustGraph.issue_revocation(revoked_by, revoked, issued_at_sec, sig_res.signature!)
if issue_result.success:
result <<- issue_result.revocation
else:
error <<- issue_result.error
else:
error <<- sig_res.error!
else:
error <<- bytes.error
<- result, error
-- Call context: any node with registered `trust-graph` service
-- Import revocation to TG
-- Errors:
-- If TrustGraph.revoke fails, error is returned.
func import_revocation(revocation: Revocation) -> ?Error:
error: *Error
timestamp_sec <- Peer.timestamp_sec()
add_result <- TrustGraph.revoke(revocation, timestamp_sec)
if add_result.success != true:
error <<- add_result.error
<- error
-- Call context: %init_peer_id%
-- Revoke all certificates on `node` TG instance
-- which contain path from %init_peer_id% to `revoked_peer_id`
-- If `revoked_by` is not %init_peer_id%, Sig service with `revoked_by` peer id as service id should be defined
-- Errors:
-- if issue_revocation or import_revocation fails, error is returned.
func revoke(node: PeerId, revoked_by: PeerId, revoked: PeerId) -> ?Error:
revocation, issue_error <- issue_revocation(revoked_by, revoked)
error: *Error
if revocation == nil:
error <<- issue_error!
else:
on node:
import_error <- import_revocation(revocation!)
append_error(error, import_error)
<- error
-- Call context: any node with registered `trust-graph` service
-- Return all certificates issued for current node which contains trust from `issuer`
func get_host_certs_from(issuer: PeerId) -> AllCertsResult:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_host_certs_from(issuer, timestamp_sec)
<- result
-- Call context: any node with registered `trust-graph` service
-- Return all certificates issued for given peer id
func get_all_certs(issued_for: PeerId) -> AllCertsResult:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_all_certs(issued_for, timestamp_sec)
<- result
-- Call context: any node with registered `trust-graph` service
-- Return all certificates issued for given peer id which contains trust from `issuer`
func get_all_certs_from(issued_for: PeerId, issuer: PeerId) -> AllCertsResult:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_all_certs_from(issued_for, issuer, timestamp_sec)
<- result
-- Call context: any node with registered `trust-graph` service
-- Return all certificates issued for current node
func get_host_certs() -> AllCertsResult:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.get_host_certs(timestamp_sec)
<- result
-- Call context: any node with registered `trust-graph` service
-- Insert certificate to TG instance on current node
func insert_cert(certificate: Certificate) -> InsertResult:
timestamp_sec <- Peer.timestamp_sec()
result <- TrustGraph.insert_cert(certificate, timestamp_sec)
<- result

View File

@ -1,9 +1,5 @@
module TrustGraph declares *
data AddRootResult:
success: bool
error: string
data AddTrustResult:
success: bool
error: string
@ -24,6 +20,18 @@ data AllCertsResult:
certificates: []Certificate
error: string
data Revocation:
revoked_peer_id: string
revoked_at: u64
signature: string
sig_type: string
revoked_by: string
data ExportRevocationsResult:
success: bool
revocations: []Revocation
error: string
data GetRevokeBytesResult:
success: bool
error: string
@ -38,17 +46,10 @@ data InsertResult:
success: bool
error: string
data Revoke:
revoked_peer_id: string
revoked_at: u64
signature: string
sig_type: string
revoked_by: string
data IssueRevocationResult:
success: bool
error: string
revoke: Revoke
revocation: Revocation
data IssueTrustResult:
success: bool
@ -59,6 +60,10 @@ data RevokeResult:
success: bool
error: string
data SetRootResult:
success: bool
error: string
data VerifyTrustResult:
success: bool
error: string
@ -70,18 +75,20 @@ data WeightResult:
error: string
service TrustGraph("trust-graph"):
add_root(peer_id: string, weight_factor: u32) -> AddRootResult
add_trust(trust: Trust, issuer_peer_id: string, timestamp_sec: u64) -> AddTrustResult
export_revocations(issued_for: string) -> ExportRevocationsResult
get_all_certs(issued_for: string, timestamp_sec: u64) -> AllCertsResult
get_all_certs_from(issued_for: string, issuer: string, timestamp_sec: u64) -> AllCertsResult
get_host_certs(timestamp_sec: u64) -> AllCertsResult
get_host_certs_from(issuer: string, timestamp_sec: u64) -> AllCertsResult
get_revoke_bytes(revoked_peer_id: string, revoked_at: u64) -> GetRevokeBytesResult
get_revocation_bytes(revoked_peer_id: string, revoked_at: u64) -> GetRevokeBytesResult
get_trust_bytes(issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64) -> GetTrustBytesResult
get_weight(peer_id: string, timestamp_sec: u64) -> WeightResult
get_weight_factor(max_chain_len: u32) -> u32
get_weight_from(peer_id: string, issuer: string, timestamp_sec: u64) -> WeightResult
insert_cert(certificate: Certificate, timestamp_sec: u64) -> InsertResult
insert_cert_raw(certificate: string, timestamp_sec: u64) -> InsertResult
issue_revocation(revoked_peer_id: string, revoked_by_peer_id: string, revoked_at_sec: u64, signature_bytes: []u8) -> IssueRevocationResult
issue_revocation(revoked_by_peer_id: string, revoked_peer_id: string, revoked_at_sec: u64, signature_bytes: []u8) -> IssueRevocationResult
issue_trust(issued_for_peer_id: string, expires_at_sec: u64, issued_at_sec: u64, trust_bytes: []u8) -> IssueTrustResult
revoke(revoke: Revoke, timestamp_sec: u64) -> RevokeResult
revoke(revoke: Revocation, timestamp_sec: u64) -> RevokeResult
set_root(peer_id: string, max_chain_len: u32) -> SetRootResult
verify_trust(trust: Trust, issuer_peer_id: string, timestamp_sec: u64) -> VerifyTrustResult

15
build.sh Executable file
View File

@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
# set current working directory to script directory to run script from everywhere
cd "$(dirname "$0")"
./service/build.sh
TARGET="distro/trust-graph-service/"
mkdir -p "$TARGET"
cp -v ./distro/init_certs.json service/artifacts/trust-graph.wasm service/artifacts/sqlite3.wasm distro/Config.toml "$TARGET"
cd distro
cargo build

View File

@ -1,7 +0,0 @@
{
"name": "trust-graph",
"dependencies": [
"name:sqlite3",
"name:trust-graph"
]
}

View File

@ -1,6 +1,7 @@
(seq
(seq
(call relay ("trust-graph" "add_root") ["12D3KooWNbZKaPWRZ8wgjGvrxdJFz9Fq5uVwkR6ERV1f74HhPdyB" 2] add_root_res)
; set fluence root peer id as TG root
(call relay ("trust-graph" "set_root") ["12D3KooWNbZKaPWRZ8wgjGvrxdJFz9Fq5uVwkR6ERV1f74HhPdyB" 5] add_root_res)
(xor
(match add_root_res.$.success! true
(null)
@ -14,6 +15,7 @@
(seq
(seq
(call relay ("peer" "timestamp_sec") [] cur_time)
; insert all certificates from on_start.json
(call relay ("trust-graph" "insert_cert") [i cur_time] insert_result)
)
(xor

File diff suppressed because one or more lines are too long

View File

@ -3,20 +3,39 @@ set -o pipefail -o nounset -o errexit
# set current working directory to script directory to run script from everywhere
cd "$(dirname "$0")"
SCRIPT_DIR="$(pwd)"
PACKAGE_DIR="$(pwd)/../package/trust-graph"
(
rm -rf $PACKAGE_DIR/*
mkdir -p $PACKAGE_DIR
)
(
echo "*** copy wasm files ***"
cd ../service
cp artifacts/*.wasm "$SCRIPT_DIR"
cp artifacts/*.wasm "$PACKAGE_DIR"
)
(
echo "*** copy on_start script ***"
cp on_start.json "$PACKAGE_DIR"
cp on_start.air "$PACKAGE_DIR"
)
TRUST_GRAPH_CID=$(ipfs add -q --only-hash --cid-version=1 --chunker=size-262144 $PACKAGE_DIR/trust-graph.wasm)
SQLITE_CID=$(ipfs add -q --only-hash --cid-version=1 --chunker=size-262144 $PACKAGE_DIR/sqlite3.wasm)
mv $PACKAGE_DIR/trust-graph.wasm "$PACKAGE_DIR"/"$TRUST_GRAPH_CID".wasm
mv $PACKAGE_DIR/sqlite3.wasm "$PACKAGE_DIR"/"$SQLITE_CID".wasm
cp trust-graph_config.json "$PACKAGE_DIR"/"$TRUST_GRAPH_CID"_config.json
cp sqlite3_config.json "$PACKAGE_DIR"/"$SQLITE_CID"_config.json
# write blueprint.json
echo "{}" | jq --arg trust_graph_cid "$TRUST_GRAPH_CID" --arg sqlite_cid "$SQLITE_CID" '{"name": "trust-graph", "dependencies":[{"/":$sqlite_cid},{"/":$trust_graph_cid}]}' > "$PACKAGE_DIR/blueprint.json"
(
echo "*** create builtin distribution package ***"
cd ..
mv builtin-package trust-graph
tar --exclude="package.sh" -f trust-graph.tar.gz -zcv ./trust-graph
mv trust-graph builtin-package
cd $PACKAGE_DIR/..
tar -f ../trust-graph.tar.gz -zcv ./trust-graph
)
echo "*** done ***"

20
distro/CHANGELOG.md Normal file
View File

@ -0,0 +1,20 @@
# Changelog
## [0.4.7](https://github.com/fluencelabs/trust-graph/compare/distro-v0.4.6...distro-v0.4.7) (2023-07-04)
### Miscellaneous Chores
* **distro:** Synchronize trust-graph, wasm and api versions
## [0.4.6](https://github.com/fluencelabs/trust-graph/compare/distro-v0.4.5...distro-v0.4.6) (2023-06-30)
### Features
* add distro crate [fixes NET-463] ([#93](https://github.com/fluencelabs/trust-graph/issues/93)) ([3ba3855](https://github.com/fluencelabs/trust-graph/commit/3ba3855892ae355962212a0a42099dd9f9820800))
### Reverts
* release master ([#110](https://github.com/fluencelabs/trust-graph/issues/110)) ([d80a43b](https://github.com/fluencelabs/trust-graph/commit/d80a43bcff721aff8fadf3d2d5c252804ce27a6c))

18
distro/Cargo.toml Normal file
View File

@ -0,0 +1,18 @@
[package]
name = "trust-graph-distro"
version = "0.10.4"
edition = "2021"
build = "build.rs"
license = "Apache-2.0"
include = [ "/src", "build.rs", "Cargo.toml", "trust-graph-service"]
description = "Distribution package for the trust-graph service"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
maplit = "1.0.2"
serde = "1.0.160"
serde_json = "1.0.96"
lazy_static = "1.4.0"
[build-dependencies]
built = "0.6.0"

15
distro/Config.toml Normal file
View File

@ -0,0 +1,15 @@
modules_dir = "."
total_memory_limit = "Infinity"
[[module]]
name = "sqlite3"
logger_enabled = true
[module.wasi]
mapped_dirs = { "tmp" = "data" }
[[module]]
name = "trust-graph"
logger_enabled = true
[module.wasi]
mapped_dirs = { "tmp" = "data" }

3
distro/build.rs Normal file
View File

@ -0,0 +1,3 @@
fn main() {
built::write_built_file().expect("Failed to acquire build-time information")
}

1
distro/init_certs.json Normal file

File diff suppressed because one or more lines are too long

60
distro/src/lib.rs Normal file
View File

@ -0,0 +1,60 @@
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "cargo-clippy"))]
pub const TRUST_GRAPH_WASM: &[u8] = include_bytes!("../trust-graph-service/trust-graph.wasm");
#[cfg(feature = "cargo-clippy")]
pub const TRUST_GRAPH_WASM: &[u8] = &[];
#[cfg(not(feature = "cargo-clippy"))]
pub const SQLITE_WASM: &[u8] = include_bytes!("../trust-graph-service/sqlite3.wasm");
#[cfg(feature = "cargo-clippy")]
pub const SQLITE_WASM: &[u8] = &[];
#[cfg(not(feature = "cargo-clippy"))]
pub const CONFIG: &[u8] = include_bytes!("../trust-graph-service/Config.toml");
#[cfg(feature = "cargo-clippy")]
pub const CONFIG: &[u8] = &[];
#[cfg(not(feature = "cargo-clippy"))]
pub const KRAS_CERTS_JSON: &str = include_str!("../trust-graph-service/init_certs.json");
#[cfg(feature = "cargo-clippy")]
pub const KRAS_CERTS_JSON: &str = "{}";
pub mod build_info {
include!(concat!(env!("OUT_DIR"), "/built.rs"));
}
pub use build_info::PKG_VERSION as VERSION;
pub fn modules() -> std::collections::HashMap<&'static str, &'static [u8]> {
maplit::hashmap! {
"sqlite3" => SQLITE_WASM,
"trust-graph" => TRUST_GRAPH_WASM,
}
}
#[derive(Deserialize, Serialize)]
pub struct Certs {
pub root_node: String,
pub max_chain_length: u32,
pub certs: Vec<Cert>,
}
#[derive(Deserialize, Serialize)]
pub struct Cert {
pub chain: Vec<Trust>,
}
#[derive(Deserialize, Serialize)]
pub struct Trust {
pub issued_for: String,
pub expires_at: u64,
pub signature: String,
pub sig_type: String,
pub issued_at: u64,
}
lazy_static! {
pub static ref KRAS_CERTS: Certs = serde_json::from_str(KRAS_CERTS_JSON).unwrap();
}

View File

@ -1,5 +1,49 @@
# Run example locally
1. Go to `local-network`
## Description
This example shows how to use Trust Graph for code execution only on trusted peers. There are some `trusted_computation` which can only be performed on a trusted peer. The label is determined by the presence of the certificate from `INIT_PEER_ID` to this peer. We use peer id from [`example_secret_key.ed25519`](../example_secret_key.ed25519) as `INIT_PEER_ID` since every node bundled with the certificate issued to this key, it should be used only for test purposes.
## Run example on network
1. Run `npm i`
2. Run `npm run start`
## Run example on network
1. Run `npm i`
2. Run `npm run start`
## Run example locally
1. Go to `local-network`
2. Run `docker compose up -d` to start Fluence node
3. Go back to `../example`
4. Run `npm run start`
3. It takes some time depending on your machine for node to start and builtin services deployed. Wait for this log line: `[2022-07-06T11:33:50.782054Z INFO particle_node] Fluence has been successfully started.`
4. Go back to `../example`
5. Run `npm i`
6. Run `npm run start local`
## Expected output
After successful execution you will get this result:
```
In this example we try to execute some trusted computations based on trusts
📘 Will connect to testNet
📗 created a fluence peer 12D3KooWD2vAZva1u3TQgoxebBUBsaGMNawKjVkp57M6UcwNwXNv with relay 12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9
📕 Trusted computation on node 12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9 failed, error: there is no certs for this peer
📕 Trusted computation on node 12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz failed, error: there is no certs for this peer
📕 Trusted computation on node 12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er failed, error: there is no certs for this peer
🌀 Issue trust to nodeB 12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz and nodeC: 12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er
Trust issued for 12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz successfully added
Trust issued for 12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er successfully added
📕 Trusted computation on node 12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9 failed, error: there is no certs for this peer
📗 Trusted computation on node 12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz successful, result is 5
📗 Trusted computation on node 12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er successful, result is 5
🚫 Revoke trust to nodeB
Trust issued for 12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz revoked
📕 Trusted computation on node 12D3KooWEXNUbCXooUwHrHBbrmjsrpHXoEphPwbjQXEGyzbqKnE9 failed, error: there is no certs for this peer
📕 Trusted computation on node 12D3KooWMhVpgfQxBLkQkJed8VFNvgN4iE6MD7xCybb1ZYWW2Gtz failed, error: there is no certs for this peer
📗 Trusted computation on node 12D3KooWHk9BjDQBUqnavciRPhAYFvqKBe4ZiPPvde7vDaqgn5er successful, result is 5
```

View File

@ -0,0 +1,31 @@
import "@fluencelabs/trust-graph/trust-graph-api.aqua"
import "@fluencelabs/trust-graph/trust-graph.aqua"
import "@fluencelabs/aqua-lib/builtin.aqua"
export trusted_computation
service CertOp("op"):
array_length(a: []Certificate) -> u32
service TrustedComputation("op"):
identity(s: u64) -> u64
func trusted_computation(node: string) -> ?u64, ?string:
result: ?u64
error: ?string
-- on our trusted relay
on HOST_PEER_ID:
-- get all certificates issued for given node by our client's peer id
certs_result <- get_all_certs_from(node, %init_peer_id%)
if certs_result.success:
len <- CertOp.array_length(certs_result.certificates)
-- if there is any certificate node is trusted and computation is possible
if len != 0:
on node:
result <- TrustedComputation.identity(5)
else:
error <<- "there is no certs for this peer"
else:
error <<- certs_result.error
<- result, error

View File

@ -1,10 +1,11 @@
import get_trust_bytes, issue_trust, verify_trust, add_trust, add_root, get_weight, get_all_certs, insert_cert, get_revoke_bytes, issue_revocation, revoke, isFluencePeer from "../../aqua/trust-graph-api.aqua"
module Export
import add_root_trust, add_trust, revoke from "@fluencelabs/trust-graph/trust-graph-api.aqua"
export add_root_trust, add_trust, revoke, timestamp_sec
import Peer from "@fluencelabs/aqua-lib/builtin.aqua"
export get_trust_bytes, issue_trust, verify_trust, add_trust, add_root, get_weight, get_all_certs, insert_cert, get_revoke_bytes, issue_revocation, revoke, isFluencePeer
alias PeerId: string
import "@fluencelabs/aqua-lib/builtin.aqua"
func timestamp_sec(node: string) -> u64:
on node:
func timestamp_sec() -> u64:
on HOST_PEER_ID:
result <- Peer.timestamp_sec()
<- result

View File

@ -14,21 +14,10 @@
* limitations under the License.
*/
import {
get_trust_bytes,
issue_trust,
verify_trust,
add_trust,
add_root,
get_weight,
timestamp_sec,
get_all_certs,
get_revoke_bytes,
issue_revocation,
revoke
} from "./generated/export";
import { Fluence, KeyPair } from "@fluencelabs/fluence";
import { Node } from "@fluencelabs/fluence-network-environment";
import { trusted_computation } from "./generated/computation";
import * as tg from "./generated/export";
import { Fluence, FluencePeer, KeyPair } from "@fluencelabs/fluence";
import { krasnodar, Node, testNet, stage } from "@fluencelabs/fluence-network-environment";
import assert from "assert";
const bs58 = require('bs58');
@ -43,82 +32,119 @@ let local: Node[] = [
multiaddr:
"/ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWRABanQHUn28dxavN9ZS1zZghqoZVAYtFpoN7FdtoGTFv",
},
{
peerId: "12D3KooWFpQ7LHxcC9FEBUh3k4nSCC12jBhijJv3gJbi7wsNYzJ5",
multiaddr:
"/ip4/127.0.0.1/tcp/9992/ws/p2p/12D3KooWFpQ7LHxcC9FEBUh3k4nSCC12jBhijJv3gJbi7wsNYzJ5",
},
];
async function add_trust_helper(node: string, issuer_kp: KeyPair, issuer_peer_id: string, issued_for_peer_id: string, expires_at_sec: number, issued_at_sec: number) {
let trust_metadata = await get_trust_bytes(node, issued_for_peer_id, expires_at_sec, issued_at_sec);
const signed_metadata = await issuer_kp.Libp2pPeerId.privKey.sign(Uint8Array.from(trust_metadata.result));
let trust = await issue_trust(node, issued_for_peer_id, expires_at_sec, issued_at_sec, Array.from(signed_metadata));
console.log("Issued trust %s", trust.trust);
let result = await verify_trust(node, trust.trust, issuer_peer_id);
console.log("Verify trust result: %s", result);
let result_add = await add_trust(node, trust.trust, issuer_peer_id);
console.log("Add trust result: %s", result_add);
async function revoke_all(relay: string, revoked_by: string, nodes: Node[]) {
for (var node of nodes) {
let error = await tg.revoke(relay, revoked_by, node.peerId);
if (error !== null) {
console.log(error)
}
}
}
async function revoke_helper(node: string, issuer_kp: KeyPair, revoked_by_peer_id: string, revoked_peer_id: string, revoked_at_sec: number) {
let trust_metadata = await get_revoke_bytes(node, revoked_peer_id, revoked_at_sec);
const signed_metadata = await issuer_kp.Libp2pPeerId.privKey.sign(Uint8Array.from(trust_metadata.result));
let revocation = await issue_revocation(node, revoked_peer_id, revoked_by_peer_id, revoked_at_sec, Array.from(signed_metadata));
console.log("Issued revocation %s", revocation.revoke);
let result_add = await revoke(node, revocation.revoke);
console.log("Revoke result: %s", result_add);
async function add_new_trust_checked(relay: string, issuer: string, issued_for_peer_id: string, expires_at_sec: number) {
let error = await tg.add_trust(relay, issuer, issued_for_peer_id, expires_at_sec);
if (error !== null) {
console.error("%s", error);
} else {
console.log("Trust issued for %s successfully added", issued_for_peer_id)
}
}
async function main() {
console.log("📘 Will connect to local nodes");
// key from local-network/builtins_secret_key.ed25519 to connect as builtins owner
let sk = bs58.decode("5FwE32bDcphFzuMca7Y2qW1gdR64fTBYoRNvD4MLE1hecDGhCMQGKn8aseMr5wRo4Xo2CRFdrEAawUNLYkgQD78K").slice(0, 32); // first 32 bytes - secret key, second - public key
async function revoke_checked(relay: string, revoked_by: string, revoked_peer_id: string) {
let error = await tg.revoke(relay, revoked_by, revoked_peer_id);
if (error !== null) {
console.log("%s", error);
} else {
console.log("Trust issued for %s revoked", revoked_peer_id)
}
}
async function exec_trusted_computation(node: string) {
let [result, error] = await trusted_computation(node)
if (result !== null) {
console.log("📗 Trusted computation on node %s successful, result is %s", node, result)
} else {
console.log("📕 Trusted computation on node %s failed, error:", node, error)
}
}
async function main(nodes: Node[]) {
// example_secret_key.ed25519
let sk = bs58.decode("E5ay3731i4HN8XjJozouV92RDMGAn3qSnb9dKSnujiWv");
let builtins_keypair = await KeyPair.fromEd25519SK(sk);
await Fluence.start({ connectTo: local[0], KeyPair: builtins_keypair});
let relay = nodes[0];
await Fluence.start({ connectTo: relay, KeyPair: builtins_keypair });
console.log(
"📗 created a fluence peer %s with relay %s",
Fluence.getStatus().peerId,
Fluence.getStatus().relayPeerId
);
const issued_timestamp_sec = await timestamp_sec(local[0].peerId);
const expires_at_sec = issued_timestamp_sec + 999999999;
const issuer_kp = await KeyPair.fromEd25519SK(bs58.decode("29Apzfedhw2Jxh94Jj4rNSmavQ1TkNe8ALYRA7bMegobwp423aLrURxLk32WtXgXHDqoSz7GAT9fQfoMhVd1e5Ww"));
let local_peer_id = Fluence.getStatus().peerId;
assert(local_peer_id !== null);
let add_root_result = await add_root(local[0].peerId, local[0].peerId, 2);
console.log("Add root weight result: %s", add_root_result);
let current_time = await tg.timestamp_sec();
let far_future = current_time + 9999999;
// add root trust
await add_trust_helper(local[0].peerId, issuer_kp, local[0].peerId, local[0].peerId, expires_at_sec, issued_timestamp_sec);
// clear all trusts from our peer id on relay
await revoke_all(relay.peerId, local_peer_id, nodes.slice(0, 3));
let root_weight_result = await get_weight(local[0].peerId, local[0].peerId);
console.log("Root weight: %s", root_weight_result);
// issue trust by local[0].peerId for local[1].peerId and add to tg
await add_trust_helper(local[0].peerId, issuer_kp, local[0].peerId, local[1].peerId, expires_at_sec, issued_timestamp_sec);
let weight_result = await get_weight(local[0].peerId, local[1].peerId);
console.log("Trust weight: %s", weight_result);
assert(root_weight_result.weight / 2 === weight_result.weight);
let certs = await get_all_certs(local[0].peerId, local[1].peerId);
console.log("Certs: %s", JSON.stringify(certs.certificates));
assert(certs.certificates.length === 1);
// wait to create revoke after trust (because timestamp in secs)
// wait to be sure that last revocation will be older than future trusts at least on 1 second (because timestamp in secs)
await new Promise(f => setTimeout(f, 1000));
// revoke local[1].peerId trust
await revoke_helper(local[0].peerId, issuer_kp, local[0].peerId, local[1].peerId, await timestamp_sec(local[0].peerId));
let nodeA = nodes[0].peerId
let nodeB = nodes[1].peerId
let nodeC = nodes[2].peerId
let empty_certs = await get_all_certs(local[0].peerId, local[1].peerId);
assert(empty_certs.certificates.length === 0);
console.log();
// try to exec computation on every node, will fail
await exec_trusted_computation(nodeA); // fail
await exec_trusted_computation(nodeB); // fail
await exec_trusted_computation(nodeC); // fail
console.log();
console.log("🌀 Issue trust to nodeB %s and nodeC: %s", nodeB, nodeC);
await add_new_trust_checked(relay.peerId, local_peer_id, nodeB, far_future);
await add_new_trust_checked(relay.peerId, local_peer_id, nodeC, far_future);
console.log();
await exec_trusted_computation(nodeA); // fail
await exec_trusted_computation(nodeB); // success
await exec_trusted_computation(nodeC); // success
console.log();
await new Promise(f => setTimeout(f, 1000));
console.log("🚫 Revoke trust to nodeB");
await revoke_checked(relay.peerId, local_peer_id, nodeB);
console.log();
await exec_trusted_computation(nodeA); // fail
await exec_trusted_computation(nodeB); // fail
await exec_trusted_computation(nodeC); // success
return;
}
console.log("In this example we try to execute some trusted computations based on trusts");
let args = process.argv.slice(2);
var environment: Node[];
if (args.length >= 1 && args[0] == "local") {
environment = local;
console.log("📘 Will connect to local nodes");
} else {
environment = testNet;
console.log("📘 Will connect to testNet");
}
main()
main(environment)
.then(() => process.exit(0))
.catch((error) => {
console.error(error);

14215
example/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,14 +13,14 @@
"author": "Fluence Labs",
"license": "MIT",
"dependencies": {
"@fluencelabs/aqua": "0.4.1-240",
"@fluencelabs/aqua-lib": "0.2.0",
"@fluencelabs/fluence": "0.14.3",
"@fluencelabs/aqua-lib": "^0.6.0",
"@fluencelabs/fluence": "^0.23.0",
"@fluencelabs/fluence-network-environment": "^1.0.10",
"@fluencelabs/trust-graph": "file:../aqua",
"@fluencelabs/trust-graph": "3.1.2",
"bs58": "^4.0.1"
},
"devDependencies": {
"typescript": "^4.4.3"
"typescript": "^4.5.2",
"@fluencelabs/aqua": "^0.10.0"
}
}

View File

@ -0,0 +1 @@
E5ay3731i4HN8XjJozouV92RDMGAn3qSnb9dKSnujiWv

BIN
images/diagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

BIN
images/revocation.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
images/weights.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

67
keypair/CHANGELOG.md Normal file
View File

@ -0,0 +1,67 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.10.3](https://github.com/fluencelabs/trust-graph/compare/keypair-v0.10.2...keypair-v0.10.3) (2023-07-04)
### Bug Fixes
* **keypair:** deserialize libp2p keypair from secret key ([#116](https://github.com/fluencelabs/trust-graph/issues/116)) ([ee330a7](https://github.com/fluencelabs/trust-graph/commit/ee330a715a902e48fc9b61d662ffcd950a26379c))
## [0.10.2](https://github.com/fluencelabs/trust-graph/compare/keypair-v0.10.1...keypair-v0.10.2) (2023-06-30)
### Features
* update libp2p identity ([#109](https://github.com/fluencelabs/trust-graph/issues/109)) ([d04120b](https://github.com/fluencelabs/trust-graph/commit/d04120bacf802a7e1127f4955b7391b0a4353128))
### Bug Fixes
* **keypair:** update description ([#105](https://github.com/fluencelabs/trust-graph/issues/105)) ([f7ef0f8](https://github.com/fluencelabs/trust-graph/commit/f7ef0f8da095fe1fef80faaa0b0c2d5ef854bd16))
### Reverts
* release master ([#110](https://github.com/fluencelabs/trust-graph/issues/110)) ([d80a43b](https://github.com/fluencelabs/trust-graph/commit/d80a43bcff721aff8fadf3d2d5c252804ce27a6c))
## [0.10.1](https://github.com/fluencelabs/trust-graph/compare/keypair-v0.10.0...keypair-v0.10.1) (2023-05-02)
### Features
* **keypair:** Make `KeyFormat` more convenient ([#91](https://github.com/fluencelabs/trust-graph/issues/91)) ([9b942ea](https://github.com/fluencelabs/trust-graph/commit/9b942eacca49d0468b4d7512667102363a6c9aa3))
## [0.10.0](https://github.com/fluencelabs/trust-graph/compare/keypair-v0.9.0...keypair-v0.10.0) (2023-03-15)
### ⚠ BREAKING CHANGES
* **deps:** update libp2p to 0.39.1 and other deps ([#77](https://github.com/fluencelabs/trust-graph/issues/77))
### Features
* **deps:** update libp2p to 0.39.1 and other deps ([#77](https://github.com/fluencelabs/trust-graph/issues/77)) ([080503d](https://github.com/fluencelabs/trust-graph/commit/080503dcfa2ecf8d09167ff9fe7f750fadf49035))
* **keypair:** add KeyPair::from_secret_key ([#50](https://github.com/fluencelabs/trust-graph/issues/50)) ([a6ce8d9](https://github.com/fluencelabs/trust-graph/commit/a6ce8d9eee20e1ea24eb27c38ac6df6d878292ae))
## [Unreleased]
## [0.8.1] - 2022-10-06
### Added
- *(keypair)* add KeyPair::from_secret_key (#50)
### Other
- set version of fluence-keypair to 0.8.0
- fluence-keypair 0.8.0
- libp2p-core 0.33.0 (#49)
- remove circle, update gh, add lints; remove warnings (#43)
- fluence-keypair 0.6.0
- libp2p-core 0.31.0 (from crates.io) (#37)
- Remove serde version lock (#15)
- Fix revocations logic (#34)
- Trust Graph: implement WASM built-in (#18)
- Move fluence-identity to fluence-keypair (#17)

1497
keypair/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,33 +1,30 @@
[package]
name = "fluence-keypair"
version = "0.5.1"
version = "0.10.4"
authors = ["Fluence Labs"]
edition = "2018"
description = "identity"
edition = "2021"
description = "unified keypair API based on libp2p-identity"
license = "Apache-2.0"
repository = "https://github.com/fluencelabs/trust-graph"
[dependencies]
serde = { version = "=1.0.118", features = ["derive"] }
serde_json = "1.0.58"
bs58 = "0.3.1"
ed25519-dalek = { version = "1.0.1", features = ["serde", "std"] }
rand = "0.7.0"
signature = "1.3.0"
ed25519 = "1.0.3"
serde_with = "1.6.0"
serde = { version = "1.0.118", features = ["derive"] }
bs58 = "0.5.0"
ed25519-dalek = { version = "2.0.0", features = ["serde", "std", "rand_core"] }
rand = "0.8.5"
thiserror = "1.0.23"
lazy_static = "1.2"
libsecp256k1 = "0.3.1"
lazy_static = "1.4"
libsecp256k1 = "0.7.1"
asn1_der = "0.6.1"
sha2 = "0.9.1"
sha2 = "0.10.6"
zeroize = "1"
serde_bytes = "0.11"
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"]}
eyre = "0.6.5"
libp2p-identity = { workspace = true, default-features = false, features = ["peerid", "rsa", "ed25519", "secp256k1"] }
multihash = { version = "0.18.0", features = ["identity"] }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
ring = { version = "0.16.9", features = ["alloc", "std"], default-features = false }
[dev-dependencies]
quickcheck = "0.9.0"
quickcheck = "1.0.3"

View File

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly-2022-08-30"
targets = [ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ]

View File

@ -19,16 +19,16 @@
// DEALINGS IN THE SOFTWARE.
//! Ed25519 keys.
use crate::error::{DecodingError, SigningError, VerificationError};
use crate::error::{DecodingError, DecodingError::InvalidLength, SigningError, VerificationError};
use core::fmt;
use ed25519_dalek::{self as ed25519, Signer as _, Verifier as _};
use rand::RngCore;
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
use zeroize::Zeroize;
/// An Ed25519 keypair.
pub struct Keypair(ed25519::Keypair);
/// An Ed25519 keypair
#[derive(Clone)]
pub struct Keypair(ed25519::SigningKey);
impl Keypair {
/// Generate a new Ed25519 keypair.
@ -40,13 +40,15 @@ impl Keypair {
/// of the secret scalar and the compressed public point,
/// an informal standard for encoding Ed25519 keypairs.
pub fn encode(&self) -> [u8; 64] {
self.0.to_bytes()
self.0.to_keypair_bytes()
}
/// Decode a keypair from the format produced by `encode`,
/// zeroing the input on success.
pub fn decode(kp: &mut [u8]) -> Result<Self, DecodingError> {
ed25519::Keypair::from_bytes(kp)
let bytes = <[u8; 64]>::try_from(&*kp).map_err(InvalidLength)?;
ed25519::SigningKey::from_keypair_bytes(&bytes)
.map(|k| {
kp.zeroize();
Keypair(k)
@ -61,12 +63,12 @@ impl Keypair {
/// Get the public key of this keypair.
pub fn public(&self) -> PublicKey {
PublicKey(self.0.public)
PublicKey(self.0.verifying_key())
}
/// Get the secret key of this keypair.
pub fn secret(&self) -> SecretKey {
SecretKey::from_bytes(&mut self.0.secret.to_bytes())
SecretKey::from_bytes(&mut self.0.to_bytes())
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
}
}
@ -74,26 +76,14 @@ impl Keypair {
impl fmt::Debug for Keypair {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Keypair")
.field("public", &self.0.public)
.field("public", &self.0.verifying_key())
.finish()
}
}
impl Clone for Keypair {
fn clone(&self) -> Self {
let mut sk_bytes = self.0.secret.to_bytes();
let secret = SecretKey::from_bytes(&mut sk_bytes)
.expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
.0;
let public = ed25519::PublicKey::from_bytes(&self.0.public.to_bytes())
.expect("ed25519::PublicKey::from_bytes(to_bytes(k)) != k");
Keypair(ed25519::Keypair { secret, public })
}
}
/// Build keypair from existing ed25519 keypair
impl From<ed25519::Keypair> for Keypair {
fn from(kp: ed25519::Keypair) -> Self {
impl From<ed25519::SigningKey> for Keypair {
fn from(kp: ed25519::SigningKey) -> Self {
Keypair(kp)
}
}
@ -101,25 +91,21 @@ impl From<ed25519::Keypair> for Keypair {
/// Demote an Ed25519 keypair to a secret key.
impl From<Keypair> for SecretKey {
fn from(kp: Keypair) -> Self {
SecretKey(kp.0.secret)
SecretKey(kp.0.to_bytes())
}
}
/// Promote an Ed25519 secret key into a keypair.
impl From<SecretKey> for Keypair {
fn from(sk: SecretKey) -> Self {
let secret: ed25519::ExpandedSecretKey = (&sk.0).into();
let public = ed25519::PublicKey::from(&secret);
Keypair(ed25519::Keypair {
secret: sk.0,
public,
})
let signing = ed25519::SigningKey::from_bytes(&sk.0);
Keypair(signing)
}
}
/// An Ed25519 public key.
#[derive(PartialEq, Eq, Debug, Clone, Deserialize, Serialize)]
pub struct PublicKey(ed25519::PublicKey);
pub struct PublicKey(ed25519::VerifyingKey);
impl PublicKey {
/// Verify the Ed25519 signature on a message using the public key.
@ -143,26 +129,21 @@ impl PublicKey {
/// Decode a public key from a byte array as produced by `encode`.
pub fn decode(bytes: &[u8]) -> Result<Self, DecodingError> {
ed25519::PublicKey::from_bytes(bytes)
let bytes = <[u8; 32]>::try_from(bytes).map_err(InvalidLength)?;
ed25519::VerifyingKey::from_bytes(&bytes)
.map_err(DecodingError::Ed25519)
.map(PublicKey)
}
}
/// An Ed25519 secret key.
#[derive(Clone)]
pub struct SecretKey(pub ed25519::SecretKey);
/// View the bytes of the secret key.
impl AsRef<[u8]> for SecretKey {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}
impl Clone for SecretKey {
fn clone(&self) -> Self {
let mut sk_bytes = self.0.to_bytes();
Self::from_bytes(&mut sk_bytes).expect("ed25519::SecretKey::from_bytes(to_bytes(k)) != k")
&self.0[..]
}
}
@ -175,13 +156,8 @@ impl fmt::Debug for SecretKey {
impl SecretKey {
/// Generate a new Ed25519 secret key.
pub fn generate() -> Self {
let mut bytes = [0u8; 32];
rand::thread_rng().fill_bytes(&mut bytes);
SecretKey(
ed25519::SecretKey::from_bytes(&bytes).expect(
"this returns `Err` only if the length is wrong; the length is correct; qed",
),
)
let signing = ed25519::SigningKey::generate(&mut rand::rngs::OsRng);
SecretKey(signing.to_bytes())
}
/// Create an Ed25519 secret key from a byte slice, zeroing the input on success.
@ -189,7 +165,7 @@ impl SecretKey {
/// returned.
pub fn from_bytes(mut sk_bytes: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
let sk_bytes = sk_bytes.as_mut();
let secret = ed25519::SecretKey::from_bytes(&*sk_bytes).map_err(DecodingError::Ed25519)?;
let secret = <[u8; 32]>::try_from(&*sk_bytes).map_err(InvalidLength)?;
sk_bytes.zeroize();
Ok(SecretKey(secret))
}
@ -201,10 +177,11 @@ pub struct Signature(pub Vec<u8>);
#[cfg(test)]
mod tests {
use super::*;
use crate::KeyPair;
use quickcheck::*;
fn eq_keypairs(kp1: &Keypair, kp2: &Keypair) -> bool {
kp1.public() == kp2.public() && kp1.0.secret.as_bytes() == kp2.0.secret.as_bytes()
kp1.public() == kp2.public() && kp1.0.to_bytes() == kp2.0.to_bytes()
}
#[test]
@ -218,11 +195,22 @@ mod tests {
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
}
#[test]
fn ed25519_keypair_convert() {
fn prop() -> bool {
let kp1 = KeyPair::generate_ed25519();
let libp2p_kp: libp2p_identity::Keypair = kp1.clone().into();
let kp2: KeyPair = libp2p_kp.into();
kp1.public() == kp2.public() && kp1.secret().unwrap() == kp2.secret().unwrap()
}
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
}
#[test]
fn ed25519_keypair_from_secret() {
fn prop() -> bool {
let kp1 = Keypair::generate();
let mut sk = kp1.0.secret.to_bytes();
let mut sk = kp1.0.to_bytes();
let kp2 = Keypair::from(SecretKey::from_bytes(&mut sk).unwrap());
eq_keypairs(&kp1, &kp2) && sk == [0u8; 32]
}

View File

@ -31,6 +31,8 @@ pub enum Error {
/// An error during decoding of key material.
#[derive(ThisError, Debug)]
pub enum DecodingError {
#[error("Failed to decode, invalid length: {0}")]
InvalidLength(#[from] std::array::TryFromSliceError),
#[error("Failed to decode with ed25519: {0}")]
Ed25519(
#[from]
@ -68,7 +70,7 @@ pub enum SigningError {
Secp256k1(
#[from]
#[source]
secp256k1::Error,
libsecp256k1::Error,
),
}
@ -83,5 +85,5 @@ pub enum VerificationError {
Rsa(#[source] ring::error::Unspecified, String, String),
#[error("Failed to verify signature {1} with {2} secp256k1 public key: {0}")]
Secp256k1(#[source] secp256k1::Error, String, String),
Secp256k1(#[source] libsecp256k1::Error, String, String),
}

View File

@ -26,7 +26,7 @@ use crate::public_key::PublicKey;
use crate::rsa;
use crate::secp256k1;
use crate::signature::Signature;
use libp2p_core::PeerId;
use libp2p_identity::{KeyType, Keypair, PeerId};
use std::convert::TryFrom;
use std::str::FromStr;
@ -47,7 +47,7 @@ use std::str::FromStr;
/// let keypair = Keypair::rsa_from_pkcs8(&mut bytes);
/// ```
///
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum KeyFormat {
Ed25519,
#[cfg(not(target_arch = "wasm32"))]
@ -105,6 +105,7 @@ impl From<KeyFormat> for String {
}
}
}
#[derive(Clone)]
pub enum KeyPair {
/// An Ed25519 keypair.
@ -168,6 +169,18 @@ impl KeyPair {
}
}
/// Get the key format of this keypair.
pub fn key_format(&self) -> KeyFormat {
use KeyPair::*;
match self {
Ed25519(_) => KeyFormat::Ed25519,
#[cfg(not(target_arch = "wasm32"))]
Rsa(_) => KeyFormat::Rsa,
Secp256k1(_) => KeyFormat::Secp256k1,
}
}
/// Get the public key of this keypair.
pub fn public(&self) -> PublicKey {
use KeyPair::*;
@ -182,7 +195,7 @@ impl KeyPair {
pub fn secret(&self) -> eyre::Result<Vec<u8>> {
use KeyPair::*;
match self {
Ed25519(pair) => Ok(pair.secret().0.to_bytes().to_vec()),
Ed25519(pair) => Ok(pair.secret().0.to_vec()),
#[cfg(not(target_arch = "wasm32"))]
Rsa(_) => Err(eyre::eyre!("secret key is not available for RSA")),
Secp256k1(pair) => Ok(pair.secret().to_bytes().to_vec()),
@ -219,47 +232,81 @@ impl KeyPair {
}
}
pub fn from_secret_key(bytes: Vec<u8>, format: KeyFormat) -> Result<Self, DecodingError> {
use KeyPair::*;
match format {
KeyFormat::Ed25519 => Ok(Ed25519(ed25519::SecretKey::from_bytes(bytes)?.into())),
KeyFormat::Secp256k1 => Ok(Secp256k1(secp256k1::SecretKey::from_bytes(bytes)?.into())),
#[cfg(not(target_arch = "wasm32"))]
KeyFormat::Rsa => Err(DecodingError::KeypairDecodingIsNotSupported),
}
}
pub fn get_peer_id(&self) -> PeerId {
self.public().to_peer_id()
}
}
impl From<libp2p_core::identity::Keypair> for KeyPair {
fn from(key: libp2p_core::identity::Keypair) -> Self {
use libp2p_core::identity::Keypair::*;
match key {
Ed25519(kp) => KeyPair::Ed25519(ed25519::Keypair::decode(&mut kp.encode()).unwrap()),
#[cfg(not(target_arch = "wasm32"))]
// safety: these Keypair structures are identical
Rsa(kp) => KeyPair::Rsa(unsafe {
std::mem::transmute::<libp2p_core::identity::rsa::Keypair, rsa::Keypair>(kp)
}),
Secp256k1(kp) => KeyPair::Secp256k1(secp256k1::Keypair::from(
secp256k1::SecretKey::from_bytes(kp.secret().to_bytes()).unwrap(),
)),
impl From<libp2p_identity::Keypair> for KeyPair {
fn from(key: libp2p_identity::Keypair) -> Self {
fn convert_keypair(key: Keypair) -> eyre::Result<KeyPair> {
match key.key_type() {
KeyType::Ed25519 => {
let kp = key.try_into_ed25519()?;
let raw_kp = ed25519::Keypair::decode(&mut kp.to_bytes())?;
Ok(KeyPair::Ed25519(raw_kp))
}
#[cfg(not(target_arch = "wasm32"))]
KeyType::RSA => {
let kp = key.try_into_rsa()?;
let raw_kp = unsafe {
std::mem::transmute::<libp2p_identity::rsa::Keypair, rsa::Keypair>(kp)
};
Ok(KeyPair::Rsa(raw_kp))
}
KeyType::Secp256k1 => {
let kp = key.try_into_secp256k1()?;
let raw_kp = secp256k1::SecretKey::from_bytes(kp.secret().to_bytes())?;
Ok(KeyPair::Secp256k1(secp256k1::Keypair::from(raw_kp)))
}
_ => unreachable!(),
}
}
convert_keypair(key).expect("Could not convert keypair")
}
}
impl From<KeyPair> for libp2p_core::identity::Keypair {
impl From<KeyPair> for libp2p_identity::Keypair {
fn from(key: KeyPair) -> Self {
use libp2p_core::identity;
use libp2p_core::identity::Keypair;
use KeyPair::*;
match key {
Ed25519(kp) => Keypair::Ed25519(
identity::ed25519::Keypair::decode(kp.encode().to_vec().as_mut_slice()).unwrap(),
),
#[cfg(not(target_arch = "wasm32"))]
// safety: these Keypair structures are identical
Rsa(kp) => Keypair::Rsa(unsafe {
std::mem::transmute::<rsa::Keypair, libp2p_core::identity::rsa::Keypair>(kp)
}),
Secp256k1(kp) => Keypair::Secp256k1(identity::secp256k1::Keypair::from(
identity::secp256k1::SecretKey::from_bytes(kp.secret().to_bytes()).unwrap(),
)),
fn convert_keypair(key: KeyPair) -> eyre::Result<libp2p_identity::Keypair> {
match key {
KeyPair::Ed25519(kp) => {
// for some reason, libp2p takes SecretKey's 32 bytes here instead of Keypair's 64 bytes
let secret_bytes = kp.secret().0;
let kp = libp2p_identity::Keypair::ed25519_from_bytes(secret_bytes)?;
Ok(kp)
}
#[cfg(not(target_arch = "wasm32"))]
// safety: these Keypair structures are identical
KeyPair::Rsa(kp) => {
let kp = unsafe {
std::mem::transmute::<rsa::Keypair, libp2p_identity::rsa::Keypair>(kp)
};
let kp = Keypair::from(kp);
Ok(kp)
}
KeyPair::Secp256k1(kp) => {
let sk = libp2p_identity::secp256k1::SecretKey::try_from_bytes(
kp.secret().to_bytes(),
)?;
let kp = libp2p_identity::secp256k1::Keypair::from(sk);
let kp = Keypair::from(kp);
Ok(kp)
}
}
}
convert_keypair(key).expect("Could not convert key pair")
}
}

View File

@ -41,7 +41,7 @@ pub use key_pair::KeyFormat;
pub use key_pair::KeyPair;
pub mod peerid_serializer {
use libp2p_core::PeerId;
use libp2p_identity::PeerId;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::str::FromStr;
@ -58,7 +58,7 @@ pub mod peerid_serializer {
{
let str = String::deserialize(deserializer)?;
PeerId::from_str(&str).map_err(|e| {
serde::de::Error::custom(format!("peer id deserialization failed for {:?}", e))
serde::de::Error::custom(format!("peer id deserialization failed for {e:?}"))
})
}
}

View File

@ -21,7 +21,7 @@ use crate::secp256k1;
use crate::signature::Signature;
use crate::key_pair::KeyFormat;
use libp2p_core::PeerId;
use libp2p_identity::{KeyType, PeerId};
use serde::{Deserialize, Serialize};
use std::convert::TryFrom;
@ -109,7 +109,7 @@ impl PublicKey {
}
pub fn to_peer_id(&self) -> PeerId {
PeerId::from_public_key(self.clone().into())
PeerId::from_public_key(&self.clone().into())
}
pub fn get_key_format(&self) -> KeyFormat {
@ -124,53 +124,85 @@ impl PublicKey {
}
}
impl From<libp2p_core::identity::PublicKey> for PublicKey {
fn from(key: libp2p_core::identity::PublicKey) -> Self {
use libp2p_core::identity::PublicKey::*;
match key {
Ed25519(key) => {
PublicKey::Ed25519(ed25519::PublicKey::decode(&key.encode()[..]).unwrap())
}
#[cfg(not(target_arch = "wasm32"))]
Rsa(key) => PublicKey::Rsa(rsa::PublicKey::from_pkcs1(key.encode_pkcs1()).unwrap()),
Secp256k1(key) => {
PublicKey::Secp256k1(secp256k1::PublicKey::decode(&key.encode()[..]).unwrap())
impl From<libp2p_identity::PublicKey> for PublicKey {
fn from(key: libp2p_identity::PublicKey) -> Self {
fn convert_key(key: libp2p_identity::PublicKey) -> eyre::Result<PublicKey> {
match key.key_type() {
KeyType::Ed25519 => {
let pk = key.try_into_ed25519()?;
let raw_pk = ed25519::PublicKey::decode(&pk.to_bytes())?;
Ok(PublicKey::Ed25519(raw_pk))
}
#[cfg(not(target_arch = "wasm32"))]
KeyType::RSA => {
let pk = key.try_into_rsa()?;
let raw_pk = rsa::PublicKey::from_pkcs1(pk.encode_pkcs1())?;
Ok(PublicKey::Rsa(raw_pk))
}
KeyType::Secp256k1 => {
let pk = key.try_into_secp256k1()?;
let raw_pk = secp256k1::PublicKey::decode(&pk.to_bytes())?;
Ok(PublicKey::Secp256k1(raw_pk))
}
_ => unreachable!(),
}
}
convert_key(key).expect("Could not convert public key")
}
}
impl From<PublicKey> for libp2p_core::identity::PublicKey {
impl From<PublicKey> for libp2p_identity::PublicKey {
fn from(key: PublicKey) -> Self {
use libp2p_core::identity as libp2p_identity;
match key {
PublicKey::Ed25519(key) => libp2p_identity::PublicKey::Ed25519(
libp2p_identity::ed25519::PublicKey::decode(&key.encode()[..]).unwrap(),
),
#[cfg(not(target_arch = "wasm32"))]
PublicKey::Rsa(key) => libp2p_identity::PublicKey::Rsa(
libp2p_identity::rsa::PublicKey::decode_x509(&key.encode_x509()).unwrap(),
),
PublicKey::Secp256k1(key) => libp2p_identity::PublicKey::Secp256k1(
libp2p_identity::secp256k1::PublicKey::decode(&key.encode()[..]).unwrap(),
),
fn convert_key(key: PublicKey) -> eyre::Result<libp2p_identity::PublicKey> {
match key {
PublicKey::Ed25519(key) => {
let raw_pk =
libp2p_identity::ed25519::PublicKey::try_from_bytes(&key.encode())?;
let pk = libp2p_identity::PublicKey::from(raw_pk);
Ok(pk)
}
#[cfg(not(target_arch = "wasm32"))]
PublicKey::Rsa(key) => {
let raw_pk =
libp2p_identity::rsa::PublicKey::try_decode_x509(&key.encode_x509())?;
let pk = libp2p_identity::PublicKey::from(raw_pk);
Ok(pk)
}
PublicKey::Secp256k1(key) => {
let raw_pk =
libp2p_identity::secp256k1::PublicKey::try_from_bytes(&key.encode())?;
let pk = libp2p_identity::PublicKey::from(raw_pk);
Ok(pk)
}
}
}
convert_key(key).expect("Could not convert key")
}
}
impl TryFrom<libp2p_core::PeerId> for PublicKey {
impl TryFrom<PeerId> for PublicKey {
type Error = DecodingError;
fn try_from(peer_id: libp2p_core::PeerId) -> Result<Self, Self::Error> {
Ok(peer_id
.as_public_key()
.ok_or(DecodingError::PublicKeyNotInlined(peer_id.to_base58()))?
fn try_from(peer_id: PeerId) -> Result<Self, Self::Error> {
Ok(as_public_key(&peer_id)
.ok_or_else(|| DecodingError::PublicKeyNotInlined(peer_id.to_base58()))?
.into())
}
}
/// Convert PeerId to libp2p's PublicKey
fn as_public_key(peer_id: &PeerId) -> Option<libp2p_identity::PublicKey> {
let mhash = peer_id.as_ref();
match multihash::Code::try_from(mhash.code()) {
Ok(multihash::Code::Identity) => {
libp2p_identity::PublicKey::try_decode_protobuf(mhash.digest()).ok()
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -196,8 +228,8 @@ mod tests {
fn public_key_peer_id_conversions() {
let kp = KeyPair::generate_secp256k1();
let fluence_pk = kp.public();
let libp2p_pk: libp2p_core::PublicKey = fluence_pk.clone().into();
let peer_id = PeerId::from_public_key(libp2p_pk);
let libp2p_pk: libp2p_identity::PublicKey = fluence_pk.clone().into();
let peer_id = PeerId::from_public_key(&libp2p_pk);
let fluence_pk_converted = PublicKey::try_from(peer_id).unwrap();
assert_eq!(fluence_pk, fluence_pk_converted);

View File

@ -43,7 +43,7 @@ impl Keypair {
///
/// [RFC5208]: https://tools.ietf.org/html/rfc5208#section-5
pub fn from_pkcs8(der: &mut [u8]) -> Result<Self, DecodingError> {
let kp = RsaKeyPair::from_pkcs8(&der).map_err(|_| DecodingError::Rsa)?;
let kp = RsaKeyPair::from_pkcs8(der).map_err(|_| DecodingError::Rsa)?;
der.zeroize();
Ok(Keypair(Arc::new(kp)))
}
@ -57,7 +57,7 @@ impl Keypair {
pub fn sign(&self, data: &[u8]) -> Result<Vec<u8>, SigningError> {
let mut signature = vec![0; self.0.public_modulus_len()];
let rng = SystemRandom::new();
match self.0.sign(&RSA_PKCS1_SHA256, &rng, &data, &mut signature) {
match self.0.sign(&RSA_PKCS1_SHA256, &rng, data, &mut signature) {
Ok(()) => Ok(signature),
Err(_) => Err(SigningError::Rsa),
}
@ -127,7 +127,7 @@ impl fmt::Debug for PublicKey {
let mut hex = String::with_capacity(bytes.len() * 2);
for byte in bytes {
write!(hex, "{:02x}", byte).expect("Can't fail on writing to string");
write!(hex, "{byte:02x}").expect("Can't fail on writing to string");
}
f.debug_struct("PublicKey").field("pkcs1", &hex).finish()
@ -234,7 +234,6 @@ pub struct Signature(pub Vec<u8>);
mod tests {
use super::*;
use quickcheck::*;
use rand::seq::SliceRandom;
use std::fmt;
const KEY1: &'static [u8] = include_bytes!("test/rsa-2048.pk8");
@ -251,8 +250,8 @@ mod tests {
}
impl Arbitrary for SomeKeypair {
fn arbitrary<G: Gen>(g: &mut G) -> SomeKeypair {
let mut key = [KEY1, KEY2, KEY3].choose(g).unwrap().to_vec();
fn arbitrary(g: &mut Gen) -> SomeKeypair {
let mut key = g.choose(&[KEY1, KEY2, KEY3]).unwrap().to_vec();
SomeKeypair(Keypair::from_pkcs8(&mut key).unwrap())
}
}

View File

@ -23,8 +23,8 @@ use crate::error::{DecodingError, SigningError, VerificationError};
use asn1_der::{DerObject, FromDerObject};
use core::fmt;
use libsecp256k1::Message;
use rand::RngCore;
use secp256k1::Message;
use serde::de::Error as SerdeError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_bytes::{ByteBuf as SerdeByteBuf, Bytes as SerdeBytes};
@ -66,7 +66,7 @@ impl fmt::Debug for Keypair {
/// Promote a Secp256k1 secret key into a keypair.
impl From<SecretKey> for Keypair {
fn from(secret: SecretKey) -> Self {
let public = PublicKey(secp256k1::PublicKey::from_secret_key(&secret.0));
let public = PublicKey(libsecp256k1::PublicKey::from_secret_key(&secret.0));
Keypair { secret, public }
}
}
@ -80,7 +80,7 @@ impl From<Keypair> for SecretKey {
/// A Secp256k1 secret key.
#[derive(Clone)]
pub struct SecretKey(secp256k1::SecretKey);
pub struct SecretKey(libsecp256k1::SecretKey);
impl fmt::Debug for SecretKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -92,12 +92,12 @@ impl SecretKey {
/// Generate a new Secp256k1 secret key.
pub fn generate() -> Self {
let mut r = rand::thread_rng();
let mut b = [0; secp256k1::util::SECRET_KEY_SIZE];
let mut b = [0; libsecp256k1::util::SECRET_KEY_SIZE];
// This is how it is done in `secp256k1::SecretKey::random` which
// we do not use here because it uses `rand::Rng` from rand-0.4.
loop {
r.fill_bytes(&mut b);
if let Ok(k) = secp256k1::SecretKey::parse(&b) {
if let Ok(k) = libsecp256k1::SecretKey::parse(&b) {
return SecretKey(k);
}
}
@ -108,8 +108,8 @@ impl SecretKey {
/// error is returned.
pub fn from_bytes(mut sk: impl AsMut<[u8]>) -> Result<Self, DecodingError> {
let sk_bytes = sk.as_mut();
let secret =
secp256k1::SecretKey::parse_slice(&*sk_bytes).map_err(|_| DecodingError::Secp256k1)?;
let secret = libsecp256k1::SecretKey::parse_slice(&*sk_bytes)
.map_err(|_| DecodingError::Secp256k1)?;
sk_bytes.zeroize();
Ok(SecretKey(secret))
}
@ -122,7 +122,7 @@ impl SecretKey {
// TODO: Stricter parsing.
let der_obj = der.as_mut();
let obj: Vec<DerObject> =
FromDerObject::deserialize((&*der_obj).iter()).map_err(|_| DecodingError::Secp256k1)?;
FromDerObject::deserialize((*der_obj).iter()).map_err(|_| DecodingError::Secp256k1)?;
der_obj.zeroize();
let sk_obj = obj.into_iter().nth(1).ok_or(DecodingError::Secp256k1)?;
let mut sk_bytes: Vec<u8> =
@ -149,7 +149,7 @@ impl SecretKey {
/// ECDSA signature.
pub fn sign_hashed(&self, msg: &[u8]) -> Result<Vec<u8>, SigningError> {
let m = Message::parse_slice(msg).map_err(SigningError::Secp256k1)?;
Ok(secp256k1::sign(&m, &self.0)
Ok(libsecp256k1::sign(&m, &self.0)
.0
.serialize_der()
.as_ref()
@ -159,7 +159,7 @@ impl SecretKey {
/// A Secp256k1 public key.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct PublicKey(secp256k1::PublicKey);
pub struct PublicKey(libsecp256k1::PublicKey);
impl PublicKey {
/// Verify the Secp256k1 signature on a message using the public key.
@ -171,7 +171,8 @@ impl PublicKey {
pub fn verify_hashed(&self, msg: &[u8], sig: &[u8]) -> Result<(), VerificationError> {
Message::parse_slice(msg)
.and_then(|m| {
secp256k1::Signature::parse_der(sig).map(|s| secp256k1::verify(&m, &s, &self.0))
libsecp256k1::Signature::parse_der(sig)
.map(|s| libsecp256k1::verify(&m, &s, &self.0))
})
.map_err(|e| {
VerificationError::Secp256k1(
@ -197,7 +198,7 @@ impl PublicKey {
/// Decode a public key from a byte slice in the the format produced
/// by `encode`.
pub fn decode(bytes: &[u8]) -> Result<Self, DecodingError> {
secp256k1::PublicKey::parse_slice(bytes, Some(secp256k1::PublicKeyFormat::Compressed))
libsecp256k1::PublicKey::parse_slice(bytes, Some(libsecp256k1::PublicKeyFormat::Compressed))
.map_err(|_| DecodingError::Secp256k1)
.map(PublicKey)
}
@ -228,6 +229,12 @@ pub struct Signature(pub Vec<u8>);
#[cfg(test)]
mod tests {
use super::*;
use crate::{key_pair, KeyFormat};
use quickcheck::QuickCheck;
fn eq_keypairs(kp1: key_pair::KeyPair, kp2: key_pair::KeyPair) -> bool {
kp1.public() == kp2.public() && kp1.secret().unwrap() == kp2.secret().unwrap()
}
#[test]
fn secp256k1_secret_from_bytes() {
@ -238,4 +245,15 @@ mod tests {
assert_eq!(sk1.0.serialize(), sk2.0.serialize());
assert_eq!(sk_bytes, [0; 32]);
}
#[test]
fn secp256k1_keypair_encode_decode() {
fn prop() -> bool {
let kp1 = key_pair::KeyPair::generate(KeyFormat::Secp256k1);
let kp1_enc = libp2p_identity::Keypair::from(kp1.clone());
let kp2 = key_pair::KeyPair::from(kp1_enc);
eq_keypairs(kp1, kp2)
}
QuickCheck::new().tests(10).quickcheck(prop as fn() -> _);
}
}

View File

@ -98,7 +98,7 @@ impl Signature {
pub fn get_raw_signature(&self) -> RawSignature {
RawSignature {
bytes: self.to_vec().clone().to_vec(),
bytes: self.to_vec().to_vec(),
sig_type: self.get_signature_type(),
}
}

View File

@ -1,7 +1,7 @@
# management secret key is NAB5rGwT4qOEB+6nLQawkTfCOV2eiFSjgQK8bfEdZXY=
services:
fluence-0: # /ip4/127.0.0.1/tcp/9990/ws/p2p/12D3KooWHBG9oaVx4i3vi6c1rSBUm7MLBmyGmmbHoZ23pmjDCnvK
command: -f ed25519 -k 29Apzfedhw2Jxh94Jj4rNSmavQ1TkNe8ALYRA7bMegobwp423aLrURxLk32WtXgXHDqoSz7GAT9fQfoMhVd1e5Ww -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7770 -w 9990 # --bootstraps /dns4/fluence-1/tcp/7771 /dns4/fluence-2/tcp/7772
command: -f ed25519 -k 29Apzfedhw2Jxh94Jj4rNSmavQ1TkNe8ALYRA7bMegobwp423aLrURxLk32WtXgXHDqoSz7GAT9fQfoMhVd1e5Ww -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7770 -w 9990 --bootstraps /dns4/fluence-1/tcp/7771 /dns4/fluence-2/tcp/7772
container_name: fluence-0
environment:
RUST_BACKTRACE: full
@ -11,7 +11,7 @@ services:
ports:
- 7770:7770 # tcp
- 9990:9990 # ws
- 5000:5001 # ipfs rpc
- 5003:5001 # ipfs rpc
- 4000:4001 # ipfs swarm
- 18080:18080 # /metrics
restart: always
@ -22,56 +22,58 @@ services:
networks:
- fluence
# fluence-1: # /ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWRABanQHUn28dxavN9ZS1zZghqoZVAYtFpoN7FdtoGTFv
# command: -f ed25519 -k 5fNENMwkUT4dW3hPs9ZwqV4qA5pdTtUChTazAx9Awe2Vpz1yaJu3VCmcEZow6YgdFBGoZoFAZUZBbF3c2Ebd2iL -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7771 -w 9991 --bootstraps /dns4/fluence-0/tcp/7770 /dns4/fluence-2/tcp/7772 #/dns4/kras-00.fluence.dev/tcp/7770
# container_name: fluence-1
# environment:
# RUST_BACKTRACE: full
# RUST_LOG: info,network=trace,aquamarine=info,aquamarine::actor=info,tokio_threadpool=info,tokio_reactor=info,mio=info,tokio_io=info,soketto=info,yamux=info,multistream_select=info,libp2p_secio=info,libp2p_websocket::framed=info,libp2p_ping=info,libp2p_core::upgrade::apply=info,libp2p_kad::kbucket=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,particle_server::behaviour::identify=info,libp2p_mplex=info,libp2p_identify=info,walrus=info,particle_protocol::libp2p_protocol::upgrade=info,kademlia::behaviour=info
# WASM_LOG: info
# image: fluencelabs/node:latest
# ports:
# - 7771:7771 # tcp
# - 9991:9991 # ws
# - 5001:5001 # ipfs rpc
# - 4001:4001 # ipfs swarm
# - 18081:18080 # /metrics
# restart: always
# volumes:
# - fluence-1:/.fluence
# - data-1:/config
# networks:
# - fluence
#
# fluence-2: # /ip4/127.0.0.1/tcp/9992/ws/p2p/12D3KooWFpQ7LHxcC9FEBUh3k4nSCC12jBhijJv3gJbi7wsNYzJ5
# command: -f ed25519 -k 5DTs9LQS8Ay2dM8xBcikDRwYLMcanhsC6tynSSgpLyBZEv5Ey34LVw1fYcCuUj9A9EfvQJB2bsaGhSRoHQ7D6UE5 -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7772 -w 9992 --bootstraps /dns4/fluence-0/tcp/7770 /dns4/fluence-1/tcp/7771 #/dns4/kras-00.fluence.dev/tcp/7770
# container_name: fluence-2
# environment:
# RUST_BACKTRACE: full
# RUST_LOG: info,network=trace,aquamarine=info,aquamarine::actor=info,tokio_threadpool=info,tokio_reactor=info,mio=info,tokio_io=info,soketto=info,yamux=info,multistream_select=info,libp2p_secio=info,libp2p_websocket::framed=info,libp2p_ping=info,libp2p_core::upgrade::apply=info,libp2p_kad::kbucket=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,particle_server::behaviour::identify=info,libp2p_mplex=info,libp2p_identify=info,walrus=info,particle_protocol::libp2p_protocol::upgrade=info,kademlia::behaviour=info
# WASM_LOG: info
# image: fluencelabs/node:latest
# ports:
# - 7772:7772 # tcp
# - 9992:9992 # ws
# - 5002:5001 # ipfs rpc
# - 4002:4001 # ipfs swarm
# - 18082:18080 # /metrics
# restart: always
# volumes:
# - fluence-2:/.fluence
# - data-2:/config
# networks:
# - fluence
fluence-1: # /ip4/127.0.0.1/tcp/9991/ws/p2p/12D3KooWRABanQHUn28dxavN9ZS1zZghqoZVAYtFpoN7FdtoGTFv
command: -f ed25519 -k 5fNENMwkUT4dW3hPs9ZwqV4qA5pdTtUChTazAx9Awe2Vpz1yaJu3VCmcEZow6YgdFBGoZoFAZUZBbF3c2Ebd2iL -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7771 -w 9991 --bootstraps /dns4/fluence-0/tcp/7770 /dns4/fluence-2/tcp/7772 #/dns4/kras-00.fluence.dev/tcp/7770
container_name: fluence-1
environment:
RUST_BACKTRACE: full
RUST_LOG: info,network=trace,aquamarine=info,aquamarine::actor=info,tokio_threadpool=info,tokio_reactor=info,mio=info,tokio_io=info,soketto=info,yamux=info,multistream_select=info,libp2p_secio=info,libp2p_websocket::framed=info,libp2p_ping=info,libp2p_core::upgrade::apply=info,libp2p_kad::kbucket=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,particle_server::behaviour::identify=info,libp2p_mplex=info,libp2p_identify=info,walrus=info,particle_protocol::libp2p_protocol::upgrade=info,kademlia::behaviour=info
WASM_LOG: info
image: fluencelabs/node:latest
ports:
- 7771:7771 # tcp
- 9991:9991 # ws
- 5001:5001 # ipfs rpc
- 4001:4001 # ipfs swarm
- 18081:18080 # /metrics
restart: always
volumes:
- fluence-1:/.fluence
- data-1:/config
- ./builtins_secret_key.ed25519:/.fluence/v1/builtins_secret_key.ed25519
networks:
- fluence
fluence-2: # /ip4/127.0.0.1/tcp/9992/ws/p2p/12D3KooWFpQ7LHxcC9FEBUh3k4nSCC12jBhijJv3gJbi7wsNYzJ5
command: -f ed25519 -k 5DTs9LQS8Ay2dM8xBcikDRwYLMcanhsC6tynSSgpLyBZEv5Ey34LVw1fYcCuUj9A9EfvQJB2bsaGhSRoHQ7D6UE5 -m 12D3KooWFRgVmb1uWcmCbmJqLr8tBQghL6ysSpK2VyE2VZbaQ6wy -t 7772 -w 9992 --bootstraps /dns4/fluence-0/tcp/7770 /dns4/fluence-1/tcp/7771 #/dns4/kras-00.fluence.dev/tcp/7770
container_name: fluence-2
environment:
RUST_BACKTRACE: full
RUST_LOG: info,network=trace,aquamarine=info,aquamarine::actor=info,tokio_threadpool=info,tokio_reactor=info,mio=info,tokio_io=info,soketto=info,yamux=info,multistream_select=info,libp2p_secio=info,libp2p_websocket::framed=info,libp2p_ping=info,libp2p_core::upgrade::apply=info,libp2p_kad::kbucket=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,cranelift_codegen=info,wasmer_wasi=info,async_io=info,polling=info,wasmer_interface_types_fl=info,particle_server::behaviour::identify=info,libp2p_mplex=info,libp2p_identify=info,walrus=info,particle_protocol::libp2p_protocol::upgrade=info,kademlia::behaviour=info
WASM_LOG: info
image: fluencelabs/node:latest
ports:
- 7772:7772 # tcp
- 9992:9992 # ws
- 5002:5001 # ipfs rpc
- 4002:4001 # ipfs swarm
- 18082:18080 # /metrics
restart: always
volumes:
- fluence-2:/.fluence
- data-2:/config
- ./builtins_secret_key.ed25519:/.fluence/v1/builtins_secret_key.ed25519
networks:
- fluence
version: "3.5"
volumes:
fluence-0:
# fluence-1:
# fluence-2:
fluence-1:
fluence-2:
data-0:
# data-1:
# data-2:
data-1:
data-2:
networks:
fluence:

View File

@ -1,3 +1,4 @@
[toolchain]
channel = "nightly-2021-09-01"
targets = [ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ]
channel = "nightly-2022-12-06"
targets = [ "x86_64-apple-darwin", "wasm32-wasi", "wasm32-unknown-unknown", "x86_64-unknown-linux-gnu" ]
components = [ "rustfmt", "clippy" ]

141
service/CHANGELOG.md Normal file
View File

@ -0,0 +1,141 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
* The following workspace dependencies were updated
* dependencies
* trust-graph bumped from 0.4.1 to 0.4.2
* fluence-keypair bumped from 0.10.0 to 0.10.1
## [0.4.7](https://github.com/fluencelabs/trust-graph/compare/trust-graph-wasm-v0.4.6...trust-graph-wasm-v0.4.7) (2023-07-04)
### Bug Fixes
* **deps:** update rust crate marine-rs-sdk-test to 0.10.0 ([#106](https://github.com/fluencelabs/trust-graph/issues/106)) ([725d3f8](https://github.com/fluencelabs/trust-graph/commit/725d3f8f48b3bf1ed8605e9ba2da5c966a145f0d))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* trust-graph bumped from 0.4.6 to 0.4.7
* fluence-keypair bumped from 0.10.2 to 0.10.3
## [0.4.6](https://github.com/fluencelabs/trust-graph/compare/trust-graph-wasm-v0.4.5...trust-graph-wasm-v0.4.6) (2023-06-30)
### Reverts
* release master ([#110](https://github.com/fluencelabs/trust-graph/issues/110)) ([d80a43b](https://github.com/fluencelabs/trust-graph/commit/d80a43bcff721aff8fadf3d2d5c252804ce27a6c))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* trust-graph bumped from 0.4.5 to 0.4.6
* fluence-keypair bumped from 0.10.1 to 0.10.2
## [0.4.5](https://github.com/fluencelabs/trust-graph/compare/trust-graph-wasm-v0.4.4...trust-graph-wasm-v0.4.5) (2023-05-09)
### Miscellaneous Chores
* **trust-graph-wasm:** Synchronize trust-graph, wasm and api versions
### Dependencies
* The following workspace dependencies were updated
* dependencies
* trust-graph bumped from 0.4.4 to 0.4.5
## [0.4.4](https://github.com/fluencelabs/trust-graph/compare/trust-graph-wasm-v0.4.3...trust-graph-wasm-v0.4.4) (2023-05-09)
### Miscellaneous Chores
* **trust-graph-wasm:** Synchronize trust-graph, wasm and api versions
### Dependencies
* The following workspace dependencies were updated
* dependencies
* trust-graph bumped from 0.4.3 to 0.4.4
## [0.4.3](https://github.com/fluencelabs/trust-graph/compare/trust-graph-wasm-v0.4.2...trust-graph-wasm-v0.4.3) (2023-05-08)
### Miscellaneous Chores
* **trust-graph-wasm:** Synchronize trust-graph, wasm and api versions
### Dependencies
* The following workspace dependencies were updated
* dependencies
* trust-graph bumped from 0.4.2 to 0.4.3
## [0.4.1](https://github.com/fluencelabs/trust-graph/compare/trust-graph-wasm-v0.4.0...trust-graph-wasm-v0.4.1) (2023-04-13)
### Bug Fixes
* **deps:** Add trust-graph to workspace and bump sqlite-wasm version ([#87](https://github.com/fluencelabs/trust-graph/issues/87)) ([da38a41](https://github.com/fluencelabs/trust-graph/commit/da38a41ba727a14774a71bba6612b1bf1f498db9))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* trust-graph bumped from 0.4.0 to 0.4.1
## [0.4.0](https://github.com/fluencelabs/trust-graph/compare/trust-graph-wasm-v0.3.2...trust-graph-wasm-v0.4.0) (2023-03-15)
### ⚠ BREAKING CHANGES
* **deps:** update libp2p to 0.39.1 and other deps ([#77](https://github.com/fluencelabs/trust-graph/issues/77))
### Features
* **deps:** update libp2p to 0.39.1 and other deps ([#77](https://github.com/fluencelabs/trust-graph/issues/77)) ([080503d](https://github.com/fluencelabs/trust-graph/commit/080503dcfa2ecf8d09167ff9fe7f750fadf49035))
* **keypair:** add KeyPair::from_secret_key ([#50](https://github.com/fluencelabs/trust-graph/issues/50)) ([a6ce8d9](https://github.com/fluencelabs/trust-graph/commit/a6ce8d9eee20e1ea24eb27c38ac6df6d878292ae))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* trust-graph bumped from 0.3.0 to 0.4.0
* fluence-keypair bumped from 0.9.0 to 0.10.0
## [Unreleased]
## [0.3.1] - 2022-10-06
### Added
- *(keypair)* add KeyPair::from_secret_key (#50)
### Fixed
- fix bug get_all_certs_from, update example (#44)
- fix db paths (#28)
### Other
- set version of fluence-keypair to 0.8.0
- fluence-keypair 0.8.0
- libp2p-core 0.33.0 (#49)
- remove circle, update gh, add lints; remove warnings (#43)
- Add memory leak temporary mitigation (#42)
- High-level Aqua API (#35)
- fluence-keypair 0.6.0
- libp2p-core 0.31.0 (from crates.io) (#37)
- Fix revocations logic (#34)
- Remove mutex from TG instance (#31)
- refactoring (#30)
- Trust Graph: implement WASM built-in (#18)

View File

@ -1,38 +1,36 @@
[package]
name = "trust-graph-wasm"
version = "0.2.1"
version = "0.4.7"
authors = ["Fluence Labs"]
edition = "2018"
edition = "2021"
description = "trust graph wasm"
license = "Apache-2.0"
publish = false
[[bin]]
name = "trust-graph"
path = "src/main.rs"
[dependencies]
trust-graph = { version = "0.3.0", path = "../." }
fluence-keypair = { version = "0.5.0", path = "../keypair" }
marine-rs-sdk = { version = "0.6.14", features = ["logger"] }
marine-sqlite-connector = "0.5.2"
trust-graph = { version = "0.10.4-alpha.0", path = "../trust-graph" }
fluence-keypair = { version = "0.10.4-alpha.0", path = "../keypair" }
marine-rs-sdk = { version = "0.10.2", features = ["logger"] }
marine-sqlite-connector = "0.9.0"
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"]}
libp2p-identity = { workspace = true }
log = "0.4.8"
anyhow = "1.0.31"
boolinator = "2.4.0"
once_cell = "1.4.1"
parking_lot = "0.11.1"
serde_json = "1.0"
bs58 = "0.3.1"
rmp-serde = "0.15.0"
bs58 = "0.4.0"
rmp-serde = "1.1.1"
bincode = "1.3.1"
serde_bencode = "^0.2.3"
thiserror = "1.0.23"
[dev-dependencies]
marine-rs-sdk-test = "0.4.0"
rusqlite = "0.26.1"
marine-rs-sdk-test = "0.9.1"
rusqlite = "0.28.0"
[build-dependencies]
marine-rs-sdk-test = "0.4.0"
marine-rs-sdk-test = "0.9.1"

View File

@ -1,16 +1,15 @@
modules_dir = "artifacts/"
total_memory_limit = "Infinity"
[[module]]
name = "sqlite3"
logger_enabled = true
[module.wasi]
preopened_files = ["./data"]
mapped_dirs = { "data" = "data" }
mapped_dirs = { "/tmp" = "data" }
[[module]]
name = "trust-graph"
logger_enabled = true
[module.wasi]
preopened_files = ["./data"]
mapped_dirs = { "data" = "data" }
mapped_dirs = { "/tmp" = "data" }

View File

@ -13,7 +13,7 @@ mkdir -p artifacts
cp ../target/wasm32-wasi/release/trust-graph.wasm artifacts/
# download SQLite 3 to use in tests
curl -L https://github.com/fluencelabs/sqlite/releases/download/v0.15.0_w/sqlite3.wasm -o artifacts/sqlite3.wasm
curl -sS -L https://github.com/fluencelabs/sqlite/releases/download/sqlite-wasm-v0.18.1/sqlite3.wasm -o artifacts/sqlite3.wasm
# generate Aqua bindings
marine aqua artifacts/trust-graph.wasm -s TrustGraph -i trust-graph > ../aqua/trust-graph.aqua

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2021-09-01"
targets = [ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ]
channel = "nightly-2022-12-06"
targets = [ "x86_64-apple-darwin", "wasm32-wasi", "wasm32-unknown-unknown", "x86_64-unknown-linux-gnu" ]

View File

@ -1,7 +1,7 @@
use crate::dto::DtoConversionError::PeerIdDecodeError;
use fluence_keypair::error::DecodingError;
use fluence_keypair::{KeyFormat, PublicKey, Signature};
use libp2p_core::PeerId;
use libp2p_identity::PeerId;
use marine_rs_sdk::marine;
use std::convert::TryFrom;
use std::str::FromStr;
@ -40,7 +40,7 @@ pub struct Certificate {
impl From<trust_graph::Certificate> for Certificate {
fn from(c: trust_graph::Certificate) -> Self {
let chain: Vec<Trust> = c.chain.into_iter().map(|t| t.into()).collect();
return Certificate { chain };
Certificate { chain }
}
}
@ -51,10 +51,10 @@ impl TryFrom<Certificate> for trust_graph::Certificate {
let chain: Result<Vec<trust_graph::Trust>, DtoConversionError> = c
.chain
.into_iter()
.map(|t| trust_graph::Trust::try_from(t))
.map(trust_graph::Trust::try_from)
.collect();
let chain = chain?;
return Ok(trust_graph::Certificate { chain });
Ok(trust_graph::Certificate { chain })
}
}
@ -78,19 +78,19 @@ impl TryFrom<Trust> for trust_graph::Trust {
fn try_from(t: Trust) -> Result<Self, Self::Error> {
let issued_for = PublicKey::try_from(
PeerId::from_str(&t.issued_for).map_err(|e| PeerIdDecodeError(format!("{:?}", e)))?,
PeerId::from_str(&t.issued_for).map_err(|e| PeerIdDecodeError(format!("{e:?}")))?,
)
.map_err(|e| DtoConversionError::PeerIdDecodeError(e.to_string()))?;
let signature = bs58::decode(&t.signature).into_vec()?;
let signature = Signature::from_bytes(KeyFormat::from_str(&t.sig_type)?, signature);
let expires_at = Duration::from_secs(t.expires_at);
let issued_at = Duration::from_secs(t.issued_at);
return Ok(trust_graph::Trust {
Ok(trust_graph::Trust {
issued_for,
expires_at,
signature,
issued_at,
});
})
}
}
@ -101,19 +101,19 @@ impl From<trust_graph::Trust> for Trust {
let signature = bs58::encode(raw_signature.bytes).into_string();
let expires_at = t.expires_at.as_secs();
let issued_at = t.issued_at.as_secs();
return Trust {
Trust {
issued_for,
expires_at,
signature,
sig_type: raw_signature.sig_type.into(),
issued_at,
};
}
}
}
#[marine]
#[derive(Default)]
pub struct Revoke {
pub struct Revocation {
/// who is revoked
pub revoked_peer_id: String,
/// date when revocation was created
@ -126,44 +126,44 @@ pub struct Revoke {
pub revoked_by: String,
}
impl TryFrom<Revoke> for trust_graph::Revoke {
impl TryFrom<Revocation> for trust_graph::Revocation {
type Error = DtoConversionError;
fn try_from(r: Revoke) -> Result<Self, Self::Error> {
fn try_from(r: Revocation) -> Result<Self, Self::Error> {
let revoked_pk = PublicKey::try_from(
PeerId::from_str(&r.revoked_peer_id)
.map_err(|e| PeerIdDecodeError(format!("{:?}", e)))?,
.map_err(|e| PeerIdDecodeError(format!("{e:?}")))?,
)
.map_err(|e| DtoConversionError::PeerIdDecodeError(e.to_string()))?;
let revoked_by_pk = PublicKey::try_from(
PeerId::from_str(&r.revoked_by).map_err(|e| PeerIdDecodeError(format!("{:?}", e)))?,
PeerId::from_str(&r.revoked_by).map_err(|e| PeerIdDecodeError(format!("{e:?}")))?,
)
.map_err(|e| DtoConversionError::PeerIdDecodeError(e.to_string()))?;
let signature = bs58::decode(&r.signature).into_vec()?;
let signature = Signature::from_bytes(KeyFormat::from_str(&r.sig_type)?, signature);
let revoked_at = Duration::from_secs(r.revoked_at);
return Ok(trust_graph::Revoke {
Ok(trust_graph::Revocation {
pk: revoked_pk,
revoked_at,
revoked_by: revoked_by_pk,
signature,
});
})
}
}
impl From<trust_graph::Revoke> for Revoke {
fn from(r: trust_graph::Revoke) -> Self {
impl From<trust_graph::Revocation> for Revocation {
fn from(r: trust_graph::Revocation) -> Self {
let revoked_by = r.revoked_by.to_peer_id().to_base58();
let revoked_peer_id = r.pk.to_peer_id().to_base58();
let raw_signature = r.signature.get_raw_signature();
let signature = bs58::encode(raw_signature.bytes).into_string();
let revoked_at = r.revoked_at.as_secs();
return Revoke {
Revocation {
revoked_peer_id,
revoked_at,
signature,
sig_type: raw_signature.sig_type.into(),
revoked_by,
};
}
}
}

64
service/src/error.rs Normal file
View File

@ -0,0 +1,64 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use thiserror::Error as ThisError;
use crate::dto::DtoConversionError;
use fluence_keypair::error::DecodingError;
use trust_graph::{CertificateError, TrustError, TrustGraphError};
#[derive(ThisError, Debug)]
pub enum ServiceError {
#[error("peer id parse error: {0}")]
PeerIdParseError(String),
#[error("public key extraction from peer id failed: {0}")]
PublicKeyExtractionError(String),
#[error("{0}")]
PublicKeyDecodeError(
#[from]
#[source]
DecodingError,
),
#[error("{0}")]
TGError(
#[from]
#[source]
TrustGraphError,
),
#[error("{0}")]
CertError(
#[from]
#[source]
CertificateError,
),
#[error("{0}")]
DtoError(
#[from]
#[source]
DtoConversionError,
),
#[error("{0}")]
TrustError(
#[from]
#[source]
TrustError,
),
#[error("you should use host peer.timestamp_sec to pass timestamp: {0}")]
InvalidTimestampTetraplet(String),
#[error("{0} can't be issued later than the current timestamp")]
InvalidTimestamp(String),
#[error("Root could add only by trust graph service owner")]
NotOwner,
}

View File

@ -7,12 +7,15 @@ use marine_rs_sdk::WasmLoggerBuilder;
module_manifest!();
mod dto;
mod error;
mod misc;
mod results;
mod service_api;
mod service_impl;
mod storage_impl;
mod tests;
pub static TRUSTED_TIMESTAMP: (&str, &str) = ("peer", "timestamp_sec");
pub fn main() {
WasmLoggerBuilder::new()
.with_log_level(log::LevelFilter::Trace)

77
service/src/misc.rs Normal file
View File

@ -0,0 +1,77 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::error::ServiceError;
use crate::error::ServiceError::*;
use crate::storage_impl::{SQLiteStorage, DB_PATH};
use crate::TRUSTED_TIMESTAMP;
use fluence_keypair::PublicKey;
use libp2p_identity::PeerId;
use marine_rs_sdk::CallParameters;
use std::cell::RefCell;
use std::convert::TryFrom;
use std::ops::DerefMut;
use std::str::FromStr;
use trust_graph::TrustGraph;
/// Check timestamps are generated on the current host with builtin ("peer" "timestamp_sec")
#[allow(clippy::unnecessary_lazy_evaluations)]
pub(crate) fn check_timestamp_tetraplets(
call_parameters: &CallParameters,
arg_number: usize,
) -> Result<(), ServiceError> {
let tetraplets = call_parameters
.tetraplets
.get(arg_number)
.ok_or_else(|| InvalidTimestampTetraplet(format!("{:?}", call_parameters.tetraplets)))?;
let tetraplet = tetraplets
.get(0)
.ok_or_else(|| InvalidTimestampTetraplet(format!("{:?}", call_parameters.tetraplets)))?;
(TRUSTED_TIMESTAMP.eq(&(&tetraplet.service_id, &tetraplet.function_name))
&& tetraplet.peer_pk == call_parameters.host_id)
.then(|| ())
.ok_or_else(|| InvalidTimestampTetraplet(format!("{tetraplet:?}")))
}
fn parse_peer_id(peer_id: String) -> Result<PeerId, ServiceError> {
libp2p_identity::PeerId::from_str(&peer_id)
.map_err(|e| ServiceError::PeerIdParseError(format!("{e:?}")))
}
thread_local!(static INSTANCE: RefCell<TrustGraph<SQLiteStorage>> = RefCell::new(TrustGraph::new(
SQLiteStorage::new(marine_sqlite_connector::open(DB_PATH).unwrap()),
)));
pub fn with_tg<F, T>(func: F) -> T
where
F: FnOnce(&mut TrustGraph<SQLiteStorage>) -> T,
{
INSTANCE.with(|tg| func(tg.borrow_mut().deref_mut()))
}
pub fn wrapped_try<F, T>(func: F) -> T
where
F: FnOnce() -> T,
{
func()
}
pub fn extract_public_key(peer_id: String) -> Result<PublicKey, ServiceError> {
PublicKey::try_from(
parse_peer_id(peer_id)
.map_err(|e| ServiceError::PublicKeyExtractionError(e.to_string()))?,
)
.map_err(ServiceError::PublicKeyDecodeError)
}

View File

@ -1,5 +1,5 @@
use crate::dto::{Certificate, Revoke, Trust};
use crate::service_impl::ServiceError;
use crate::dto::{Certificate, Revocation, Trust};
use crate::error::ServiceError;
use marine_rs_sdk::marine;
#[marine]
@ -17,7 +17,7 @@ impl From<Result<(), ServiceError>> for InsertResult {
},
Err(e) => InsertResult {
success: false,
error: format!("{}", e),
error: format!("{e}"),
},
}
}
@ -44,7 +44,7 @@ impl From<Result<(u32, String), ServiceError>> for WeightResult {
success: false,
weight: 0u32,
peer_id: "".to_string(),
error: format!("{}", e),
error: format!("{e}"),
},
}
}
@ -60,36 +60,36 @@ pub struct AllCertsResult {
impl From<Result<Vec<Certificate>, ServiceError>> for AllCertsResult {
fn from(result: Result<Vec<Certificate>, ServiceError>) -> Self {
match result {
Ok(certs) => AllCertsResult {
Ok(certificates) => AllCertsResult {
success: true,
certificates: certs,
certificates,
error: "".to_string(),
},
Err(e) => AllCertsResult {
success: false,
certificates: vec![],
error: format!("{}", e),
error: format!("{e}"),
},
}
}
}
#[marine]
pub struct AddRootResult {
pub struct SetRootResult {
pub success: bool,
pub error: String,
}
impl From<Result<(), ServiceError>> for AddRootResult {
impl From<Result<(), ServiceError>> for SetRootResult {
fn from(result: Result<(), ServiceError>) -> Self {
match result {
Ok(()) => AddRootResult {
Ok(()) => SetRootResult {
success: true,
error: "".to_string(),
},
Err(e) => AddRootResult {
Err(e) => SetRootResult {
success: false,
error: format!("{}", e),
error: format!("{e}"),
},
}
}
@ -112,7 +112,7 @@ impl From<Result<Vec<u8>, ServiceError>> for GetTrustBytesResult {
},
Err(e) => GetTrustBytesResult {
success: false,
error: format!("{}", e),
error: format!("{e}"),
result: vec![],
},
}
@ -136,7 +136,7 @@ impl From<Result<Trust, ServiceError>> for IssueTrustResult {
},
Err(e) => IssueTrustResult {
success: false,
error: format!("{}", e),
error: format!("{e}"),
trust: Trust::default(),
},
}
@ -158,7 +158,7 @@ impl From<Result<(), ServiceError>> for VerifyTrustResult {
},
Err(e) => VerifyTrustResult {
success: false,
error: format!("{}", e),
error: format!("{e}"),
},
}
}
@ -181,7 +181,7 @@ impl From<Result<u32, ServiceError>> for AddTrustResult {
},
Err(e) => AddTrustResult {
success: false,
error: format!("{}", e),
error: format!("{e}"),
weight: u32::default(),
},
}
@ -205,7 +205,7 @@ impl From<Result<Vec<u8>, ServiceError>> for GetRevokeBytesResult {
},
Err(e) => GetRevokeBytesResult {
success: false,
error: format!("{}", e),
error: format!("{e}"),
result: vec![],
},
}
@ -216,21 +216,21 @@ impl From<Result<Vec<u8>, ServiceError>> for GetRevokeBytesResult {
pub struct IssueRevocationResult {
pub success: bool,
pub error: String,
pub revoke: Revoke,
pub revocation: Revocation,
}
impl From<Result<Revoke, ServiceError>> for IssueRevocationResult {
fn from(result: Result<Revoke, ServiceError>) -> Self {
impl From<Result<Revocation, ServiceError>> for IssueRevocationResult {
fn from(result: Result<Revocation, ServiceError>) -> Self {
match result {
Ok(revoke) => IssueRevocationResult {
Ok(revocation) => IssueRevocationResult {
success: true,
error: "".to_string(),
revoke,
revocation,
},
Err(e) => IssueRevocationResult {
success: false,
error: format!("{}", e),
revoke: Revoke::default(),
error: format!("{e}"),
revocation: Revocation::default(),
},
}
}
@ -251,7 +251,31 @@ impl From<Result<(), ServiceError>> for RevokeResult {
},
Err(e) => RevokeResult {
success: false,
error: format!("{}", e),
error: format!("{e}"),
},
}
}
}
#[marine]
pub struct ExportRevocationsResult {
pub success: bool,
pub revocations: Vec<Revocation>,
pub error: String,
}
impl From<Result<Vec<Revocation>, ServiceError>> for ExportRevocationsResult {
fn from(result: Result<Vec<Revocation>, ServiceError>) -> Self {
match result {
Ok(revocations) => ExportRevocationsResult {
success: true,
revocations,
error: "".to_string(),
},
Err(e) => ExportRevocationsResult {
success: false,
revocations: vec![],
error: format!("{e}"),
},
}
}

View File

@ -1,34 +1,36 @@
use crate::dto::{Certificate, Revoke, Trust};
use crate::dto::{Certificate, Revocation, Trust};
use crate::error::ServiceError;
use crate::misc::{check_timestamp_tetraplets, extract_public_key, with_tg, wrapped_try};
use crate::results::{
AddRootResult, AddTrustResult, AllCertsResult, GetRevokeBytesResult, GetTrustBytesResult,
InsertResult, IssueRevocationResult, IssueTrustResult, RevokeResult, VerifyTrustResult,
WeightResult,
};
use crate::service_impl::{
add_root_impl, add_trust_impl, get_all_certs_impl, get_host_certs_impl, get_revoke_bytes_impl,
get_trust_bytes_impl, get_weight_impl, insert_cert_impl, insert_cert_impl_raw,
issue_revocation_impl, issue_trust_impl, revoke_impl, verify_trust_impl, ServiceError,
AddTrustResult, AllCertsResult, ExportRevocationsResult, GetRevokeBytesResult,
GetTrustBytesResult, InsertResult, IssueRevocationResult, IssueTrustResult, RevokeResult,
SetRootResult, VerifyTrustResult, WeightResult,
};
use crate::storage_impl::SQLiteStorage;
use fluence_keypair::Signature;
use marine_rs_sdk::{get_call_parameters, marine, CallParameters};
use trust_graph::MAX_WEIGHT_FACTOR;
use std::convert::{TryFrom, TryInto};
use std::str::FromStr;
use std::time::Duration;
use trust_graph::TrustGraph;
#[marine]
fn get_weight_factor(max_chain_len: u32) -> u32 {
MAX_WEIGHT_FACTOR.checked_sub(max_chain_len).unwrap_or(0u32)
}
#[marine]
/// could add only a owner of a trust graph service
fn add_root(peer_id: String, weight_factor: u32) -> AddRootResult {
/// Only service owner can set roots
fn set_root(peer_id: String, max_chain_len: u32) -> SetRootResult {
let call_parameters: CallParameters = marine_rs_sdk::get_call_parameters();
let init_peer_id = call_parameters.init_peer_id;
if call_parameters.service_creator_peer_id == init_peer_id {
add_root_impl(peer_id, weight_factor).into()
with_tg(|tg| {
let public_key = extract_public_key(peer_id)?;
tg.set_root(public_key, max_chain_len)?;
Ok(())
})
.into()
} else {
return AddRootResult {
SetRootResult {
success: false,
error: ServiceError::NotOwner.to_string(),
};
}
}
}
@ -36,44 +38,116 @@ fn add_root(peer_id: String, weight_factor: u32) -> AddRootResult {
/// add a certificate in string representation to trust graph if it is valid
/// see `trust_graph::Certificate` class for string encoding/decoding
fn insert_cert_raw(certificate: String, timestamp_sec: u64) -> InsertResult {
insert_cert_impl_raw(certificate, timestamp_sec).into()
with_tg(|tg| {
let certificate = trust_graph::Certificate::from_str(&certificate)?;
let timestamp_sec = Duration::from_secs(timestamp_sec);
tg.add(certificate, timestamp_sec)?;
Ok(())
})
.into()
}
#[marine]
/// add a certificate in JSON representation to trust graph if it is valid
/// see `dto::Certificate` class for structure
fn insert_cert(certificate: Certificate, timestamp_sec: u64) -> InsertResult {
insert_cert_impl(certificate, timestamp_sec).into()
with_tg(|tg| {
let timestamp_sec = Duration::from_secs(timestamp_sec);
tg.add(
trust_graph::Certificate::try_from(certificate)?,
timestamp_sec,
)?;
Ok(())
})
.into()
}
fn get_certs(
tg: &mut TrustGraph<SQLiteStorage>,
issued_for: String,
timestamp_sec: u64,
) -> Result<Vec<Certificate>, ServiceError> {
let public_key = extract_public_key(issued_for)?;
let certs = tg.get_all_certs(public_key, Duration::from_secs(timestamp_sec))?;
Ok(certs.into_iter().map(|c| c.into()).collect())
}
fn get_certs_from(
tg: &mut TrustGraph<SQLiteStorage>,
issued_for: String,
issuer: String,
timestamp_sec: u64,
) -> Result<Vec<Certificate>, ServiceError> {
let issued_for_pk = extract_public_key(issued_for)?;
let issuer_pk = extract_public_key(issuer)?;
let certs =
tg.get_all_certs_from(issued_for_pk, issuer_pk, Duration::from_secs(timestamp_sec))?;
Ok(certs.into_iter().map(|c| c.into()).collect())
}
#[marine]
fn get_all_certs(issued_for: String, timestamp_sec: u64) -> AllCertsResult {
get_all_certs_impl(issued_for, timestamp_sec).into()
with_tg(|tg| {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
get_certs(tg, issued_for, timestamp_sec)
})
.into()
}
#[marine]
fn get_all_certs_from(issued_for: String, issuer: String, timestamp_sec: u64) -> AllCertsResult {
with_tg(|tg| {
let cp = get_call_parameters();
check_timestamp_tetraplets(&cp, 2)?;
get_certs_from(tg, issued_for, issuer, timestamp_sec)
})
.into()
}
#[marine]
fn get_host_certs(timestamp_sec: u64) -> AllCertsResult {
get_host_certs_impl(timestamp_sec).into()
with_tg(|tg| {
let cp = marine_rs_sdk::get_call_parameters();
check_timestamp_tetraplets(&cp, 0)?;
get_certs(tg, cp.host_id, timestamp_sec)
})
.into()
}
#[marine]
fn get_host_certs_from(issuer: String, timestamp_sec: u64) -> AllCertsResult {
let host_id = get_call_parameters().host_id;
get_all_certs_impl(host_id, timestamp_sec)
.map(|certs| {
certs
.into_iter()
.filter(|cert| cert.chain.iter().any(|t| t.issued_for == issuer))
.collect()
})
.into()
with_tg(|tg| {
let cp = get_call_parameters();
check_timestamp_tetraplets(&cp, 1)?;
get_certs_from(tg, cp.host_id, issuer, timestamp_sec)
})
.into()
}
#[marine]
fn get_weight(peer_id: String, timestamp_sec: u64) -> WeightResult {
get_weight_impl(peer_id.clone(), timestamp_sec)
.map(|w| (w, peer_id))
.into()
with_tg(|tg| {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
let public_key = extract_public_key(peer_id.clone())?;
let weight = tg.weight(public_key, Duration::from_secs(timestamp_sec))?;
Ok(weight)
})
.map(|w| (w, peer_id))
.into()
}
#[marine]
fn get_weight_from(peer_id: String, issuer: String, timestamp_sec: u64) -> WeightResult {
with_tg(|tg| {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
let issued_for_pk = extract_public_key(peer_id.clone())?;
let issuer_pk = extract_public_key(issuer)?;
let weight =
tg.weight_from(issued_for_pk, issuer_pk, Duration::from_secs(timestamp_sec))?;
Ok(weight)
})
.map(|w| (w, peer_id))
.into()
}
#[marine]
@ -82,7 +156,16 @@ fn get_trust_bytes(
expires_at_sec: u64,
issued_at_sec: u64,
) -> GetTrustBytesResult {
get_trust_bytes_impl(issued_for_peer_id, expires_at_sec, issued_at_sec).into()
wrapped_try(|| {
let public_key = extract_public_key(issued_for_peer_id)?;
Ok(trust_graph::Trust::signature_bytes(
&public_key,
Duration::from_secs(expires_at_sec),
Duration::from_secs(issued_at_sec),
))
})
.into()
}
#[marine]
@ -92,47 +175,109 @@ fn issue_trust(
issued_at_sec: u64,
trust_bytes: Vec<u8>,
) -> IssueTrustResult {
issue_trust_impl(
issued_for_peer_id,
expires_at_sec,
issued_at_sec,
trust_bytes,
)
wrapped_try(|| {
let public_key = extract_public_key(issued_for_peer_id)?;
let expires_at_sec = Duration::from_secs(expires_at_sec);
let issued_at_sec = Duration::from_secs(issued_at_sec);
let signature = Signature::from_bytes(public_key.get_key_format(), trust_bytes);
Ok(Trust::from(trust_graph::Trust::new(
public_key,
expires_at_sec,
issued_at_sec,
signature,
)))
})
.into()
}
#[marine]
fn verify_trust(trust: Trust, issuer_peer_id: String, timestamp_sec: u64) -> VerifyTrustResult {
verify_trust_impl(trust, issuer_peer_id, timestamp_sec).into()
}
wrapped_try(|| {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 2)?;
let public_key = extract_public_key(issuer_peer_id)?;
trust_graph::Trust::verify(
&trust.try_into()?,
&public_key,
Duration::from_secs(timestamp_sec),
)?;
#[marine]
fn add_trust(trust: Trust, issuer_peer_id: String, timestamp_sec: u64) -> AddTrustResult {
add_trust_impl(trust, issuer_peer_id, timestamp_sec).into()
}
#[marine]
fn get_revoke_bytes(revoked_peer_id: String, revoked_at: u64) -> GetRevokeBytesResult {
get_revoke_bytes_impl(revoked_peer_id, revoked_at).into()
}
#[marine]
fn issue_revocation(
revoked_peer_id: String,
revoked_by_peer_id: String,
revoked_at_sec: u64,
signature_bytes: Vec<u8>,
) -> IssueRevocationResult {
issue_revocation_impl(
revoked_peer_id,
revoked_by_peer_id,
revoked_at_sec,
signature_bytes,
)
Ok(())
})
.into()
}
#[marine]
fn revoke(revoke: Revoke, timestamp_sec: u64) -> RevokeResult {
revoke_impl(revoke, timestamp_sec).into()
fn add_trust(trust: Trust, issuer_peer_id: String, timestamp_sec: u64) -> AddTrustResult {
with_tg(|tg| {
let public_key = extract_public_key(issuer_peer_id)?;
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 2)?;
if trust.issued_at > timestamp_sec {
return Err(ServiceError::InvalidTimestamp("trust".to_string()));
}
Ok(tg.add_trust(
&trust.try_into()?,
public_key,
Duration::from_secs(timestamp_sec),
)?)
})
.into()
}
#[marine]
fn get_revocation_bytes(revoked_peer_id: String, revoked_at: u64) -> GetRevokeBytesResult {
wrapped_try(|| {
let public_key = extract_public_key(revoked_peer_id)?;
Ok(trust_graph::Revocation::signature_bytes(
&public_key,
Duration::from_secs(revoked_at),
))
})
.into()
}
#[marine]
fn issue_revocation(
revoked_by_peer_id: String,
revoked_peer_id: String,
revoked_at_sec: u64,
signature_bytes: Vec<u8>,
) -> IssueRevocationResult {
wrapped_try(|| {
let revoked_pk = extract_public_key(revoked_peer_id)?;
let revoked_by_pk = extract_public_key(revoked_by_peer_id)?;
let revoked_at = Duration::from_secs(revoked_at_sec);
let signature = Signature::from_bytes(revoked_by_pk.get_key_format(), signature_bytes);
Ok(trust_graph::Revocation::new(revoked_by_pk, revoked_pk, revoked_at, signature).into())
})
.into()
}
#[marine]
fn revoke(revoke: Revocation, timestamp_sec: u64) -> RevokeResult {
with_tg(|tg| {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
if revoke.revoked_at > timestamp_sec {
return Err(ServiceError::InvalidTimestamp("revoke".to_string()));
}
Ok(tg.revoke(revoke.try_into()?)?)
})
.into()
}
#[marine]
fn export_revocations(issued_for: String) -> ExportRevocationsResult {
with_tg(|tg| {
let issued_for_pk = extract_public_key(issued_for)?;
Ok(tg
.get_revocations(issued_for_pk)?
.into_iter()
.map(|r| r.into())
.collect())
})
.into()
}

View File

@ -1,255 +0,0 @@
use crate::dto::{Certificate, DtoConversionError, Revoke, Trust};
use crate::service_impl::ServiceError::InvalidTimestampTetraplet;
use crate::storage_impl::get_data;
use fluence_keypair::error::DecodingError;
use fluence_keypair::{PublicKey, Signature};
use libp2p_core::PeerId;
use marine_rs_sdk::CallParameters;
use std::convert::{Into, TryFrom, TryInto};
use std::str::FromStr;
use std::time::Duration;
use thiserror::Error as ThisError;
use trust_graph::{CertificateError, TrustError, TrustGraphError};
pub static TRUSTED_TIMESTAMP_SERVICE_ID: &str = "peer";
pub static TRUSTED_TIMESTAMP_FUNCTION_NAME: &str = "timestamp_sec";
/// Check timestamps are generated on the current host with builtin ("peer" "timestamp_sec")
pub(crate) fn check_timestamp_tetraplets(
call_parameters: &CallParameters,
arg_number: usize,
) -> Result<(), ServiceError> {
let tetraplets = call_parameters
.tetraplets
.get(arg_number)
.ok_or(InvalidTimestampTetraplet)?;
let tetraplet = tetraplets.get(0).ok_or(InvalidTimestampTetraplet)?;
(tetraplet.service_id == TRUSTED_TIMESTAMP_SERVICE_ID
&& tetraplet.function_name == TRUSTED_TIMESTAMP_FUNCTION_NAME
&& tetraplet.peer_pk == call_parameters.host_id)
.then(|| ())
.ok_or(InvalidTimestampTetraplet)
}
#[derive(ThisError, Debug)]
pub enum ServiceError {
#[error("peer id parse error: {0}")]
PeerIdParseError(String),
#[error("public key extraction from peer id failed: {0}")]
PublicKeyExtractionError(String),
#[error("{0}")]
PublicKeyDecodeError(
#[from]
#[source]
DecodingError,
),
#[error("{0}")]
TGError(
#[from]
#[source]
TrustGraphError,
),
#[error("{0}")]
CertError(
#[from]
#[source]
CertificateError,
),
#[error("{0}")]
DtoError(
#[from]
#[source]
DtoConversionError,
),
#[error("{0}")]
TrustError(
#[from]
#[source]
TrustError,
),
#[error("you should use host peer.timestamp_sec to pass timestamp")]
InvalidTimestampTetraplet,
#[error("{0} can't be issued later than the current timestamp")]
InvalidTimestamp(String),
#[error("Root could add only by trust graph service owner")]
NotOwner,
}
fn parse_peer_id(peer_id: String) -> Result<PeerId, ServiceError> {
libp2p_core::PeerId::from_str(&peer_id)
.map_err(|e| ServiceError::PeerIdParseError(format!("{:?}", e)))
}
fn extract_public_key(peer_id: String) -> Result<PublicKey, ServiceError> {
PublicKey::try_from(
parse_peer_id(peer_id)
.map_err(|e| ServiceError::PublicKeyExtractionError(e.to_string()))?,
)
.map_err(ServiceError::PublicKeyDecodeError)
}
pub fn get_weight_impl(peer_id: String, timestamp_sec: u64) -> Result<u32, ServiceError> {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
let mut tg = get_data().lock();
let public_key = extract_public_key(peer_id)?;
let weight = tg.weight(public_key, Duration::from_secs(timestamp_sec))?;
Ok(weight)
}
fn add_cert(certificate: trust_graph::Certificate, timestamp_sec: u64) -> Result<(), ServiceError> {
let timestamp_sec = Duration::from_secs(timestamp_sec);
let mut tg = get_data().lock();
tg.add(certificate, timestamp_sec)?;
Ok(())
}
pub fn insert_cert_impl_raw(certificate: String, timestamp_sec: u64) -> Result<(), ServiceError> {
let certificate = trust_graph::Certificate::from_str(&certificate)?;
add_cert(certificate, timestamp_sec)?;
Ok(())
}
pub fn get_all_certs_impl(
issued_for: String,
timestamp_sec: u64,
) -> Result<Vec<Certificate>, ServiceError> {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
get_certs_helper(issued_for, timestamp_sec)
}
pub fn get_host_certs_impl(timestamp_sec: u64) -> Result<Vec<Certificate>, ServiceError> {
let cp = marine_rs_sdk::get_call_parameters();
check_timestamp_tetraplets(&cp, 0)?;
get_certs_helper(cp.host_id, timestamp_sec)
}
pub fn get_certs_helper(
issued_for: String,
timestamp_sec: u64,
) -> Result<Vec<Certificate>, ServiceError> {
let mut tg = get_data().lock();
let public_key = extract_public_key(issued_for)?;
let certs = tg.get_all_certs(public_key, Duration::from_secs(timestamp_sec))?;
Ok(certs.into_iter().map(|c| c.into()).collect())
}
pub fn insert_cert_impl(certificate: Certificate, timestamp_sec: u64) -> Result<(), ServiceError> {
let certificate: trust_graph::Certificate = certificate.try_into()?;
add_cert(certificate, timestamp_sec)?;
Ok(())
}
pub fn add_root_impl(peer_id: String, weight: u32) -> Result<(), ServiceError> {
let mut tg = get_data().lock();
let public_key = extract_public_key(peer_id)?;
tg.add_root_weight_factor(public_key, weight)?;
Ok(())
}
pub fn get_trust_bytes_impl(
peer_id: String,
expires_at_sec: u64,
issued_at_sec: u64,
) -> Result<Vec<u8>, ServiceError> {
let public_key = extract_public_key(peer_id)?;
Ok(trust_graph::Trust::signature_bytes(
&public_key,
Duration::from_secs(expires_at_sec),
Duration::from_secs(issued_at_sec),
))
}
pub fn issue_trust_impl(
peer_id: String,
expires_at_sec: u64,
issued_at_sec: u64,
trust_bytes: Vec<u8>,
) -> Result<Trust, ServiceError> {
let public_key = extract_public_key(peer_id)?;
let expires_at_sec = Duration::from_secs(expires_at_sec);
let issued_at_sec = Duration::from_secs(issued_at_sec);
let signature = Signature::from_bytes(public_key.get_key_format(), trust_bytes);
Ok(Trust::from(trust_graph::Trust::new(
public_key,
expires_at_sec,
issued_at_sec,
signature,
)))
}
pub fn verify_trust_impl(
trust: Trust,
issuer_peer_id: String,
timestamp_sec: u64,
) -> Result<(), ServiceError> {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 2)?;
let public_key = extract_public_key(issuer_peer_id)?;
trust_graph::Trust::verify(
&trust.try_into()?,
&public_key,
Duration::from_secs(timestamp_sec),
)?;
Ok(())
}
pub fn add_trust_impl(
trust: Trust,
issuer_peer_id: String,
timestamp_sec: u64,
) -> Result<u32, ServiceError> {
let public_key = extract_public_key(issuer_peer_id)?;
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 2)?;
if trust.issued_at > timestamp_sec {
return Err(ServiceError::InvalidTimestamp("Trust".to_string()));
}
let mut tg = get_data().lock();
tg.add_trust(
&trust.try_into()?,
public_key,
Duration::from_secs(timestamp_sec),
)
.map_err(ServiceError::TGError)
}
pub fn get_revoke_bytes_impl(
revoked_peer_id: String,
revoked_at: u64,
) -> Result<Vec<u8>, ServiceError> {
let public_key = extract_public_key(revoked_peer_id)?;
Ok(trust_graph::Revoke::signature_bytes(
&public_key,
Duration::from_secs(revoked_at),
))
}
pub fn issue_revocation_impl(
revoked_peer_id: String,
revoked_by_peer_id: String,
revoked_at_sec: u64,
signature_bytes: Vec<u8>,
) -> Result<Revoke, ServiceError> {
let revoked_pk = extract_public_key(revoked_peer_id)?;
let revoked_by_pk = extract_public_key(revoked_by_peer_id)?;
let revoked_at = Duration::from_secs(revoked_at_sec);
let signature = Signature::from_bytes(revoked_by_pk.get_key_format(), signature_bytes);
Ok(trust_graph::Revoke::new(revoked_pk, revoked_by_pk, revoked_at, signature).into())
}
pub fn revoke_impl(revoke: Revoke, timestamp_sec: u64) -> Result<(), ServiceError> {
check_timestamp_tetraplets(&marine_rs_sdk::get_call_parameters(), 1)?;
if revoke.revoked_at > timestamp_sec {
return Err(ServiceError::InvalidTimestamp("Revoke".to_string()));
}
let mut tg = get_data().lock();
tg.revoke(revoke.try_into()?).map_err(ServiceError::TGError)
}

View File

@ -10,8 +10,6 @@ use core::convert::TryFrom;
use fluence_keypair::error::DecodingError;
use fluence_keypair::Signature;
use marine_sqlite_connector::{Connection, Error as InternalSqliteError, Value};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use rmp_serde::decode::Error as RmpDecodeError;
use rmp_serde::encode::Error as RmpEncodeError;
use std::convert::From;
@ -19,16 +17,13 @@ use std::str::FromStr;
use std::time::Duration;
use thiserror::Error as ThisError;
use trust_graph::{
Auth, PublicKeyHashable as PK, PublicKeyHashable, Revoke, Storage, StorageError, Trust,
TrustGraph, TrustRelation, WeightFactor,
Auth, PublicKeyHashable as PK, PublicKeyHashable, Revocation, Storage, StorageError, Trust,
TrustRelation, WeightFactor,
};
#[allow(dead_code)]
static INSTANCE: OnceCell<Mutex<TrustGraph<SQLiteStorage>>> = OnceCell::new();
static AUTH_TYPE: i64 = 0;
static REVOKE_TYPE: i64 = 1;
pub static DB_PATH: &str = "data/users12312233.sqlite";
static REVOCATION_TYPE: i64 = 1;
pub static DB_PATH: &str = "/tmp/trust-graph.sqlite";
pub fn create_tables() {
let connection = marine_sqlite_connector::open(DB_PATH).unwrap();
@ -56,14 +51,6 @@ pub fn create_tables() {
.unwrap();
}
#[allow(dead_code)]
pub fn get_data() -> &'static Mutex<TrustGraph<SQLiteStorage>> {
INSTANCE.get_or_init(|| {
let connection = marine_sqlite_connector::open(DB_PATH).unwrap();
Mutex::new(TrustGraph::new(SQLiteStorage::new(connection)))
})
}
pub struct SQLiteStorage {
connection: Connection,
}
@ -85,7 +72,7 @@ impl SQLiteStorage {
}
}
Some(TrustRelation::Revoke(revoke)) => {
Some(TrustRelation::Revocation(revoke)) => {
if revoke.revoked_at < relation.issued_at() {
self.insert(relation)?;
}
@ -98,6 +85,35 @@ impl SQLiteStorage {
Ok(())
}
fn get_relations(
&self,
issued_for: &PublicKeyHashable,
relation_type: i64,
) -> Result<Vec<TrustRelation>, SQLiteStorageError> {
let mut cursor = self
.connection
.prepare(
"SELECT relation_type, issued_for, issued_by, issued_at, expires_at, signature \
FROM trust_relations WHERE issued_for = ? and relation_type = ?",
)?
.cursor();
cursor.bind(&[
Value::String(format!("{issued_for}")),
Value::Integer(relation_type),
])?;
let mut relations: Vec<TrustRelation> = vec![];
while let Some(row) = cursor.next()? {
match parse_relation(row) {
Ok(r) => relations.push(r),
Err(e) => log::error!("parse_relation: {:?}", e),
}
}
Ok(relations)
}
}
#[derive(ThisError, Debug)]
@ -156,7 +172,7 @@ fn parse_relation(row: &[Value]) -> Result<TrustRelation, SQLiteStorageError> {
issued_by: issued_by.into(),
}))
} else {
Ok(TrustRelation::Revoke(Revoke {
Ok(TrustRelation::Revocation(Revocation {
pk: issued_for.into(),
revoked_at: issued_at,
revoked_by: issued_by.into(),
@ -190,8 +206,8 @@ impl Storage for SQLiteStorage {
.cursor();
cursor.bind(&[
Value::String(format!("{}", issued_by)),
Value::String(format!("{}", issued_for)),
Value::String(format!("{issued_by}")),
Value::String(format!("{issued_for}")),
])?;
if let Some(row) = cursor.next()? {
@ -202,25 +218,34 @@ impl Storage for SQLiteStorage {
}
/// return all auths issued for pk
fn get_authorizations(&self, pk: &PublicKeyHashable) -> Result<Vec<Auth>, Self::Error> {
let mut cursor = self
.connection
.prepare(
"SELECT relation_type, issued_for, issued_by, issued_at, expires_at, signature \
FROM trust_relations WHERE issued_for = ? and relation_type = ?",
)?
.cursor();
fn get_authorizations(&self, issued_for: &PublicKeyHashable) -> Result<Vec<Auth>, Self::Error> {
Ok(self
.get_relations(issued_for, AUTH_TYPE)?
.into_iter()
.fold(vec![], |mut acc, r| {
if let TrustRelation::Auth(a) = r {
acc.push(a);
}
cursor.bind(&[Value::String(format!("{}", pk)), Value::Integer(AUTH_TYPE)])?;
let mut auths: Vec<Auth> = vec![];
acc
}))
}
while let Some(row) = cursor.next()? {
if let TrustRelation::Auth(auth) = parse_relation(row)? {
auths.push(auth);
}
}
/// return all revocations issued for pk
fn get_revocations(
&self,
issued_for: &PublicKeyHashable,
) -> Result<Vec<Revocation>, Self::Error> {
Ok(self
.get_relations(issued_for, REVOCATION_TYPE)?
.into_iter()
.fold(vec![], |mut acc, r| {
if let TrustRelation::Revocation(revocation) = r {
acc.push(revocation);
}
Ok(auths)
acc
}))
}
fn insert(&mut self, relation: TrustRelation) -> Result<(), Self::Error> {
@ -230,7 +255,7 @@ impl Storage for SQLiteStorage {
let relation_type = match relation {
TrustRelation::Auth(_) => AUTH_TYPE,
TrustRelation::Revoke(_) => REVOKE_TYPE,
TrustRelation::Revocation(_) => REVOCATION_TYPE,
};
statement.bind(1, &Value::Integer(relation_type))?;
@ -247,7 +272,7 @@ impl Storage for SQLiteStorage {
statement.bind(6, &Value::Binary(relation.signature().encode()))?;
statement.next()?;
Ok({})
Ok(())
}
fn get_root_weight_factor(&self, pk: &PK) -> Result<Option<WeightFactor>, Self::Error> {
@ -256,7 +281,7 @@ impl Storage for SQLiteStorage {
.prepare("SELECT public_key, weight_factor FROM roots WHERE public_key = ?")?
.cursor();
cursor.bind(&[Value::String(format!("{}", pk))])?;
cursor.bind(&[Value::String(format!("{pk}"))])?;
if let Some(row) = cursor.next()? {
let w = u32::try_from(row[1].as_integer().ok_or(WeightFactorConversionDB)?)
@ -268,7 +293,7 @@ impl Storage for SQLiteStorage {
}
}
fn add_root_weight_factor(
fn set_root_weight_factor(
&mut self,
pk: PK,
weight_factor: WeightFactor,
@ -279,12 +304,12 @@ impl Storage for SQLiteStorage {
.cursor();
cursor.bind(&[
Value::String(format!("{}", pk)),
Value::String(format!("{pk}")),
Value::Integer(i64::from(weight_factor)),
])?;
cursor.next()?;
Ok({})
Ok(())
}
fn root_keys(&self) -> Result<Vec<PK>, Self::Error> {
@ -296,7 +321,6 @@ impl Storage for SQLiteStorage {
let mut roots = vec![];
while let Some(row) = cursor.next()? {
log::info!("row: {:?}", row);
let pk = row[0].as_string().ok_or(PublicKeyConversion)?;
let pk: PK = PK::from_str(pk).map_err(|e| PublicKeyFromStr(e.to_string()))?;
@ -306,8 +330,8 @@ impl Storage for SQLiteStorage {
Ok(roots)
}
fn revoke(&mut self, revoke: Revoke) -> Result<(), Self::Error> {
self.update_relation(TrustRelation::Revoke(revoke))
fn revoke(&mut self, revoke: Revocation) -> Result<(), Self::Error> {
self.update_relation(TrustRelation::Revocation(revoke))
}
fn update_auth(&mut self, auth: Auth, _cur_time: Duration) -> Result<(), Self::Error> {

View File

@ -17,20 +17,19 @@
#[cfg(test)]
mod service_tests {
marine_rs_sdk_test::include_test_env!("/marine_test_env.rs");
use crate::service_impl::{
ServiceError, TRUSTED_TIMESTAMP_FUNCTION_NAME, TRUSTED_TIMESTAMP_SERVICE_ID,
};
use crate::storage_impl::DB_PATH;
use crate::error::ServiceError;
use crate::TRUSTED_TIMESTAMP;
use fluence_keypair::KeyPair;
use libp2p_core::PeerId;
use libp2p_identity::PeerId;
use marine_rs_sdk::{CallParameters, SecurityTetraplet};
use marine_test_env::trust_graph::{Certificate, Revoke, ServiceInterface, Trust};
use rusqlite::Connection;
use marine_test_env::trust_graph::{Certificate, Revocation, ServiceInterface, Trust};
use std::collections::HashMap;
use std::time::{SystemTime, UNIX_EPOCH};
static HOST_ID: &str = "some_host_id";
static TEST_DB_PATH: &str = "data/trust-graph.sqlite";
struct Auth {
issuer: PeerId,
trust: Trust,
@ -56,12 +55,7 @@ mod service_tests {
}
fn clear_env() {
let connection = Connection::open(DB_PATH).unwrap();
connection
.execute("DELETE FROM trust_relations", [])
.unwrap();
connection.execute("DELETE FROM roots", []).unwrap();
std::fs::remove_file(TEST_DB_PATH).unwrap_or_default();
}
fn get_correct_timestamp_cp(arg_number: usize) -> CallParameters {
@ -80,16 +74,16 @@ mod service_tests {
cp.tetraplets.push(vec![SecurityTetraplet {
peer_pk: host_id,
service_id: TRUSTED_TIMESTAMP_SERVICE_ID.to_string(),
function_name: TRUSTED_TIMESTAMP_FUNCTION_NAME.to_string(),
service_id: TRUSTED_TIMESTAMP.0.to_string(),
function_name: TRUSTED_TIMESTAMP.1.to_string(),
json_path: "".to_string(),
}]);
cp
}
fn add_root_peer_id(trust_graph: &mut ServiceInterface, peer_id: PeerId, weight_factor: u32) {
let result = trust_graph.add_root(peer_id.to_base58(), weight_factor);
fn set_root_peer_id(trust_graph: &mut ServiceInterface, peer_id: PeerId, max_chain_len: u32) {
let result = trust_graph.set_root(peer_id.to_base58(), max_chain_len);
assert!(result.success, "{}", result.error);
}
@ -98,9 +92,9 @@ mod service_tests {
issuer_kp: &KeyPair,
issued_at_sec: u64,
expires_at_sec: u64,
weight_factor: u32,
max_chain_len: u32,
) -> Trust {
let result = trust_graph.add_root(issuer_kp.get_peer_id().to_base58(), weight_factor);
let result = trust_graph.set_root(issuer_kp.get_peer_id().to_base58(), max_chain_len);
assert!(result.success, "{}", result.error);
add_trust(
trust_graph,
@ -201,28 +195,28 @@ mod service_tests {
issuer_kp: &KeyPair,
revoked_peer_id: &PeerId,
revoked_at_sec: u64,
) -> Revoke {
let result = trust_graph.get_revoke_bytes(revoked_peer_id.to_base58(), revoked_at_sec);
) -> Revocation {
let result = trust_graph.get_revocation_bytes(revoked_peer_id.to_base58(), revoked_at_sec);
assert!(result.success, "{}", result.error);
let revoke_bytes = issuer_kp.sign(&result.result).unwrap().to_vec().to_vec();
let issue_result = trust_graph.issue_revocation(
revoked_peer_id.to_base58(),
issuer_kp.get_peer_id().to_base58(),
revoked_peer_id.to_base58(),
revoked_at_sec,
revoke_bytes,
);
assert!(issue_result.success, "{}", issue_result.error);
let revoke_result = trust_graph.revoke_cp(
issue_result.revoke.clone(),
issue_result.revocation.clone(),
revoked_at_sec,
get_correct_timestamp_cp(1),
);
assert!(revoke_result.success, "{}", revoke_result.error);
issue_result.revoke
issue_result.revocation
}
fn generate_trust_chain_with(
@ -326,7 +320,7 @@ mod service_tests {
};
let some_peer_id = KeyPair::generate_ed25519().get_peer_id();
let result = trust_graph.add_root_cp(some_peer_id.to_base58(), 0, cp);
let result = trust_graph.set_root_cp(some_peer_id.to_base58(), 0, cp);
assert!(!result.success);
assert_eq!(result.error, ServiceError::NotOwner.to_string());
}
@ -344,7 +338,7 @@ mod service_tests {
};
let some_peer_id = KeyPair::generate_ed25519().get_peer_id();
let result = trust_graph.add_root_cp(some_peer_id.to_base58(), 0, cp);
let result = trust_graph.set_root_cp(some_peer_id.to_base58(), 0, cp);
assert!(result.success, "{}", result.error);
}
@ -358,7 +352,7 @@ mod service_tests {
let expires_at_sec = 9999u64;
let issued_at_sec = 0u64;
add_root_peer_id(&mut trust_graph, root_kp.get_peer_id(), 4u32);
set_root_peer_id(&mut trust_graph, root_kp.get_peer_id(), 4u32);
let result =
trust_graph.get_trust_bytes(root_peer_id.to_base58(), expires_at_sec, issued_at_sec);
@ -409,7 +403,7 @@ mod service_tests {
&root_kp,
cur_time,
root_expired_time - 1,
4,
10,
);
let trust_kp = KeyPair::generate_ed25519();
@ -437,39 +431,98 @@ mod service_tests {
assert_eq!(certs.len(), 0);
}
/// 1. peer `A` gives trusts to `B`
/// 2. weight of `B` is not 0
/// 3. peer `A` revokes `B`
/// 4. there is no path from `A` to `B`, weight of `A` is 0
#[test]
fn revoke_test() {
fn trust_direct_revoke_test() {
let mut trust_graph = marine_test_env::trust_graph::ServiceInterface::new();
clear_env();
let root_kp = KeyPair::generate_ed25519();
let peer_a_kp = KeyPair::generate_ed25519();
let mut cur_time = 100u64;
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, cur_time + 9999, 4u32);
add_root_with_trust(&mut trust_graph, &peer_a_kp, cur_time, cur_time + 9999, 10);
let trust_kp = KeyPair::generate_ed25519();
let peer_b_kp = KeyPair::generate_ed25519();
add_trust(
&mut trust_graph,
&root_kp,
&trust_kp.get_peer_id(),
&peer_a_kp,
&peer_b_kp.get_peer_id(),
cur_time,
cur_time + 99999,
);
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
let weight = get_weight(&mut trust_graph, peer_b_kp.get_peer_id(), cur_time);
assert_ne!(weight, 0u32);
cur_time += 1;
// A revokes B and cancels trust
revoke(
&mut trust_graph,
&root_kp,
&trust_kp.get_peer_id(),
&peer_a_kp,
&peer_b_kp.get_peer_id(),
cur_time,
);
let weight = get_weight(&mut trust_graph, trust_kp.get_peer_id(), cur_time);
let weight = get_weight(&mut trust_graph, peer_b_kp.get_peer_id(), cur_time);
assert_eq!(weight, 0u32);
}
/// There is chain of trusts [0] -> [1] -> [2] -> [3] -> [4]
/// 1. [1] revokes [4]
/// 2. there is no path from [0] to [4], weight of [4] is 0
/// 3. [0] gives trust to [2]
/// 4. now there is path [0] -> [2] -> [3] -> [4]
/// 5. weight of [4] is not 0
#[test]
fn indirect_revoke_test() {
let mut trust_graph = marine_test_env::trust_graph::ServiceInterface::new();
clear_env();
let (key_pairs, trusts) =
generate_trust_chain_with_len(&mut trust_graph, 5, HashMap::new());
let mut cur_time = current_time();
let root_peer_id = key_pairs[0].get_peer_id();
set_root_peer_id(&mut trust_graph, root_peer_id, 10);
add_trusts(&mut trust_graph, &trusts, cur_time);
let target_peer_id = key_pairs[4].get_peer_id();
let revoked_by = &key_pairs[1];
let weight = get_weight(&mut trust_graph, target_peer_id, cur_time);
assert_ne!(weight, 0u32);
cur_time += 1;
// [1] revokes [4]
revoke(&mut trust_graph, &revoked_by, &target_peer_id, cur_time);
// now there are no path from root to [4]
let weight = get_weight(&mut trust_graph, target_peer_id, cur_time);
assert_eq!(weight, 0u32);
// [0] trusts [2]
add_trust(
&mut trust_graph,
&key_pairs[0],
&key_pairs[2].get_peer_id(),
cur_time,
cur_time + 99999,
);
// [2] trusts [4]
add_trust(
&mut trust_graph,
&key_pairs[2],
&target_peer_id,
cur_time,
cur_time + 99999,
);
// now we have [0] -> [2] -> [4] path
let weight = get_weight(&mut trust_graph, target_peer_id, cur_time);
assert_ne!(weight, 0u32);
}
#[test]
fn test_add_one_trust_to_cert_last() {
let mut trust_graph = ServiceInterface::new();
@ -478,7 +531,7 @@ mod service_tests {
let cur_time = current_time();
let root_peer_id = key_pairs[0].get_peer_id();
add_root_peer_id(&mut trust_graph, root_peer_id, 2);
set_root_peer_id(&mut trust_graph, root_peer_id, 10);
add_trusts(&mut trust_graph, &trusts, cur_time);
let issued_by = key_pairs.last().unwrap().get_peer_id();
@ -519,7 +572,7 @@ mod service_tests {
let cur_time = current_time();
let root1_peer_id = key_pairs[0].get_peer_id();
add_root_peer_id(&mut trust_graph, root1_peer_id, 2);
set_root_peer_id(&mut trust_graph, root1_peer_id, 10);
add_trusts(&mut trust_graph, &trusts, cur_time);
let issued_by = key_pairs.last().unwrap().get_peer_id();
@ -562,7 +615,7 @@ mod service_tests {
let cur_time = current_time();
let root_peer_id = key_pairs[0].get_peer_id();
add_root_peer_id(&mut trust_graph, root_peer_id, 1);
set_root_peer_id(&mut trust_graph, root_peer_id, 10);
for auth in trusts.iter() {
add_trust_checked(&mut trust_graph, auth.trust.clone(), auth.issuer, cur_time);
@ -591,9 +644,9 @@ mod service_tests {
let far_future = cur_time + 9999;
// add first and last trusts as roots
add_root_peer_id(&mut trust_graph, kps[0].get_peer_id(), 0);
set_root_peer_id(&mut trust_graph, kps[0].get_peer_id(), 10);
add_trusts(&mut trust_graph, &trusts, cur_time);
add_root_with_trust(&mut trust_graph, &kps[5], cur_time, far_future, 0);
add_root_with_trust(&mut trust_graph, &kps[5], cur_time, far_future, 10);
let certs = get_all_certs(&mut trust_graph, kps[5].get_peer_id(), cur_time);
// first with self-signed last trust, second - without
@ -609,7 +662,7 @@ mod service_tests {
let root_kp = KeyPair::generate_ed25519();
let cur_time = 100u64;
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, cur_time + 999, 4u32);
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, cur_time + 999, 10);
let trust_kp = KeyPair::generate_ed25519();
add_trust(
@ -654,7 +707,7 @@ mod service_tests {
let root_kp = KeyPair::generate_ed25519();
let mut cur_time = 100u64;
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, cur_time + 999, 4u32);
add_root_with_trust(&mut trust_graph, &root_kp, cur_time, cur_time + 999, 10);
let trust_kp = KeyPair::generate_ed25519();
let expires_at_sec = cur_time + 10;
@ -694,10 +747,10 @@ mod service_tests {
let root2_kp = KeyPair::generate_ed25519();
let cur_time = 100;
let far_future = cur_time + 99999;
// root with bigger weight (smaller weight factor)
add_root_with_trust(&mut trust_graph, &root1_kp, cur_time, far_future, 0u32);
// root with bigger weight (bigger max_chain_len)
add_root_with_trust(&mut trust_graph, &root1_kp, cur_time, far_future, 10);
// opposite
add_root_with_trust(&mut trust_graph, &root2_kp, cur_time, far_future, 5u32);
add_root_with_trust(&mut trust_graph, &root2_kp, cur_time, far_future, 5);
// issue trust from root2 to any other peer_id
let issued_by_root2_peer_id = KeyPair::generate_ed25519().get_peer_id();
@ -774,13 +827,13 @@ mod service_tests {
let cur_time = current_time();
let root_peer_id = key_pairs[0].get_peer_id();
add_root_peer_id(&mut trust_graph, root_peer_id, 1);
set_root_peer_id(&mut trust_graph, root_peer_id, 10);
for auth in trusts.iter() {
add_trust_checked(&mut trust_graph, auth.trust.clone(), auth.issuer, cur_time);
}
let mut cp = get_correct_timestamp_cp_with_host_id(
let cp = get_correct_timestamp_cp_with_host_id(
0,
key_pairs.last().unwrap().get_peer_id().to_base58(),
);
@ -799,18 +852,18 @@ mod service_tests {
fn test_get_one_host_cert_from() {
let mut trust_graph = ServiceInterface::new();
clear_env();
let (key_pairs, mut trusts) =
let (key_pairs, trusts) =
generate_trust_chain_with_len(&mut trust_graph, 5, HashMap::new());
let cur_time = current_time();
let root_peer_id = key_pairs[0].get_peer_id();
add_root_peer_id(&mut trust_graph, root_peer_id, 1);
set_root_peer_id(&mut trust_graph, root_peer_id, 10);
for auth in trusts.iter() {
add_trust_checked(&mut trust_graph, auth.trust.clone(), auth.issuer, cur_time);
}
let mut cp = get_correct_timestamp_cp_with_host_id(
let cp = get_correct_timestamp_cp_with_host_id(
1,
key_pairs.last().unwrap().get_peer_id().to_base58(),
);
@ -829,4 +882,40 @@ mod service_tests {
assert_eq!(*trust, trusts[i].trust);
}
}
#[test]
fn test_get_all_cert_from() {
let mut trust_graph = ServiceInterface::new();
clear_env();
let (key_pairs, trusts) =
generate_trust_chain_with_len(&mut trust_graph, 5, HashMap::new());
let cur_time = current_time();
let root_peer_id = key_pairs[0].get_peer_id();
set_root_peer_id(&mut trust_graph, root_peer_id, 10);
for auth in trusts.iter() {
add_trust_checked(&mut trust_graph, auth.trust.clone(), auth.issuer, cur_time);
}
let cp = get_correct_timestamp_cp_with_host_id(
2,
key_pairs.last().unwrap().get_peer_id().to_base58(),
);
let certs = trust_graph.get_all_certs_from_cp(
key_pairs[4].get_peer_id().to_base58(),
key_pairs[3].get_peer_id().to_base58(),
cur_time,
cp,
);
assert!(certs.success, "{}", certs.error);
let certs = certs.certificates;
assert_eq!(certs.len(), 1);
assert_eq!(certs[0].chain.len(), 5);
for (i, trust) in certs[0].chain.iter().enumerate() {
assert_eq!(*trust, trusts[i].trust);
}
}
}

View File

@ -1,206 +0,0 @@
/*
* Copyright 2020 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::public_key_hashable::PublicKeyHashable;
use crate::revoke::Revoke;
use crate::trust::Trust;
use failure::_core::time::Duration;
use fluence_keypair::public_key::PublicKey;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
enum TrustRelation {
Auth(Auth),
Revoke(Revoke),
}
impl TrustRelation {
/// Returns timestamp of when this relation was created
pub fn issued_at(&self) -> Duration {
match self {
TrustRelation::Auth(auth) => auth.trust.issued_at,
TrustRelation::Revoke(revoke) => revoke.revoked_at,
}
}
/// Returns public key of the creator of this relation
pub fn issued_by(&self) -> &PublicKey {
match self {
TrustRelation::Auth(auth) => &auth.issued_by,
TrustRelation::Revoke(revoke) => &revoke.revoked_by,
}
}
}
/// Represents who give a certificate
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Auth {
/// proof of this authorization
pub trust: Trust,
/// the issuer of this authorization
pub issued_by: PublicKey,
}
/// An element of trust graph that store relations (trust or revoke)
/// that given by some owners of public keys.
#[serde_as]
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TrustNode {
/// identity key of this element
pub pk: PublicKey,
/// one public key could be authorized or revoked by multiple certificates
#[serde_as(as = "Vec<(_, _)>")]
trust_relations: HashMap<PublicKeyHashable, TrustRelation>,
/// for maintain
pub verified_at: Duration,
}
#[allow(dead_code)]
impl TrustNode {
pub fn new(pk: PublicKey, verified_at: Duration) -> Self {
Self {
pk,
trust_relations: HashMap::new(),
verified_at,
}
}
pub fn get_auth(&self, pk: PublicKey) -> Option<Auth> {
match self.trust_relations.get(&pk.into()) {
Some(TrustRelation::Auth(auth)) => Some(auth.clone()),
_ => None,
}
}
pub fn get_revoke(&self, pk: PublicKey) -> Option<Revoke> {
match self.trust_relations.get(&pk.into()) {
Some(TrustRelation::Revoke(rev)) => Some(rev.clone()),
_ => None,
}
}
pub fn authorizations(&self) -> impl Iterator<Item = &Auth> + '_ {
self.trust_relations.values().filter_map(|tr| {
if let TrustRelation::Auth(auth) = tr {
Some(auth)
} else {
None
}
})
}
pub fn revocations(&self) -> impl Iterator<Item = &Revoke> + '_ {
self.trust_relations.values().filter_map(|tr| {
if let TrustRelation::Revoke(revoke) = tr {
Some(revoke)
} else {
None
}
})
}
/// Adds authorization. If the trust node already has this authorization,
/// add auth with later expiration date.
pub fn update_auth(&mut self, auth: Auth) {
self.update_relation(TrustRelation::Auth(auth));
}
// insert new trust relation, ignore if there is another one with same public key
fn insert(&mut self, pk: PublicKeyHashable, tr: TrustRelation) {
self.trust_relations.insert(pk, tr);
}
fn update_relation(&mut self, relation: TrustRelation) {
let issued_by = relation.issued_by().as_ref();
match self.trust_relations.get(issued_by) {
Some(TrustRelation::Auth(auth)) => {
if auth.trust.issued_at < relation.issued_at() {
self.insert(issued_by.clone(), relation)
}
}
Some(TrustRelation::Revoke(existed_revoke)) => {
if existed_revoke.revoked_at < relation.issued_at() {
self.insert(issued_by.clone(), relation)
}
}
None => self.insert(issued_by.clone(), relation),
};
}
pub fn update_revoke(&mut self, revoke: Revoke) {
self.update_relation(TrustRelation::Revoke(revoke));
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use fluence_keypair::key_pair::KeyPair;
use super::*;
#[test]
fn test_auth_and_revoke_trust_node() {
let kp = KeyPair::generate_ed25519();
let now = Duration::new(50, 0);
let past = Duration::new(5, 0);
let future = Duration::new(500, 0);
let mut trust_node = TrustNode {
pk: kp.public(),
trust_relations: HashMap::new(),
verified_at: now,
};
let truster = KeyPair::generate_ed25519();
let revoke = Revoke::create(&truster, kp.public(), now);
trust_node.update_revoke(revoke);
assert!(trust_node.get_revoke(truster.public()).is_some());
let old_trust = Trust::create(&truster, kp.public(), Duration::new(60, 0), past);
let old_auth = Auth {
trust: old_trust,
issued_by: truster.public(),
};
trust_node.update_auth(old_auth);
assert!(trust_node.get_revoke(truster.public()).is_some());
assert!(trust_node.get_auth(truster.public()).is_none());
let trust = Trust::create(&truster, kp.public(), Duration::new(60, 0), future);
let auth = Auth {
trust,
issued_by: truster.public(),
};
trust_node.update_auth(auth);
assert!(trust_node.get_auth(truster.public()).is_some());
assert!(trust_node.get_revoke(truster.public()).is_none());
}
}

91
trust-graph/CHANGELOG.md Normal file
View File

@ -0,0 +1,91 @@
# Changelog
* The following workspace dependencies were updated
* dependencies
* fluence-keypair bumped from 0.10.0 to 0.10.1
## [0.4.7](https://github.com/fluencelabs/trust-graph/compare/trust-graph-v0.4.6...trust-graph-v0.4.7) (2023-07-04)
### Miscellaneous Chores
* **trust-graph:** Synchronize trust-graph, wasm and api versions
### Dependencies
* The following workspace dependencies were updated
* dependencies
* fluence-keypair bumped from 0.10.2 to 0.10.3
## [0.4.6](https://github.com/fluencelabs/trust-graph/compare/trust-graph-v0.4.5...trust-graph-v0.4.6) (2023-06-30)
### Bug Fixes
* **deps:** update rust crate derivative to 2.2.0 ([2001f90](https://github.com/fluencelabs/trust-graph/commit/2001f900fa13a949decd513d8cbe15e3f006a7fc))
* **deps:** update rust crate derivative to 2.2.0 ([#88](https://github.com/fluencelabs/trust-graph/issues/88)) ([2001f90](https://github.com/fluencelabs/trust-graph/commit/2001f900fa13a949decd513d8cbe15e3f006a7fc))
### Reverts
* release master ([#110](https://github.com/fluencelabs/trust-graph/issues/110)) ([d80a43b](https://github.com/fluencelabs/trust-graph/commit/d80a43bcff721aff8fadf3d2d5c252804ce27a6c))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* fluence-keypair bumped from 0.10.1 to 0.10.2
## [0.4.5](https://github.com/fluencelabs/trust-graph/compare/trust-graph-v0.4.4...trust-graph-v0.4.5) (2023-05-09)
### Features
* dummy to trigger release ([#101](https://github.com/fluencelabs/trust-graph/issues/101)) ([1a26a68](https://github.com/fluencelabs/trust-graph/commit/1a26a6809ea9a90ca8ff3829a76257779a8767d5))
## [0.4.4](https://github.com/fluencelabs/trust-graph/compare/trust-graph-v0.4.3...trust-graph-v0.4.4) (2023-05-09)
### Features
* fix trust-graph package ([#98](https://github.com/fluencelabs/trust-graph/issues/98)) ([a8fdb44](https://github.com/fluencelabs/trust-graph/commit/a8fdb4472ef1676724e4bfab1b4419f07faae2d9))
## [0.4.3](https://github.com/fluencelabs/trust-graph/compare/trust-graph-v0.4.2...trust-graph-v0.4.3) (2023-05-08)
### Features
* dummy change for release ([#95](https://github.com/fluencelabs/trust-graph/issues/95)) ([b6df3fe](https://github.com/fluencelabs/trust-graph/commit/b6df3fe5484b0adcad0c88abe170317a837142b3))
## [0.4.1](https://github.com/fluencelabs/trust-graph/compare/trust-graph-v0.4.0...trust-graph-v0.4.1) (2023-04-13)
### Miscellaneous Chores
* **trust-graph:** Synchronize trust-graph, wasm and api versions
## [0.4.0](https://github.com/fluencelabs/trust-graph/compare/trust-graph-v0.3.2...trust-graph-v0.4.0) (2023-03-15)
### ⚠ BREAKING CHANGES
* **deps:** update libp2p to 0.39.1 and other deps ([#77](https://github.com/fluencelabs/trust-graph/issues/77))
### Features
* **deps:** update libp2p to 0.39.1 and other deps ([#77](https://github.com/fluencelabs/trust-graph/issues/77)) ([080503d](https://github.com/fluencelabs/trust-graph/commit/080503dcfa2ecf8d09167ff9fe7f750fadf49035))
* **keypair:** add KeyPair::from_secret_key ([#50](https://github.com/fluencelabs/trust-graph/issues/50)) ([a6ce8d9](https://github.com/fluencelabs/trust-graph/commit/a6ce8d9eee20e1ea24eb27c38ac6df6d878292ae))
### Bug Fixes
* **deps:** Update libp2p-core to 0.38 ([#51](https://github.com/fluencelabs/trust-graph/issues/51)) ([015422e](https://github.com/fluencelabs/trust-graph/commit/015422efcce41530a6cd84a25091598bc459d2e6))
### Dependencies
* The following workspace dependencies were updated
* dependencies
* fluence-keypair bumped from 0.9.0 to 0.10.0

22
trust-graph/Cargo.toml Normal file
View File

@ -0,0 +1,22 @@
[package]
name = "trust-graph"
version = "0.10.4"
authors = ["Fluence Labs"]
edition = "2021"
description = "trust graph"
license = "Apache-2.0"
repository = "https://github.com/fluencelabs/trust-graph"
[dependencies]
serde = { version = "1.0.118", features = ["derive"] }
fluence-keypair = { path = "../keypair", version = "0.10.4-alpha.0" }
bs58 = "0.4.0"
failure = "0.1.6"
log = "0.4.11"
ref-cast = "1.0.2"
derivative = "2.2.0"
thiserror = "1.0.23"
sha2 = "0.10.6"
nonempty = "0.8.1"
rand = "0.8.5"

View File

@ -74,14 +74,26 @@ impl Certificate {
Self { chain }
}
pub fn new_from_root_trust(root_trust: Trust, issued_trust: Trust, cur_time: Duration) -> Result<Self, CertificateError> {
pub fn new_from_root_trust(
root_trust: Trust,
issued_trust: Trust,
cur_time: Duration,
) -> Result<Self, CertificateError> {
Trust::verify(&root_trust, &root_trust.issued_for, cur_time).map_err(MalformedRoot)?;
Trust::verify(&issued_trust, &root_trust.issued_for, cur_time).map_err(|e| VerificationError(1, e))?;
Trust::verify(&issued_trust, &root_trust.issued_for, cur_time)
.map_err(|e| VerificationError(1, e))?;
Ok(Self { chain: vec![root_trust, issued_trust] })
Ok(Self {
chain: vec![root_trust, issued_trust],
})
}
pub fn issue_with_trust(issued_by: PublicKey, trust: Trust, extend_cert: &Certificate, cur_time: Duration) -> Result<Self, CertificateError> {
pub fn issue_with_trust(
issued_by: PublicKey,
trust: Trust,
extend_cert: &Certificate,
cur_time: Duration,
) -> Result<Self, CertificateError> {
if trust.expires_at.lt(&trust.issued_at) {
return Err(ExpirationError {
expires_at: format!("{:?}", trust.expires_at),
@ -89,7 +101,11 @@ impl Certificate {
});
}
Certificate::verify(extend_cert, &[extend_cert.chain[0].issued_for.clone()], cur_time)?;
Certificate::verify(
extend_cert,
&[extend_cert.chain[0].issued_for.clone()],
cur_time,
)?;
// check if `issued_by` is allowed to issue a certificate (i.e., theres a trust for it in a chain)
let mut previous_trust_num: i32 = -1;
for pk_id in 0..extend_cert.chain.len() {
@ -114,7 +130,6 @@ impl Certificate {
Ok(Self { chain: new_chain })
}
/// Creates new certificate with root trust (self-signed public key) from a key pair.
#[allow(dead_code)]
pub fn issue_root(
@ -145,13 +160,17 @@ impl Certificate {
) -> Result<Self, CertificateError> {
if expires_at.lt(&issued_at) {
return Err(ExpirationError {
expires_at: format!("{:?}", expires_at),
issued_at: format!("{:?}", issued_at),
expires_at: format!("{expires_at:?}"),
issued_at: format!("{issued_at:?}"),
});
}
// first, verify given certificate
Certificate::verify(extend_cert, &[extend_cert.chain[0].issued_for.clone()], cur_time)?;
Certificate::verify(
extend_cert,
&[extend_cert.chain[0].issued_for.clone()],
cur_time,
)?;
let issued_by_pk = issued_by.public();
@ -309,7 +328,7 @@ impl FromStr for Certificate {
str_lines[i + 2],
str_lines[i + 3],
)
.map_err(|e| DecodeTrustError(i, e))?;
.map_err(|e| DecodeTrustError(i, e))?;
trusts.push(trust);
}
@ -353,7 +372,7 @@ mod tests {
cur_time,
cur_time,
)
.unwrap();
.unwrap();
let serialized = new_cert.to_string();
let deserialized = Certificate::from_str(&serialized);
@ -380,7 +399,7 @@ mod tests {
cur_time,
cur_time,
)
.unwrap();
.unwrap();
let serialized = new_cert.encode();
let deserialized = Certificate::decode(serialized.as_slice());
@ -468,7 +487,7 @@ mod tests {
cur_time.checked_sub(one_minute()).unwrap(),
cur_time,
)
.unwrap();
.unwrap();
assert!(Certificate::verify(&new_cert, &trusted_roots, cur_time).is_err());
}
@ -490,7 +509,7 @@ mod tests {
cur_time,
cur_time,
)
.unwrap();
.unwrap();
let new_cert = Certificate::issue(
&third_kp,
fourth_kp.public(),
@ -528,7 +547,7 @@ mod tests {
cur_time,
cur_time,
)
.unwrap();
.unwrap();
let new_cert = Certificate::issue(
&second_kp,
fourth_kp.public(),

View File

@ -35,7 +35,7 @@ mod single {
{
let str = String::deserialize(deserializer)?;
Certificate::from_str(&str)
.map_err(|e| Error::custom(format!("certificate deserialization failed for {:?}", e)))
.map_err(|e| Error::custom(format!("certificate deserialization failed for {e:?}")))
}
}
@ -63,7 +63,7 @@ pub mod vec {
v.into_iter()
.map(|e| {
Certificate::from_str(&e).map_err(|e| {
Error::custom(format!("certificate deserialization failed for {:?}", e))
Error::custom(format!("certificate deserialization failed for {e:?}"))
})
})
.collect()

47
trust-graph/src/chain.rs Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright 2021 Fluence Labs Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use crate::{Auth, PublicKeyHashable, Revocation};
use fluence_keypair::PublicKey;
use nonempty::NonEmpty;
use std::collections::HashSet;
#[derive(Clone)]
pub(crate) struct Chain {
pub(crate) auths: NonEmpty<Auth>,
revoked_by: HashSet<PublicKeyHashable>,
}
impl Chain {
pub(crate) fn new(auths: NonEmpty<Auth>, revocations: Vec<Revocation>) -> Self {
let mut chain = Self {
auths,
revoked_by: Default::default(),
};
chain.add_revocations(revocations);
chain
}
pub(crate) fn can_be_extended_by(&self, pk: &PublicKey) -> bool {
!self.revoked_by.contains(pk.as_ref())
&& !self.auths.iter().any(|a| a.trust.issued_for.eq(pk))
}
pub(crate) fn add_revocations(&mut self, revocations: Vec<Revocation>) {
revocations.into_iter().for_each(move |r| {
self.revoked_by.insert(r.revoked_by.into());
});
}
}

View File

@ -29,6 +29,7 @@
mod certificate;
pub mod certificate_serde;
mod chain;
mod misc;
mod public_key_hashable;
mod revoke;
@ -40,7 +41,7 @@ mod trust_relation;
pub use crate::certificate::{Certificate, CertificateError};
pub use crate::misc::current_time;
pub use crate::public_key_hashable::PublicKeyHashable;
pub use crate::revoke::Revoke;
pub use crate::revoke::Revocation;
pub use crate::trust::{Trust, TrustError};
pub use crate::trust_graph::{TrustGraph, TrustGraphError, WeightFactor, MAX_WEIGHT_FACTOR};
pub use crate::trust_graph_storage::{Storage, StorageError};

View File

@ -5,6 +5,6 @@ pub fn current_time() -> Duration {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs() as u64,
.as_secs(),
)
}

View File

@ -120,10 +120,10 @@ impl<'de> serde::Deserialize<'de> for PublicKeyHashable {
{
bs58::decode(s)
.into_vec()
.map_err(|err| Error::custom(format!("Invalid string '{}': {}", s, err)))
.map_err(|err| Error::custom(format!("Invalid string '{s}': {err}")))
.and_then(|v| self.visit_bytes(v.as_slice()))
.map_err(|err: E| {
Error::custom(format!("Parsed string '{}' as base58, but {}", s, err))
Error::custom(format!("Parsed string '{s}' as base58, but {err}"))
})
}
@ -132,7 +132,7 @@ impl<'de> serde::Deserialize<'de> for PublicKeyHashable {
E: Error,
{
let pk = PublicKey::decode(b)
.map_err(|err| Error::custom(format!("Invalid bytes {:?}: {}", b, err)))?;
.map_err(|err| Error::custom(format!("Invalid bytes {b:?}: {err}")))?;
Ok(PublicKeyHashable::from(pk))
}
}

View File

@ -36,7 +36,7 @@ pub enum RevokeError {
/// "A document" that cancels trust created before.
/// TODO delete pk from Revoke (it is already in a trust node)
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Revoke {
pub struct Revocation {
/// who is revoked
pub pk: PublicKey,
/// date when revocation was created
@ -47,11 +47,10 @@ pub struct Revoke {
pub signature: Signature,
}
impl Revoke {
#[allow(dead_code)]
impl Revocation {
pub fn new(
pk: PublicKey,
revoked_by: PublicKey,
pk: PublicKey,
revoked_at: Duration,
signature: Signature,
) -> Self {
@ -64,12 +63,11 @@ impl Revoke {
}
/// Creates new revocation signed by a revoker.
#[allow(dead_code)]
pub fn create(revoker: &KeyPair, to_revoke: PublicKey, revoked_at: Duration) -> Self {
let msg = Revoke::signature_bytes(&to_revoke, revoked_at);
let msg = Revocation::signature_bytes(&to_revoke, revoked_at);
let signature = revoker.sign(&msg).unwrap();
Revoke::new(to_revoke, revoker.public(), revoked_at, signature)
Revocation::new(revoker.public(), to_revoke, revoked_at, signature)
}
pub fn signature_bytes(pk: &PublicKey, revoked_at: Duration) -> Vec<u8> {
@ -77,14 +75,14 @@ impl Revoke {
let pk_bytes = &pk.encode();
metadata.push(pk_bytes.len() as u8);
metadata.extend(pk_bytes);
metadata.extend_from_slice(&(revoked_at.as_secs() as u64).to_le_bytes());
metadata.extend_from_slice(&revoked_at.as_secs().to_le_bytes());
sha2::Sha256::digest(&metadata).to_vec()
}
/// Verifies that revocation is cryptographically correct.
pub fn verify(revoke: &Revoke) -> Result<(), RevokeError> {
let msg = Revoke::signature_bytes(&revoke.pk, revoke.revoked_at);
pub fn verify(revoke: &Revocation) -> Result<(), RevokeError> {
let msg = Revocation::signature_bytes(&revoke.pk, revoke.revoked_at);
revoke
.revoked_by
@ -104,9 +102,9 @@ mod tests {
let duration = Duration::new(100, 0);
let revoke = Revoke::create(&revoker, to_revoke.public(), duration);
let revoke = Revocation::create(&revoker, to_revoke.public(), duration);
assert_eq!(Revoke::verify(&revoke).is_ok(), true);
assert_eq!(Revocation::verify(&revoke).is_ok(), true);
}
#[test]
@ -116,16 +114,16 @@ mod tests {
let duration = Duration::new(100, 0);
let revoke = Revoke::create(&revoker, to_revoke.public(), duration);
let revoke = Revocation::create(&revoker, to_revoke.public(), duration);
let duration2 = Duration::new(95, 0);
let corrupted_revoke = Revoke::new(
to_revoke.public(),
let corrupted_revoke = Revocation::new(
revoker.public(),
to_revoke.public(),
duration2,
revoke.signature,
);
assert_eq!(Revoke::verify(&corrupted_revoke).is_ok(), false);
assert_eq!(Revocation::verify(&corrupted_revoke).is_ok(), false);
}
}

View File

@ -144,8 +144,8 @@ impl Trust {
pub fn signature_bytes(pk: &PublicKey, expires_at: Duration, issued_at: Duration) -> Vec<u8> {
let pk_encoded = pk.encode();
let expires_at_encoded: [u8; EXPIRATION_LEN] = (expires_at.as_secs() as u64).to_le_bytes();
let issued_at_encoded: [u8; ISSUED_LEN] = (issued_at.as_secs() as u64).to_le_bytes();
let expires_at_encoded: [u8; EXPIRATION_LEN] = expires_at.as_secs().to_le_bytes();
let issued_at_encoded: [u8; ISSUED_LEN] = issued_at.as_secs().to_le_bytes();
let mut metadata = Vec::new();
metadata.extend(pk_encoded);
@ -165,8 +165,8 @@ impl Trust {
vec.append(&mut issued_for);
vec.push(signature.len() as u8);
vec.append(&mut signature);
vec.extend_from_slice(&(self.expires_at.as_secs() as u64).to_le_bytes());
vec.extend_from_slice(&(self.issued_at.as_secs() as u64).to_le_bytes());
vec.extend_from_slice(&self.expires_at.as_secs().to_le_bytes());
vec.extend_from_slice(&self.issued_at.as_secs().to_le_bytes());
vec
}
@ -260,13 +260,10 @@ impl ToString for Trust {
fn to_string(&self) -> String {
let issued_for = bs58::encode(self.issued_for.encode()).into_string();
let signature = bs58::encode(self.signature.encode()).into_string();
let expires_at = (self.expires_at.as_secs() as u64).to_string();
let issued_at = (self.issued_at.as_secs() as u64).to_string();
let expires_at = self.expires_at.as_secs().to_string();
let issued_at = self.issued_at.as_secs().to_string();
format!(
"{}\n{}\n{}\n{}",
issued_for, signature, expires_at, issued_at
)
format!("{issued_for}\n{signature}\n{expires_at}\n{issued_at}")
}
}

View File

@ -16,8 +16,9 @@
use crate::certificate::CertificateError::CertificateLengthError;
use crate::certificate::{Certificate, CertificateError};
use crate::chain::Chain;
use crate::public_key_hashable::PublicKeyHashable as PK;
use crate::revoke::Revoke;
use crate::revoke::Revocation;
use crate::revoke::RevokeError;
use crate::trust::Trust;
use crate::trust_graph::TrustGraphError::{
@ -27,6 +28,7 @@ use crate::trust_graph_storage::Storage;
use crate::trust_relation::Auth;
use crate::{StorageError, TrustError};
use fluence_keypair::public_key::PublicKey;
use nonempty::NonEmpty;
use std::borrow::Borrow;
use std::collections::{HashSet, VecDeque};
use std::convert::{From, Into};
@ -88,12 +90,16 @@ impl<T: StorageError + 'static> From<T> for TrustGraphError {
impl From<TrustGraphError> for String {
fn from(err: TrustGraphError) -> Self {
format!("{}", err)
format!("{err}")
}
}
fn get_weight_factor(max_chain_len: u32) -> u32 {
MAX_WEIGHT_FACTOR.saturating_sub(max_chain_len)
}
pub fn get_weight_from_factor(wf: WeightFactor) -> u32 {
2u32.pow(MAX_WEIGHT_FACTOR.checked_sub(wf).unwrap_or(0u32))
2u32.pow(MAX_WEIGHT_FACTOR.saturating_sub(wf))
}
impl<S> TrustGraph<S>
@ -105,12 +111,10 @@ where
}
/// Insert new root weight
pub fn add_root_weight_factor(
&mut self,
pk: PublicKey,
weight: WeightFactor,
) -> Result<(), TrustGraphError> {
Ok(self.storage.add_root_weight_factor(pk.into(), weight)?)
pub fn set_root(&mut self, pk: PublicKey, max_chain_len: u32) -> Result<(), TrustGraphError> {
Ok(self
.storage
.set_root_weight_factor(pk.into(), get_weight_factor(max_chain_len))?)
}
pub fn add_trust<T, P>(
@ -196,6 +200,29 @@ where
Ok(max_weight)
}
/// Get the maximum weight of trust for one public key.
/// for all chains which contain `issuer`
pub fn weight_from<P>(
&mut self,
issued_for: P,
issuer: P,
cur_time: Duration,
) -> Result<u32, TrustGraphError>
where
P: Borrow<PublicKey>,
{
let mut max_weight = 0;
// get all possible certificates from the given public key to all roots in the graph
// which contain `issuer`
let certs = self.get_all_certs_from(issued_for, issuer, cur_time)?;
if let Some(weight_factor) = self.certificates_weight_factor(certs)? {
max_weight = std::cmp::max(max_weight, get_weight_from_factor(weight_factor))
}
Ok(max_weight)
}
/// Calculate weight from given certificates
/// Returns None if there is no such public key
/// or some trust between this key and a root key is revoked.
@ -245,12 +272,14 @@ where
roots: HashSet<&PK>,
) -> Result<Vec<Vec<Auth>>, TrustGraphError> {
// queue to collect all chains in the trust graph (each chain is a path in the trust graph)
let mut chains_queue: VecDeque<Vec<Auth>> = VecDeque::new();
let mut chains_queue: VecDeque<Chain> = VecDeque::new();
let node_auths: Vec<Auth> = self.storage.get_authorizations(pk)?;
let node_revocations = self.storage.get_revocations(pk)?;
// put all auth in the queue as the first possible paths through the graph
for auth in node_auths {
chains_queue.push_back(vec![auth]);
chains_queue.push_back(Chain::new(NonEmpty::new(auth), node_revocations.clone()));
}
// List of all chains that converge (terminate) to known roots
@ -261,22 +290,21 @@ where
.pop_front()
.expect("`chains_queue` always has at least one element");
let last = cur_chain
.last()
.expect("`cur_chain` always has at least one element");
let last = cur_chain.auths.last();
let auths = self
.storage
.get_authorizations(&last.issued_by.clone().into())?;
for auth in auths {
// if there is auth, that we not visited in the current chain, copy chain and append this auth
if !cur_chain
.iter()
.any(|a| a.trust.issued_for == auth.issued_by)
{
// if there is auth, that we not visited in the current chain and no revocations to any chain member -- copy chain and append this auth
if cur_chain.can_be_extended_by(&auth.issued_by) {
let mut new_chain = cur_chain.clone();
new_chain.push(auth);
new_chain.add_revocations(
self.storage
.get_revocations(&auth.issued_by.clone().into())?,
);
new_chain.auths.push(auth);
chains_queue.push_back(new_chain);
}
}
@ -289,14 +317,35 @@ where
let issued_by: &PK = last.issued_by.as_ref();
let converges_to_root = roots.contains(issued_by);
if self_signed && converges_to_root && cur_chain.len() > 1 {
terminated_chains.push(cur_chain);
if self_signed && converges_to_root && cur_chain.auths.len() > 1 {
terminated_chains.push(cur_chain.auths.into());
}
}
Ok(terminated_chains)
}
/// Get all possible certificates where `issued_for` will be the last element of the chain,
/// all certificates contain `issuer`
/// and one of the destinations is the root of this chain.
pub fn get_all_certs_from<P>(
&mut self,
issued_for: P,
issuer: P,
cur_time: Duration,
) -> Result<Vec<Certificate>, TrustGraphError>
where
P: Borrow<PublicKey>,
{
self.get_all_certs(issued_for, cur_time).map(|c| {
c.into_iter()
.filter(|cert: &Certificate| {
cert.chain.iter().any(|t| t.issued_for.eq(issuer.borrow()))
})
.collect()
})
}
/// Get all possible certificates where `issued_for` will be the last element of the chain
/// and one of the destinations is the root of this chain.
pub fn get_all_certs<P>(
@ -325,8 +374,7 @@ where
// Certificate with one trust means nothing, gotta be a bug. Checking for it here.
debug_assert!(
c.chain.len() > 1,
"certificate with chain of len 1 arose: {:#?}",
c
"certificate with chain of len 1 arose: {c:#?}",
);
c.chain.len() > 1
})
@ -334,9 +382,18 @@ where
}
/// Mark public key as revoked.
pub fn revoke(&mut self, revoke: Revoke) -> Result<(), TrustGraphError> {
Revoke::verify(&revoke)?;
/// Every chain that contains path from `revoked_by` to revoked `pk`
/// will be excluded from valid certificates until revocation canceled by giving trust
pub fn revoke(&mut self, revocation: Revocation) -> Result<(), TrustGraphError> {
Revocation::verify(&revocation)?;
Ok(self.storage.revoke(revoke)?)
Ok(self.storage.revoke(revocation)?)
}
pub fn get_revocations<P>(&self, issued_for: P) -> Result<Vec<Revocation>, TrustGraphError>
where
P: Borrow<PublicKey>,
{
Ok(self.storage.get_revocations(issued_for.borrow().as_ref())?)
}
}

View File

@ -1,5 +1,5 @@
use crate::public_key_hashable::PublicKeyHashable as PK;
use crate::revoke::Revoke;
use crate::revoke::Revocation;
use crate::trust_graph::WeightFactor;
use crate::trust_relation::{Auth, TrustRelation};
use std::fmt::Display;
@ -16,13 +16,19 @@ pub trait Storage {
issued_by: &PK,
) -> Result<Option<TrustRelation>, Self::Error>;
fn get_authorizations(&self, pk: &PK) -> Result<Vec<Auth>, Self::Error>;
fn get_authorizations(&self, issued_for: &PK) -> Result<Vec<Auth>, Self::Error>;
fn get_revocations(&self, issued_for: &PK) -> Result<Vec<Revocation>, Self::Error>;
fn insert(&mut self, node: TrustRelation) -> Result<(), Self::Error>;
fn get_root_weight_factor(&self, pk: &PK) -> Result<Option<WeightFactor>, Self::Error>;
fn add_root_weight_factor(&mut self, pk: PK, weight: WeightFactor) -> Result<(), Self::Error>;
fn set_root_weight_factor(
&mut self,
pk: PK,
weight_factor: WeightFactor,
) -> Result<(), Self::Error>;
fn root_keys(&self) -> Result<Vec<PK>, Self::Error>;
fn revoke(&mut self, revoke: Revoke) -> Result<(), Self::Error>;
fn revoke(&mut self, revocation: Revocation) -> Result<(), Self::Error>;
fn update_auth(&mut self, auth: Auth, cur_time: Duration) -> Result<(), Self::Error>;
fn remove_expired(&mut self, current_time: Duration) -> Result<(), Self::Error>;
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
use crate::revoke::Revoke;
use crate::revoke::Revocation;
use crate::trust::Trust;
use failure::_core::time::Duration;
use fluence_keypair::public_key::PublicKey;
@ -33,7 +33,7 @@ pub struct Auth {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TrustRelation {
Auth(Auth),
Revoke(Revoke),
Revocation(Revocation),
}
impl TrustRelation {
@ -41,7 +41,7 @@ impl TrustRelation {
pub fn issued_at(&self) -> Duration {
match self {
TrustRelation::Auth(auth) => auth.trust.issued_at,
TrustRelation::Revoke(revoke) => revoke.revoked_at,
TrustRelation::Revocation(r) => r.revoked_at,
}
}
@ -49,28 +49,29 @@ impl TrustRelation {
pub fn issued_by(&self) -> &PublicKey {
match self {
TrustRelation::Auth(auth) => &auth.issued_by,
TrustRelation::Revoke(revoke) => &revoke.revoked_by,
TrustRelation::Revocation(r) => &r.revoked_by,
}
}
pub fn issued_for(&self) -> &PublicKey {
match self {
TrustRelation::Auth(auth) => &auth.trust.issued_for,
TrustRelation::Revoke(revoke) => &revoke.pk,
TrustRelation::Revocation(r) => &r.pk,
}
}
pub fn expires_at(&self) -> Duration {
match self {
TrustRelation::Auth(auth) => auth.trust.expires_at,
TrustRelation::Revoke(_) => Duration::from_secs(0),
// revocations never expire
TrustRelation::Revocation(_) => Duration::from_secs(0),
}
}
pub fn signature(&self) -> &Signature {
match self {
TrustRelation::Auth(auth) => &auth.trust.signature,
TrustRelation::Revoke(revoke) => &revoke.signature,
TrustRelation::Revocation(r) => &r.signature,
}
}
}