mirror of
https://github.com/fluencelabs/trust-graph
synced 2025-07-04 17:11:41 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
aa72fdab64 | |||
b9fbbbcafb | |||
7c72a59bef | |||
5609740216 | |||
58648d7037 | |||
048406aa02 | |||
b76954782b | |||
c2f63cb41e | |||
8214b1cc5c | |||
c4019127e8 | |||
24dbadaa7c | |||
7528256e78 | |||
1f31ffecc7 | |||
cb6e072231 |
@ -16,19 +16,19 @@ jobs:
|
||||
sudo bash .github/download_marine.sh
|
||||
- restore_cache:
|
||||
keys:
|
||||
- trust-graph00-{{ checksum "./service/Cargo.lock" }}-{{ checksum "./Cargo.lock" }}-{{ checksum "./keypair/Cargo.lock" }}
|
||||
- trust-graph00-{{ checksum "./Cargo.lock" }}
|
||||
- run: |
|
||||
rustup target add wasm32-wasi
|
||||
cargo test --no-fail-fast --release --all-features --
|
||||
cd ./service
|
||||
./build.sh
|
||||
mkdir data
|
||||
mkdir -p 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" }}
|
||||
key: trust-graph00-{{ checksum "./Cargo.lock" }}
|
||||
|
||||
|
||||
workflows:
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -54,7 +54,7 @@ jobs:
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly-2021-09-01
|
||||
toolchain: nightly-2022-01-16
|
||||
target: wasm32-wasi
|
||||
override: true
|
||||
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -16,3 +16,5 @@ example/src/generated/**
|
||||
example/generated/**
|
||||
admin/src/generated/**
|
||||
admin/generated/**
|
||||
|
||||
target
|
||||
|
695
Cargo.lock
generated
695
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -8,10 +8,10 @@ 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"] }
|
||||
libp2p-core = { version = "0.31", default-features = false, features = [ "secp256k1" ] }
|
||||
serde = { version = "1.0.118", features = ["derive"] }
|
||||
|
||||
fluence-keypair = { path = "./keypair", version = "0.5.0" }
|
||||
fluence-keypair = { path = "./keypair", version = "0.6.0" }
|
||||
serde_json = "1.0.58"
|
||||
bs58 = "0.3.1"
|
||||
failure = "0.1.6"
|
||||
@ -23,6 +23,7 @@ serde_with = "1.6.0"
|
||||
thiserror = "1.0.23"
|
||||
sha2 = "0.9.5"
|
||||
rand = "0.7.0"
|
||||
nonempty = "0.7.0"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
|
93
README.md
93
README.md
@ -1,13 +1,92 @@
|
||||
### 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 company’s peers.
|
||||
Trust Graph is network-wide peer relationship layer. It's designed to be used to prioritize resources and control permissions in open networks. Being a decentralized graph of relationships, basically a Web of Trust, Trust Graph is distributed among all network peers.
|
||||
|
||||
### Project structure
|
||||
Specifically, Trust Graph is used to prioritize connections from known peers to counteract Sybil attacks while still keeping network open by reserving resources for unknown peers.
|
||||
|
||||
`/.` is the main project with all trust graph logic and in-memory storage as a default
|
||||
At the same time, Trust Graph can be used at the application level in various ways such as prioritization of service execution on authorized peers or to define an interconnected subnetwork among peers of a single protocol.
|
||||
|
||||
`keypair` directory is an abstracted cryptographical layer (key pairs, public keys, signatures, etc.)
|
||||
## How to Use it in TypeScript
|
||||
|
||||
`service` is a package that provides `marine` API and could be compiled to a Wasm file. It is uses `SQLite` as storage.
|
||||
See [example](./example):
|
||||
- How to call [`trust-graph`](./example/index.ts) functions in TS/JS
|
||||
- Step-by-step description [`README`](./example/README.md)
|
||||
|
||||
`example` is a `js` script that shows how to issue, sign trusts/revokes, get certificates
|
||||
## API
|
||||
|
||||
High-level API is defined in the [trust-graph-api.aqua](./aqua/trust-graph-api.aqua) module.
|
||||
|
||||
## 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 is 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)
|
||||
|
||||
## How to use in Aqua
|
||||
|
||||
```
|
||||
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 use is js
|
||||
1. Add the following to your dependencies
|
||||
- `@fluencelabs/trust-graph`
|
||||
- `@fluencelabs/aqua`
|
||||
- `@fluencelabs/aqua-lib`
|
||||
- `@fluencelabs/fluence`
|
||||
- `@fluencelabs/fluence-network-environment`
|
||||
|
||||
2. Import dependencies
|
||||
```typescript
|
||||
import * as tg from "./generated/export";
|
||||
import { Fluence, KeyPair } from "@fluencelabs/fluence";
|
||||
import { krasnodar, Node } from "@fluencelabs/fluence-network-environment";
|
||||
```
|
||||
3. Create 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*/});
|
||||
```
|
||||
4. Add 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 = tg.timestamp_sec() + 9999999999;
|
||||
let error = await tg.add_root_trust(relay, peer_id, max_chain_len, far_future);
|
||||
if (error !== null) {
|
||||
console.log(error);
|
||||
}
|
||||
```
|
||||
5. For now, trusts/revocations can only be signed with the client's private key.
|
||||
Keypair specification will be available soon.
|
||||
```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);
|
||||
}
|
||||
```
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 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);
|
||||
}
|
||||
|
||||
|
5164
admin/package-lock.json
generated
5164
admin/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -13,9 +13,9 @@
|
||||
"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": "^0.5.2-257",
|
||||
"@fluencelabs/aqua-lib": "^0.3.4",
|
||||
"@fluencelabs/fluence": "^0.18.0",
|
||||
"@fluencelabs/fluence-network-environment": "^1.0.10",
|
||||
"@fluencelabs/trust-graph": "file:../aqua",
|
||||
"bs58": "^4.0.1"
|
||||
|
39
aqua/labelling.aqua
Normal file
39
aqua/labelling.aqua
Normal 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
15
aqua/misc.aqua
Normal 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!
|
3070
aqua/package-lock.json
generated
3070
aqua/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@fluencelabs/trust-graph",
|
||||
"version": "0.1.12",
|
||||
"version": "0.2.2",
|
||||
"description": "Aqua Trust Graph API library",
|
||||
"files": [
|
||||
"*.aqua"
|
||||
],
|
||||
"dependencies": {
|
||||
"@fluencelabs/aqua-lib": "0.2.0"
|
||||
"@fluencelabs/aqua-lib": "^0.3.4"
|
||||
},
|
||||
"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.5.2-257"
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
@ -1,5 +1,10 @@
|
||||
# Run example locally
|
||||
1. Go to `local-network`
|
||||
## Description
|
||||
This example shows how to use Trust Graph to label peers. There are some `trusted_computation` which can only be executed
|
||||
on labeled peer. The label is determined by the presence of certificate from `%init_peer_id` to this peer.
|
||||
|
||||
## 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`
|
||||
4. Run `npm i`
|
||||
5. Run `npm run start`
|
||||
|
27
example/aqua/computation.aqua
Normal file
27
example/aqua/computation.aqua
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
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:
|
||||
result: ?u64
|
||||
-- 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)
|
||||
|
||||
<- result
|
@ -1,10 +1,10 @@
|
||||
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"
|
||||
import add_root_trust, add_trust, revoke from "@fluencelabs/trust-graph/trust-graph-api.aqua"
|
||||
export add_root_trust, add_trust, revoke
|
||||
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
|
||||
|
145
example/index.ts
145
example/index.ts
@ -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,77 +32,109 @@ 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) {
|
||||
for (var node of local) {
|
||||
let error = await tg.revoke(relay, revoked_by, node.peerId);
|
||||
if (error === null) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
async function add_root(relay: string, peer_id: string) {
|
||||
let current_time = await tg.timestamp_sec();
|
||||
let far_future = current_time + 9999999;
|
||||
let error = await tg.add_root_trust(relay, peer_id, 2, far_future);
|
||||
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));
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
let result_add = await revoke(node, revocation.revoke);
|
||||
console.log("Revoke result: %s", result_add);
|
||||
async function exec_trusted_computation(node: string) {
|
||||
let result = 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", node)
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("In this example we try to execute some trusted computations based on trusts");
|
||||
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
|
||||
let builtins_keypair = await KeyPair.fromEd25519SK(sk);
|
||||
await Fluence.start({ connectTo: local[0], KeyPair: builtins_keypair});
|
||||
|
||||
let relay = local[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);
|
||||
|
||||
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)
|
||||
// clear all trusts from our peer id on relay
|
||||
await revoke_all(relay.peerId, local_peer_id);
|
||||
// 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));
|
||||
// set our peer id as root to our relay
|
||||
await add_root(relay.peerId, local_peer_id);
|
||||
|
||||
let empty_certs = await get_all_certs(local[0].peerId, local[1].peerId);
|
||||
assert(empty_certs.certificates.length === 0);
|
||||
let nodeA = local[0].peerId
|
||||
let nodeB = local[1].peerId
|
||||
let nodeC = local[2].peerId
|
||||
|
||||
// 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("🌀 Issue trust to nodeB: %s", nodeB);
|
||||
await add_new_trust_checked(relay.peerId, local_peer_id, nodeB, far_future);
|
||||
|
||||
await exec_trusted_computation(nodeA); // fail
|
||||
await exec_trusted_computation(nodeB); // success
|
||||
await exec_trusted_computation(nodeC); // fail
|
||||
|
||||
await new Promise(f => setTimeout(f, 1000));
|
||||
console.log("🚫 Revoke trust to nodeB");
|
||||
await revoke_checked(relay.peerId, local_peer_id, nodeB);
|
||||
|
||||
await exec_trusted_computation(nodeA); // fail
|
||||
await exec_trusted_computation(nodeB); // fail
|
||||
await exec_trusted_computation(nodeC); // fail
|
||||
return;
|
||||
}
|
||||
|
||||
|
5231
example/package-lock.json
generated
5231
example/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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": "^0.5.2-257",
|
||||
"@fluencelabs/aqua-lib": "^0.3.4",
|
||||
"@fluencelabs/fluence": "^0.18.0",
|
||||
"@fluencelabs/fluence-network-environment": "^1.0.10",
|
||||
"@fluencelabs/trust-graph": "file:../aqua",
|
||||
"bs58": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^4.4.3"
|
||||
"typescript": "^4.5.2"
|
||||
}
|
||||
}
|
||||
|
1
example_secret_key.ed25519
Normal file
1
example_secret_key.ed25519
Normal file
@ -0,0 +1 @@
|
||||
E5ay3731i4HN8XjJozouV92RDMGAn3qSnb9dKSnujiWv
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "fluence-keypair"
|
||||
version = "0.5.1"
|
||||
version = "0.6.0"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
description = "identity"
|
||||
@ -8,7 +8,7 @@ license = "Apache-2.0"
|
||||
repository = "https://github.com/fluencelabs/trust-graph"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "=1.0.118", features = ["derive"] }
|
||||
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"] }
|
||||
@ -23,7 +23,7 @@ asn1_der = "0.6.1"
|
||||
sha2 = "0.9.1"
|
||||
zeroize = "1"
|
||||
serde_bytes = "0.11"
|
||||
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"]}
|
||||
libp2p-core = { version = "0.31", default-features = false, features = [ "secp256k1" ] }
|
||||
eyre = "0.6.5"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
|
3
keypair/rust-toolchain.toml
Normal file
3
keypair/rust-toolchain.toml
Normal file
@ -0,0 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2022-01-16"
|
||||
targets = [ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ]
|
@ -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 {
|
||||
@ -164,13 +164,24 @@ impl TryFrom<libp2p_core::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(as_public_key(&peer_id)
|
||||
.ok_or(DecodingError::PublicKeyNotInlined(peer_id.to_base58()))?
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert PeerId to libp2p's PublicKey
|
||||
fn as_public_key(peer_id: &PeerId) -> Option<libp2p_core::PublicKey> {
|
||||
use libp2p_core::multihash;
|
||||
|
||||
let mhash = peer_id.as_ref();
|
||||
|
||||
match multihash::Code::try_from(mhash.code()) {
|
||||
Ok(multihash::Code::Identity) => libp2p_core::PublicKey::from_protobuf_encoding(mhash.digest()).ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2021-09-01"
|
||||
channel = "nightly-2022-01-16"
|
||||
targets = [ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ]
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "trust-graph-wasm"
|
||||
version = "0.2.1"
|
||||
version = "0.3.1"
|
||||
authors = ["Fluence Labs"]
|
||||
edition = "2018"
|
||||
description = "trust graph wasm"
|
||||
@ -12,11 +12,11 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
trust-graph = { version = "0.3.0", path = "../." }
|
||||
fluence-keypair = { version = "0.5.0", path = "../keypair" }
|
||||
fluence-keypair = { version = "0.6.0", path = "../keypair" }
|
||||
marine-rs-sdk = { version = "0.6.14", features = ["logger"] }
|
||||
marine-sqlite-connector = "0.5.2"
|
||||
|
||||
libp2p-core = { package = "fluence-fork-libp2p-core", version = "0.27.2", features = ["secp256k1"]}
|
||||
libp2p-core = { version = "0.31", default-features = false, features = [ "secp256k1" ] }
|
||||
|
||||
log = "0.4.8"
|
||||
anyhow = "1.0.31"
|
||||
|
@ -5,12 +5,12 @@ modules_dir = "artifacts/"
|
||||
logger_enabled = true
|
||||
|
||||
[module.wasi]
|
||||
preopened_files = ["./data"]
|
||||
mapped_dirs = { "data" = "data" }
|
||||
preopened_files = ["/tmp"]
|
||||
mapped_dirs = { "tmp" = "/tmp" }
|
||||
[[module]]
|
||||
name = "trust-graph"
|
||||
logger_enabled = true
|
||||
|
||||
[module.wasi]
|
||||
preopened_files = ["./data"]
|
||||
mapped_dirs = { "data" = "data" }
|
||||
preopened_files = ["/tmp"]
|
||||
mapped_dirs = { "tmp" = "/tmp" }
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2021-09-01"
|
||||
channel = "nightly-2022-01-16"
|
||||
targets = [ "x86_64-apple-darwin", "x86_64-unknown-linux-gnu" ]
|
@ -113,7 +113,7 @@ impl From<trust_graph::Trust> for Trust {
|
||||
|
||||
#[marine]
|
||||
#[derive(Default)]
|
||||
pub struct Revoke {
|
||||
pub struct Revocation {
|
||||
/// who is revoked
|
||||
pub revoked_peer_id: String,
|
||||
/// date when revocation was created
|
||||
@ -126,10 +126,10 @@ 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)))?,
|
||||
@ -142,7 +142,7 @@ impl TryFrom<Revoke> for trust_graph::Revoke {
|
||||
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 {
|
||||
return Ok(trust_graph::Revocation {
|
||||
pk: revoked_pk,
|
||||
revoked_at,
|
||||
revoked_by: revoked_by_pk,
|
||||
@ -151,14 +151,14 @@ impl TryFrom<Revoke> for trust_graph::Revoke {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return Revocation {
|
||||
revoked_peer_id,
|
||||
revoked_at,
|
||||
signature,
|
||||
|
64
service/src/error.rs
Normal file
64
service/src/error.rs
Normal 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,
|
||||
}
|
@ -7,13 +7,37 @@ 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;
|
||||
/*
|
||||
_initialize function that calls __wasm_call_ctors is required to mitigade memory leak
|
||||
that is described in https://github.com/WebAssembly/wasi-libc/issues/298
|
||||
|
||||
In short, without this code rust wraps every export function
|
||||
with __wasm_call_ctors/__wasm_call_dtors calls. This causes memory leaks. When compiler sees
|
||||
an explicit call to __wasm_call_ctors in _initialize function, it disables export wrapping.
|
||||
|
||||
TODO: remove when updating to marine-rs-sdk with fix
|
||||
*/
|
||||
extern "C" {
|
||||
pub fn __wasm_call_ctors();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn _initialize() {
|
||||
unsafe {
|
||||
__wasm_call_ctors();
|
||||
}
|
||||
}
|
||||
//------------------------------
|
||||
pub static TRUSTED_TIMESTAMP: (&str, &str) = ("peer", "timestamp_sec");
|
||||
|
||||
pub fn main() {
|
||||
_initialize(); // As __wasm_call_ctors still does necessary work, we call it at the start of the module
|
||||
WasmLoggerBuilder::new()
|
||||
.with_log_level(log::LevelFilter::Trace)
|
||||
.build()
|
||||
|
76
service/src/misc.rs
Normal file
76
service/src/misc.rs
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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_core::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")
|
||||
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_core::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)
|
||||
}
|
@ -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]
|
||||
@ -60,9 +60,9 @@ 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 {
|
||||
@ -75,19 +75,19 @@ impl From<Result<Vec<Certificate>, ServiceError>> for AllCertsResult {
|
||||
}
|
||||
|
||||
#[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),
|
||||
},
|
||||
@ -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(),
|
||||
revocation: Revocation::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -256,3 +256,27 @@ impl From<Result<(), ServiceError>> for RevokeResult {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,33 @@
|
||||
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 {
|
||||
return 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, 1)?;
|
||||
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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
@ -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(),
|
||||
@ -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))?;
|
||||
@ -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,
|
||||
@ -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> {
|
||||
|
@ -17,14 +17,13 @@
|
||||
#[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::error::ServiceError;
|
||||
use crate::storage_impl::DB_PATH;
|
||||
use crate::TRUSTED_TIMESTAMP;
|
||||
use fluence_keypair::KeyPair;
|
||||
use libp2p_core::PeerId;
|
||||
use marine_rs_sdk::{CallParameters, SecurityTetraplet};
|
||||
use marine_test_env::trust_graph::{Certificate, Revoke, ServiceInterface, Trust};
|
||||
use marine_test_env::trust_graph::{Certificate, Revocation, ServiceInterface, Trust};
|
||||
use rusqlite::Connection;
|
||||
use std::collections::HashMap;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
@ -80,16 +79,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 +97,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 +200,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 +325,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 +343,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 +357,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 +408,7 @@ mod service_tests {
|
||||
&root_kp,
|
||||
cur_time,
|
||||
root_expired_time - 1,
|
||||
4,
|
||||
10,
|
||||
);
|
||||
|
||||
let trust_kp = KeyPair::generate_ed25519();
|
||||
@ -437,39 +436,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 peerA_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, &peerA_kp, cur_time, cur_time + 9999, 10);
|
||||
|
||||
let trust_kp = KeyPair::generate_ed25519();
|
||||
let peerB_kp = KeyPair::generate_ed25519();
|
||||
add_trust(
|
||||
&mut trust_graph,
|
||||
&root_kp,
|
||||
&trust_kp.get_peer_id(),
|
||||
&peerA_kp,
|
||||
&peerB_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, peerB_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(),
|
||||
&peerA_kp,
|
||||
&peerB_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, peerB_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 +536,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 +577,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 +620,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 +649,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 +667,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 +712,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 +752,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 +832,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 +857,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(),
|
||||
);
|
||||
|
47
src/chain.rs
Normal file
47
src/chain.rs
Normal 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());
|
||||
});
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
@ -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> {
|
||||
@ -83,8 +81,8 @@ impl Revoke {
|
||||
}
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
@ -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};
|
||||
@ -92,8 +94,12 @@ impl From<TrustGraphError> for String {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_weight_factor(max_chain_len: u32) -> u32 {
|
||||
MAX_WEIGHT_FACTOR.checked_sub(max_chain_len).unwrap_or(0u32)
|
||||
}
|
||||
|
||||
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>(
|
||||
@ -334,9 +383,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())?)
|
||||
}
|
||||
}
|
||||
|
@ -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>;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user