mirror of
https://github.com/vacp2p/nim-libp2p.git
synced 2026-01-09 14:28:11 -05:00
Compare commits
298 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f6b8e86a5 | ||
|
|
11b98b7a3f | ||
|
|
647f76341e | ||
|
|
fbf96bb2ce | ||
|
|
f0aaecb743 | ||
|
|
8d3076ea99 | ||
|
|
70b7d61436 | ||
|
|
37bae0986c | ||
|
|
b34ddab10c | ||
|
|
e09457da12 | ||
|
|
94ad1dcbc8 | ||
|
|
5b9f2cba6f | ||
|
|
59e7069c15 | ||
|
|
18a0e9c2d1 | ||
|
|
34a9a03b73 | ||
|
|
788109b4f4 | ||
|
|
44aab92b3e | ||
|
|
ad0812b40b | ||
|
|
0751f240a2 | ||
|
|
d8ecf8a135 | ||
|
|
bab863859c | ||
|
|
73d04def6f | ||
|
|
4509ade75c | ||
|
|
b64c0f6d85 | ||
|
|
582ba7e650 | ||
|
|
31ae734aff | ||
|
|
f26ff88e6c | ||
|
|
4fbf59ece8 | ||
|
|
62388a7a20 | ||
|
|
27051164db | ||
|
|
f41009461b | ||
|
|
c3faabf522 | ||
|
|
10f7f5c68a | ||
|
|
f345026900 | ||
|
|
5d6578a06f | ||
|
|
871a5d047f | ||
|
|
061195195b | ||
|
|
8add5aaaab | ||
|
|
dbf60b74c7 | ||
|
|
d2eaf07960 | ||
|
|
6e5274487e | ||
|
|
7ed62461d7 | ||
|
|
6059ee8332 | ||
|
|
4f7e232a9e | ||
|
|
5eaa43b860 | ||
|
|
17ed2d88df | ||
|
|
c7f29ed5db | ||
|
|
9865cc39b5 | ||
|
|
601f56b786 | ||
|
|
25a8ed4d07 | ||
|
|
955e28ff70 | ||
|
|
f952e6d436 | ||
|
|
bed83880bf | ||
|
|
9bd4b7393f | ||
|
|
12d1fae404 | ||
|
|
17073dc9e0 | ||
|
|
b1649b3566 | ||
|
|
ef20f46b47 | ||
|
|
9161529c84 | ||
|
|
8b70384b6a | ||
|
|
f25814a890 | ||
|
|
3d5ea1fa3c | ||
|
|
2114008704 | ||
|
|
04796b210b | ||
|
|
59faa023aa | ||
|
|
fdebea4e14 | ||
|
|
0c188df806 | ||
|
|
abee5326dc | ||
|
|
71f04d1bb3 | ||
|
|
41ae43ae80 | ||
|
|
5dbf077d9e | ||
|
|
b5fc7582ff | ||
|
|
7f83ebb198 | ||
|
|
ceb89986c1 | ||
|
|
f4ff27ca6b | ||
|
|
b517b692df | ||
|
|
7cfd26035a | ||
|
|
cd5fea53e3 | ||
|
|
d9aa393761 | ||
|
|
a4a0d9e375 | ||
|
|
c8b406d6ed | ||
|
|
f0125a62df | ||
|
|
9bf2636186 | ||
|
|
01a33ebe5c | ||
|
|
c1cd31079b | ||
|
|
9f9f38e314 | ||
|
|
f83638eb82 | ||
|
|
882cb5dfe3 | ||
|
|
81310df2a2 | ||
|
|
34110a37d7 | ||
|
|
1035e4f314 | ||
|
|
d08bad5893 | ||
|
|
7bdba4909f | ||
|
|
e71c7caf82 | ||
|
|
45476bdd6b | ||
|
|
c7ee7b950d | ||
|
|
87b3d2c864 | ||
|
|
19b4c20e2f | ||
|
|
514bd4b5f5 | ||
|
|
46d936b80c | ||
|
|
80bf27c6bb | ||
|
|
6576c5c3bf | ||
|
|
2e6b1d2738 | ||
|
|
9e6c4cb4d2 | ||
|
|
5f256049ab | ||
|
|
e29ca73386 | ||
|
|
577809750a | ||
|
|
46a5430cc2 | ||
|
|
d8b9f59c5e | ||
|
|
2951356c9d | ||
|
|
7ae21d0cbd | ||
|
|
eee8341ad2 | ||
|
|
e83bd2d582 | ||
|
|
998bb58aef | ||
|
|
c1f6dec7d3 | ||
|
|
13c613c26c | ||
|
|
45f0f9f47a | ||
|
|
b1dd0a2ec6 | ||
|
|
beecfdfadb | ||
|
|
e4faec5570 | ||
|
|
41c9bf8e8c | ||
|
|
7ae366d979 | ||
|
|
9b33cea225 | ||
|
|
f8077f7432 | ||
|
|
773fc67865 | ||
|
|
7e07ffc5a8 | ||
|
|
aa1c33ffe9 | ||
|
|
f1e220fba4 | ||
|
|
5ad656bf26 | ||
|
|
cfd631457a | ||
|
|
4f8597609b | ||
|
|
4ed72a753c | ||
|
|
2a9abbe925 | ||
|
|
ee61e234ac | ||
|
|
8f54367e3a | ||
|
|
61826a20e4 | ||
|
|
32951e1a68 | ||
|
|
1d13e405e4 | ||
|
|
729e879c1c | ||
|
|
64c9cf1b9e | ||
|
|
4d94892eb0 | ||
|
|
3ecb1744ce | ||
|
|
2f9c3fb3e2 | ||
|
|
2609c270b8 | ||
|
|
48b3e34cd3 | ||
|
|
abb2c43667 | ||
|
|
d1cfbb35d3 | ||
|
|
38a630eee0 | ||
|
|
be1a2023ce | ||
|
|
021d0c1700 | ||
|
|
f49cd377ce | ||
|
|
fc80840784 | ||
|
|
7742d06a58 | ||
|
|
e0ea1d48a4 | ||
|
|
f028ad8c12 | ||
|
|
9c153c822b | ||
|
|
d803352bd6 | ||
|
|
2eafac47e8 | ||
|
|
848fdde0a8 | ||
|
|
31e7dc68e2 | ||
|
|
08299a2059 | ||
|
|
2f3156eafb | ||
|
|
72e85101b0 | ||
|
|
d205260a3e | ||
|
|
97e576d146 | ||
|
|
888cb78331 | ||
|
|
1d4c261d2a | ||
|
|
83de0c0abd | ||
|
|
c501adc9ab | ||
|
|
f9fc24cc08 | ||
|
|
cd26244ccc | ||
|
|
cabab6aafe | ||
|
|
fb42a9b4aa | ||
|
|
141f4d9116 | ||
|
|
cb31152b53 | ||
|
|
3a7745f920 | ||
|
|
a89916fb1a | ||
|
|
c6cf46c904 | ||
|
|
b28a71ab13 | ||
|
|
95b9859bcd | ||
|
|
9e599753af | ||
|
|
2e924906bb | ||
|
|
e811c1ad32 | ||
|
|
86695b55bb | ||
|
|
8c3a4d882a | ||
|
|
4bad343ddc | ||
|
|
47b8a05c32 | ||
|
|
4e6f4af601 | ||
|
|
7275f6f9c3 | ||
|
|
c3dae6a7d4 | ||
|
|
bb404eda4a | ||
|
|
584710bd80 | ||
|
|
ad5eae9adf | ||
|
|
26fae7cd2d | ||
|
|
87d6655368 | ||
|
|
cd60b254a0 | ||
|
|
b88cdcdd4b | ||
|
|
4a5e06cb45 | ||
|
|
fff3a7ad1f | ||
|
|
05c894d487 | ||
|
|
8850e9ccd9 | ||
|
|
2746531851 | ||
|
|
2856db5490 | ||
|
|
b29e78ccae | ||
|
|
c9761c3588 | ||
|
|
e4ef21e07c | ||
|
|
61429aa0d6 | ||
|
|
c1ef011556 | ||
|
|
cd1424c09f | ||
|
|
878d627f93 | ||
|
|
1d6385ddc5 | ||
|
|
873f730b4e | ||
|
|
1c1547b137 | ||
|
|
9997f3e3d3 | ||
|
|
4d0b4ecc22 | ||
|
|
ccb24b5f1f | ||
|
|
5cb493439d | ||
|
|
24b284240a | ||
|
|
b0f77d24f9 | ||
|
|
e32ac492d3 | ||
|
|
470a7f8cc5 | ||
|
|
b269fce289 | ||
|
|
bc4febe92c | ||
|
|
b5f9bfe0f4 | ||
|
|
4ce1e8119b | ||
|
|
65136b38e2 | ||
|
|
ffc114e8d9 | ||
|
|
f2be2d6ed5 | ||
|
|
ab690a06a6 | ||
|
|
10cdaf14c5 | ||
|
|
ebbfb63c17 | ||
|
|
ac25da6cea | ||
|
|
fb41972ba3 | ||
|
|
504d1618af | ||
|
|
0f91b23f12 | ||
|
|
5ddd62a8b9 | ||
|
|
e7f13a7e73 | ||
|
|
89e825fb0d | ||
|
|
1b706e84fa | ||
|
|
5cafcb70dc | ||
|
|
8c71266058 | ||
|
|
9c986c5c13 | ||
|
|
3d0451d7f2 | ||
|
|
b1f65c97ae | ||
|
|
5584809fca | ||
|
|
7586f17b15 | ||
|
|
0e16d873c8 | ||
|
|
b11acd2118 | ||
|
|
1376f5b077 | ||
|
|
340ea05ae5 | ||
|
|
024ec51f66 | ||
|
|
efe453df87 | ||
|
|
c0f4d903ba | ||
|
|
28f2b268ae | ||
|
|
5abb6916b6 | ||
|
|
e6aec94c0c | ||
|
|
9eddc7c662 | ||
|
|
028c730a4f | ||
|
|
3c93bdaf80 | ||
|
|
037b99997e | ||
|
|
e67744bf2a | ||
|
|
5843e6fb4f | ||
|
|
f0ff7e4c69 | ||
|
|
24808ad534 | ||
|
|
c4bccef138 | ||
|
|
adf2345adb | ||
|
|
f7daad91e6 | ||
|
|
65052d7b59 | ||
|
|
b07ec5c0c6 | ||
|
|
f4c94ddba1 | ||
|
|
a7ec485ca9 | ||
|
|
86b6469e35 | ||
|
|
3e16ca724d | ||
|
|
93dd5a6768 | ||
|
|
ec43d0cb9f | ||
|
|
8469a750e7 | ||
|
|
fc6ac07ce8 | ||
|
|
79cdc31b37 | ||
|
|
be33ad6ac7 | ||
|
|
a6e45d6157 | ||
|
|
37e0f61679 | ||
|
|
5d382b6423 | ||
|
|
78a4344054 | ||
|
|
a4f0a638e7 | ||
|
|
c5aa3736f9 | ||
|
|
b0f83fd48c | ||
|
|
d6e5094095 | ||
|
|
483e1d91ba | ||
|
|
d215bb21e0 | ||
|
|
61ac0c5b95 | ||
|
|
1fa30f07e8 | ||
|
|
39d0451a10 | ||
|
|
4dc7a89f45 | ||
|
|
fd26f93b80 | ||
|
|
dd2c74d413 | ||
|
|
b7e0df127f | ||
|
|
f591e692fc | ||
|
|
8855bce085 |
@@ -1,52 +0,0 @@
|
|||||||
version: '{build}'
|
|
||||||
|
|
||||||
image: Visual Studio 2015
|
|
||||||
|
|
||||||
cache:
|
|
||||||
- NimBinaries
|
|
||||||
- p2pdCache
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
# We always want 32 and 64-bit compilation
|
|
||||||
fast_finish: false
|
|
||||||
|
|
||||||
platform:
|
|
||||||
- x86
|
|
||||||
- x64
|
|
||||||
|
|
||||||
# when multiple CI builds are queued, the tested commit needs to be in the last X commits cloned with "--depth X"
|
|
||||||
clone_depth: 10
|
|
||||||
|
|
||||||
install:
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
|
|
||||||
# use the newest versions documented here: https://www.appveyor.com/docs/windows-images-software/#mingw-msys-cygwin
|
|
||||||
- IF "%PLATFORM%" == "x86" SET PATH=C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin;%PATH%
|
|
||||||
- IF "%PLATFORM%" == "x64" SET PATH=C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;%PATH%
|
|
||||||
|
|
||||||
# build nim from our own branch - this to avoid the day-to-day churn and
|
|
||||||
# regressions of the fast-paced Nim development while maintaining the
|
|
||||||
# flexibility to apply patches
|
|
||||||
- curl -O -L -s -S https://raw.githubusercontent.com/status-im/nimbus-build-system/master/scripts/build_nim.sh
|
|
||||||
- env MAKE="mingw32-make -j2" ARCH_OVERRIDE=%PLATFORM% bash build_nim.sh Nim csources dist/nimble NimBinaries
|
|
||||||
- SET PATH=%CD%\Nim\bin;%PATH%
|
|
||||||
|
|
||||||
# set path for produced Go binaries
|
|
||||||
- MKDIR goblin
|
|
||||||
- CD goblin
|
|
||||||
- SET GOPATH=%CD%
|
|
||||||
- SET PATH=%GOPATH%\bin;%PATH%
|
|
||||||
- CD ..
|
|
||||||
|
|
||||||
# install and build go-libp2p-daemon
|
|
||||||
- bash scripts/build_p2pd.sh p2pdCache v0.3.0
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- nimble install -y --depsOnly
|
|
||||||
|
|
||||||
test_script:
|
|
||||||
- nimble test
|
|
||||||
- nimble examples_build
|
|
||||||
|
|
||||||
deploy: off
|
|
||||||
|
|
||||||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* @vacp2p/p2p
|
||||||
34
.github/actions/add_comment/action.yml
vendored
Normal file
34
.github/actions/add_comment/action.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
name: Add Comment
|
||||||
|
description: "Add or update comment in the PR"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Add/Update Comment
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const marker = "${{ env.MARKER }}";
|
||||||
|
const body = fs.readFileSync("${{ env.COMMENT_SUMMARY_PATH }}", 'utf8');
|
||||||
|
const { data: comments } = await github.rest.issues.listComments({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
});
|
||||||
|
const existing = comments.find(c => c.body && c.body.startsWith(marker));
|
||||||
|
if (existing) {
|
||||||
|
await github.rest.issues.updateComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
comment_id: existing.id,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await github.rest.issues.createComment({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
issue_number: context.issue.number,
|
||||||
|
body,
|
||||||
|
});
|
||||||
|
}
|
||||||
49
.github/actions/discord_notify/action.yml
vendored
Normal file
49
.github/actions/discord_notify/action.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
name: Discord Failure Notification
|
||||||
|
description: "Send Discord notification when CI jobs fail"
|
||||||
|
inputs:
|
||||||
|
webhook_url:
|
||||||
|
description: "Discord webhook URL"
|
||||||
|
required: true
|
||||||
|
workflow_name:
|
||||||
|
description: "Name of the workflow that failed"
|
||||||
|
required: false
|
||||||
|
default: ${{ github.workflow }}
|
||||||
|
branch:
|
||||||
|
description: "Branch name"
|
||||||
|
required: false
|
||||||
|
default: ${{ github.ref_name }}
|
||||||
|
repository:
|
||||||
|
description: "Repository name"
|
||||||
|
required: false
|
||||||
|
default: ${{ github.repository }}
|
||||||
|
run_id:
|
||||||
|
description: "GitHub run ID"
|
||||||
|
required: false
|
||||||
|
default: ${{ github.run_id }}
|
||||||
|
server_url:
|
||||||
|
description: "GitHub server URL"
|
||||||
|
required: false
|
||||||
|
default: ${{ github.server_url }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Send Discord notification
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
curl -H "Content-Type: application/json" \
|
||||||
|
-X POST \
|
||||||
|
-d "{
|
||||||
|
\"embeds\": [{
|
||||||
|
\"title\": \"${{ inputs.workflow_name }} Job Failed\",
|
||||||
|
\"url\": \"${{ inputs.server_url }}/${{ inputs.repository }}/actions/runs/${{ inputs.run_id }}\",
|
||||||
|
\"description\": \"The workflow has failed on branch \`${{ inputs.branch }}\`\",
|
||||||
|
\"color\": 15158332,
|
||||||
|
\"fields\": [
|
||||||
|
{\"name\": \"Repository\", \"value\": \"${{ inputs.repository }}\", \"inline\": true},
|
||||||
|
{\"name\": \"Branch\", \"value\": \"${{ inputs.branch }}\", \"inline\": true}
|
||||||
|
],
|
||||||
|
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\"
|
||||||
|
}]
|
||||||
|
}" \
|
||||||
|
"${{ inputs.webhook_url }}"
|
||||||
24
.github/actions/generate_plots/action.yml
vendored
Normal file
24
.github/actions/generate_plots/action.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
name: Generate Plots
|
||||||
|
description: "Set up Python and run script to generate plots with Docker Stats"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: "3.12"
|
||||||
|
|
||||||
|
- name: Install Python dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install matplotlib
|
||||||
|
|
||||||
|
- name: Plot Docker Stats
|
||||||
|
shell: bash
|
||||||
|
run: python performance/scripts/plot_docker_stats.py
|
||||||
|
|
||||||
|
- name: Plot Latency History
|
||||||
|
shell: bash
|
||||||
|
run: python performance/scripts/plot_latency_history.py
|
||||||
4
.github/actions/install_nim/action.yml
vendored
4
.github/actions/install_nim/action.yml
vendored
@@ -8,7 +8,7 @@ inputs:
|
|||||||
default: "amd64"
|
default: "amd64"
|
||||||
nim_ref:
|
nim_ref:
|
||||||
description: "Nim version"
|
description: "Nim version"
|
||||||
default: "version-1-6"
|
default: "version-2-0"
|
||||||
shell:
|
shell:
|
||||||
description: "Shell to run commands in"
|
description: "Shell to run commands in"
|
||||||
default: "bash --noprofile --norc -e -o pipefail"
|
default: "bash --noprofile --norc -e -o pipefail"
|
||||||
@@ -88,6 +88,8 @@ runs:
|
|||||||
run: |
|
run: |
|
||||||
if [[ '${{ inputs.cpu }}' == 'amd64' ]]; then
|
if [[ '${{ inputs.cpu }}' == 'amd64' ]]; then
|
||||||
PLATFORM=x64
|
PLATFORM=x64
|
||||||
|
elif [[ '${{ inputs.cpu }}' == 'arm64' ]]; then
|
||||||
|
PLATFORM=arm64
|
||||||
else
|
else
|
||||||
PLATFORM=x86
|
PLATFORM=x86
|
||||||
fi
|
fi
|
||||||
|
|||||||
21
.github/actions/process_stats/action.yml
vendored
Normal file
21
.github/actions/process_stats/action.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
name: Process Stats
|
||||||
|
description: "Set up Nim and run scripts to aggregate latency and process raw docker stats"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Set up Nim
|
||||||
|
uses: jiro4989/setup-nim-action@v2
|
||||||
|
with:
|
||||||
|
nim-version: "2.x"
|
||||||
|
repo-token: ${{ env.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Aggregate latency stats and prepare markdown for comment and summary
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
nim c -r -d:release -o:/tmp/process_latency_stats ./performance/scripts/process_latency_stats.nim
|
||||||
|
|
||||||
|
- name: Process raw docker stats to csv files
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
nim c -r -d:release -o:/tmp/process_docker_stats ./performance/scripts/process_docker_stats.nim
|
||||||
36
.github/actions/publish_history/action.yml
vendored
Normal file
36
.github/actions/publish_history/action.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
name: Publish Latency History
|
||||||
|
description: "Publish latency history CSVs in a configurable branch and folder"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Clone the branch
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository }}
|
||||||
|
ref: ${{ env.PUBLISH_BRANCH_NAME }}
|
||||||
|
path: ${{ env.CHECKOUT_SUBFOLDER_HISTORY }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Commit & push latency history CSVs
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd "$CHECKOUT_SUBFOLDER_HISTORY"
|
||||||
|
git fetch origin "$PUBLISH_BRANCH_NAME"
|
||||||
|
git reset --hard "origin/$PUBLISH_BRANCH_NAME"
|
||||||
|
|
||||||
|
mkdir -p "$PUBLISH_DIR_LATENCY_HISTORY"
|
||||||
|
|
||||||
|
cp ../$SHARED_VOLUME_PATH/$LATENCY_HISTORY_PREFIX*.csv "$PUBLISH_DIR_LATENCY_HISTORY/"
|
||||||
|
git add "$PUBLISH_DIR_LATENCY_HISTORY"
|
||||||
|
|
||||||
|
if git diff-index --quiet HEAD --; then
|
||||||
|
echo "No changes to commit"
|
||||||
|
else
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git commit -m "Update latency history CSVs"
|
||||||
|
git push origin "$PUBLISH_BRANCH_NAME"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd ..
|
||||||
56
.github/actions/publish_plots/action.yml
vendored
Normal file
56
.github/actions/publish_plots/action.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
name: Publish Plots
|
||||||
|
description: "Publish plots in performance_plots branch and add to the workflow summary"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Clone the performance_plots branch
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository }}
|
||||||
|
ref: ${{ env.PUBLISH_BRANCH_NAME }}
|
||||||
|
path: ${{ env.CHECKOUT_SUBFOLDER_SUBPLOTS }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Commit & push plots
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cd $CHECKOUT_SUBFOLDER_SUBPLOTS
|
||||||
|
git fetch origin "$PUBLISH_BRANCH_NAME"
|
||||||
|
git reset --hard "origin/$PUBLISH_BRANCH_NAME"
|
||||||
|
|
||||||
|
# Remove any branch folder older than 7 days
|
||||||
|
DAYS=7
|
||||||
|
cutoff=$(( $(date +%s) - DAYS*24*3600 ))
|
||||||
|
scan_dir="${PUBLISH_DIR_PLOTS%/}"
|
||||||
|
find "$scan_dir" -mindepth 1 -maxdepth 1 -type d -print0 \
|
||||||
|
| while IFS= read -r -d $'\0' d; do \
|
||||||
|
ts=$(git log -1 --format=%ct -- "$d" 2>/dev/null || true); \
|
||||||
|
if [ -n "$ts" ] && [ "$ts" -le "$cutoff" ]; then \
|
||||||
|
echo "[cleanup] Deleting: $d"; \
|
||||||
|
rm -rf -- "$d"; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
rm -rf $PUBLISH_DIR_PLOTS/$BRANCH_NAME
|
||||||
|
mkdir -p $PUBLISH_DIR_PLOTS/$BRANCH_NAME
|
||||||
|
|
||||||
|
cp ../$SHARED_VOLUME_PATH/*.png $PUBLISH_DIR_PLOTS/$BRANCH_NAME/ 2>/dev/null || true
|
||||||
|
cp ../$LATENCY_HISTORY_PATH/*.png $PUBLISH_DIR_PLOTS/ 2>/dev/null || true
|
||||||
|
git add -A "$PUBLISH_DIR_PLOTS/"
|
||||||
|
|
||||||
|
git status
|
||||||
|
|
||||||
|
if git diff-index --quiet HEAD --; then
|
||||||
|
echo "No changes to commit"
|
||||||
|
else
|
||||||
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config user.name "github-actions[bot]"
|
||||||
|
git commit -m "Update performance plots for $BRANCH_NAME"
|
||||||
|
git push origin $PUBLISH_BRANCH_NAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Add plots to GitHub Actions summary
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
nim c -r -d:release -o:/tmp/add_plots_to_summary ./performance/scripts/add_plots_to_summary.nim
|
||||||
12
.github/workflows/auto_assign_pr.yml
vendored
Normal file
12
.github/workflows/auto_assign_pr.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
name: Auto Assign PR to Creator
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
assign_creator:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: toshimaru/auto-author-assign@v1.6.2
|
||||||
32
.github/workflows/ci.yml
vendored
32
.github/workflows/ci.yml
vendored
@@ -14,7 +14,7 @@ concurrency:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
timeout-minutes: 90
|
timeout-minutes: 40
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -25,17 +25,14 @@ jobs:
|
|||||||
cpu: i386
|
cpu: i386
|
||||||
- os: linux-gcc-14
|
- os: linux-gcc-14
|
||||||
cpu: amd64
|
cpu: amd64
|
||||||
- os: macos
|
- os: macos-14
|
||||||
cpu: amd64
|
cpu: arm64
|
||||||
- os: windows
|
- os: windows
|
||||||
cpu: amd64
|
cpu: amd64
|
||||||
nim:
|
nim:
|
||||||
- ref: version-1-6
|
- ref: version-2-0
|
||||||
memory_management: refc
|
memory_management: refc
|
||||||
# The ref below corresponds to the branch "version-2-0".
|
- ref: version-2-2
|
||||||
# Right before an update from Nimble 0.16.1 to 0.16.2.
|
|
||||||
# That update breaks our dependency resolution.
|
|
||||||
- ref: 8754469f4947844c5938f56e1fba846c349354b5
|
|
||||||
memory_management: refc
|
memory_management: refc
|
||||||
include:
|
include:
|
||||||
- platform:
|
- platform:
|
||||||
@@ -47,8 +44,8 @@ jobs:
|
|||||||
builder: ubuntu-24.04
|
builder: ubuntu-24.04
|
||||||
shell: bash
|
shell: bash
|
||||||
- platform:
|
- platform:
|
||||||
os: macos
|
os: macos-14
|
||||||
builder: macos-13
|
builder: macos-14
|
||||||
shell: bash
|
shell: bash
|
||||||
- platform:
|
- platform:
|
||||||
os: windows
|
os: windows
|
||||||
@@ -75,23 +72,14 @@ jobs:
|
|||||||
shell: ${{ matrix.shell }}
|
shell: ${{ matrix.shell }}
|
||||||
nim_ref: ${{ matrix.nim.ref }}
|
nim_ref: ${{ matrix.nim.ref }}
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '~1.15.5'
|
|
||||||
|
|
||||||
- name: Install p2pd
|
|
||||||
run: |
|
|
||||||
V=1 bash scripts/build_p2pd.sh p2pdCache 124530a3
|
|
||||||
|
|
||||||
- name: Restore deps from cache
|
- name: Restore deps from cache
|
||||||
id: deps-cache
|
id: deps-cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: nimbledeps
|
path: nimbledeps
|
||||||
# Using nim.ref as a simple way to differentiate between nimble using the "pkgs" or "pkgs2" directories.
|
# Using nim.ref as a simple way to differentiate between nimble using the "pkgs" or "pkgs2" directories.
|
||||||
# The change happened on Nimble v0.14.0.
|
# The change happened on Nimble v0.14.0. Also forcing the deps to be reinstalled on each os and cpu.
|
||||||
key: nimbledeps-${{ matrix.nim.ref }}-${{ hashFiles('.pinned') }} # hashFiles returns a different value on windows
|
key: nimbledeps-${{ matrix.nim.ref }}-${{ matrix.builder }}-${{ matrix.platform.cpu }}-${{ hashFiles('.pinned') }} # hashFiles returns a different value on windows
|
||||||
|
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
if: ${{ steps.deps-cache.outputs.cache-hit != 'true' }}
|
if: ${{ steps.deps-cache.outputs.cache-hit != 'true' }}
|
||||||
@@ -113,5 +101,5 @@ jobs:
|
|||||||
nimble --version
|
nimble --version
|
||||||
gcc --version
|
gcc --version
|
||||||
|
|
||||||
NIMFLAGS="${NIMFLAGS} --mm:${{ matrix.nim.memory_management }}"
|
export NIMFLAGS="${NIMFLAGS} --mm:${{ matrix.nim.memory_management }}"
|
||||||
nimble test
|
nimble test
|
||||||
|
|||||||
34
.github/workflows/daily_amd64.yml
vendored
34
.github/workflows/daily_amd64.yml
vendored
@@ -6,9 +6,37 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test_amd64:
|
test_amd64_latest:
|
||||||
name: Daily amd64
|
name: Daily test amd64 (latest dependencies)
|
||||||
uses: ./.github/workflows/daily_common.yml
|
uses: ./.github/workflows/daily_common.yml
|
||||||
with:
|
with:
|
||||||
nim: "[{'ref': 'version-1-6', 'memory_management': 'refc'}, {'ref': '8754469f4947844c5938f56e1fba846c349354b5', 'memory_management': 'refc'}]"
|
nim: "[
|
||||||
|
{'ref': 'version-2-0', 'memory_management': 'refc'},
|
||||||
|
{'ref': 'version-2-2', 'memory_management': 'refc'},
|
||||||
|
{'ref': 'devel', 'memory_management': 'refc'},
|
||||||
|
]"
|
||||||
cpu: "['amd64']"
|
cpu: "['amd64']"
|
||||||
|
test_amd64_pinned:
|
||||||
|
name: Daily test amd64 (pinned dependencies)
|
||||||
|
uses: ./.github/workflows/daily_common.yml
|
||||||
|
with:
|
||||||
|
pinned_deps: true
|
||||||
|
nim: "[
|
||||||
|
{'ref': 'version-2-0', 'memory_management': 'refc'},
|
||||||
|
{'ref': 'version-2-2', 'memory_management': 'refc'},
|
||||||
|
{'ref': 'devel', 'memory_management': 'refc'},
|
||||||
|
]"
|
||||||
|
cpu: "['amd64']"
|
||||||
|
notify-on-failure:
|
||||||
|
name: Notify Discord on Failure
|
||||||
|
needs: [test_amd64_latest, test_amd64_pinned]
|
||||||
|
if: failure()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Discord notification
|
||||||
|
uses: ./.github/actions/discord_notify
|
||||||
|
with:
|
||||||
|
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||||
50
.github/workflows/daily_common.yml
vendored
50
.github/workflows/daily_common.yml
vendored
@@ -4,6 +4,11 @@ name: Daily Common
|
|||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
|
pinned_deps:
|
||||||
|
description: 'Should dependencies be installed from pinned file or use latest versions'
|
||||||
|
required: false
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
nim:
|
nim:
|
||||||
description: 'Nim Configuration'
|
description: 'Nim Configuration'
|
||||||
required: true
|
required: true
|
||||||
@@ -17,26 +22,18 @@ on:
|
|||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
default: "[]"
|
default: "[]"
|
||||||
use_sat_solver:
|
|
||||||
description: 'Install dependencies with SAT Solver'
|
|
||||||
required: false
|
|
||||||
type: boolean
|
|
||||||
default: false
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
delete_cache:
|
delete_cache:
|
||||||
name: Delete github action's branch cache
|
name: Delete github action's branch cache
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- uses: snnaplab/delete-branch-cache-action@v1
|
- uses: snnaplab/delete-branch-cache-action@v1
|
||||||
|
|
||||||
test:
|
test:
|
||||||
needs: delete_cache
|
needs: delete_cache
|
||||||
timeout-minutes: 90
|
timeout-minutes: 40
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -72,17 +69,13 @@ jobs:
|
|||||||
nim_ref: ${{ matrix.nim.ref }}
|
nim_ref: ${{ matrix.nim.ref }}
|
||||||
cpu: ${{ matrix.cpu }}
|
cpu: ${{ matrix.cpu }}
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Install dependencies (pinned)
|
||||||
uses: actions/setup-go@v5
|
if: ${{ inputs.pinned_deps }}
|
||||||
with:
|
|
||||||
go-version: '~1.15.5'
|
|
||||||
cache: false
|
|
||||||
|
|
||||||
- name: Install p2pd
|
|
||||||
run: |
|
run: |
|
||||||
V=1 bash scripts/build_p2pd.sh p2pdCache 124530a3
|
nimble install_pinned
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies (latest)
|
||||||
|
if: ${{ inputs.pinned_deps == false }}
|
||||||
run: |
|
run: |
|
||||||
nimble install -y --depsOnly
|
nimble install -y --depsOnly
|
||||||
|
|
||||||
@@ -91,11 +84,14 @@ jobs:
|
|||||||
nim --version
|
nim --version
|
||||||
nimble --version
|
nimble --version
|
||||||
|
|
||||||
if [[ "${{ inputs.use_sat_solver }}" == "true" ]]; then
|
export NIMFLAGS="${NIMFLAGS} --mm:${{ matrix.nim.memory_management }}"
|
||||||
dependency_solver="sat"
|
|
||||||
else
|
|
||||||
dependency_solver="legacy"
|
|
||||||
fi
|
|
||||||
|
|
||||||
NIMFLAGS="${NIMFLAGS} --mm:${{ matrix.nim.memory_management }} --solver:${dependency_solver}"
|
|
||||||
nimble test
|
nimble test
|
||||||
|
|
||||||
|
- name: Run integration tests
|
||||||
|
if: ${{ matrix.platform.os == 'linux' && matrix.cpu == 'amd64' }}
|
||||||
|
run: |
|
||||||
|
nim --version
|
||||||
|
nimble --version
|
||||||
|
|
||||||
|
export NIMFLAGS="${NIMFLAGS} --mm:${{ matrix.nim.memory_management }}"
|
||||||
|
nimble testintegration
|
||||||
|
|||||||
14
.github/workflows/daily_devel.yml
vendored
14
.github/workflows/daily_devel.yml
vendored
@@ -1,14 +0,0 @@
|
|||||||
name: Daily Nim Devel
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "30 6 * * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test_nim_devel:
|
|
||||||
name: Daily Nim Devel
|
|
||||||
uses: ./.github/workflows/daily_common.yml
|
|
||||||
with:
|
|
||||||
nim: "[{'ref': 'devel', 'memory_management': 'orc'}]"
|
|
||||||
cpu: "['amd64']"
|
|
||||||
43
.github/workflows/daily_i386.yml
vendored
43
.github/workflows/daily_i386.yml
vendored
@@ -6,10 +6,45 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test_i386:
|
test_i386_latest:
|
||||||
name: Daily i386 (Linux)
|
name: Daily i386 (latest dependencies)
|
||||||
uses: ./.github/workflows/daily_common.yml
|
uses: ./.github/workflows/daily_common.yml
|
||||||
with:
|
with:
|
||||||
nim: "[{'ref': 'version-1-6', 'memory_management': 'refc'}, {'ref': '8754469f4947844c5938f56e1fba846c349354b5', 'memory_management': 'refc'}, {'ref': 'devel', 'memory_management': 'orc'}]"
|
nim: "[
|
||||||
|
{'ref': 'version-2-0', 'memory_management': 'refc'},
|
||||||
|
{'ref': 'version-2-2', 'memory_management': 'refc'},
|
||||||
|
{'ref': 'devel', 'memory_management': 'refc'},
|
||||||
|
]"
|
||||||
cpu: "['i386']"
|
cpu: "['i386']"
|
||||||
exclude: "[{'platform': {'os':'macos'}}, {'platform': {'os':'windows'}}]"
|
exclude: "[
|
||||||
|
{'platform': {'os':'macos'}},
|
||||||
|
{'platform': {'os':'windows'}},
|
||||||
|
]"
|
||||||
|
test_i386_pinned:
|
||||||
|
name: Daily i386 (pinned dependencies)
|
||||||
|
uses: ./.github/workflows/daily_common.yml
|
||||||
|
with:
|
||||||
|
pinned_deps: true
|
||||||
|
nim: "[
|
||||||
|
{'ref': 'version-2-0', 'memory_management': 'refc'},
|
||||||
|
{'ref': 'version-2-2', 'memory_management': 'refc'},
|
||||||
|
{'ref': 'devel', 'memory_management': 'refc'},
|
||||||
|
]"
|
||||||
|
cpu: "['i386']"
|
||||||
|
exclude: "[
|
||||||
|
{'platform': {'os':'macos'}},
|
||||||
|
{'platform': {'os':'windows'}},
|
||||||
|
]"
|
||||||
|
notify-on-failure:
|
||||||
|
name: Notify Discord on Failure
|
||||||
|
needs: [test_i386_latest, test_i386_pinned]
|
||||||
|
if: failure()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Discord notification
|
||||||
|
uses: ./.github/actions/discord_notify
|
||||||
|
with:
|
||||||
|
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||||
39
.github/workflows/daily_nimbus.yml
vendored
Normal file
39
.github/workflows/daily_nimbus.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Daily Nimbus
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "30 6 * * *"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
compile_nimbus:
|
||||||
|
timeout-minutes: 80
|
||||||
|
name: 'Compile Nimbus (linux-amd64)'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Compile nimbus using nim-libp2p
|
||||||
|
run: |
|
||||||
|
git clone --branch unstable --single-branch https://github.com/status-im/nimbus-eth2.git
|
||||||
|
cd nimbus-eth2
|
||||||
|
git submodule set-branch --branch ${{ github.sha }} vendor/nim-libp2p
|
||||||
|
|
||||||
|
make -j"$(nproc)"
|
||||||
|
make -j"$(nproc)" nimbus_beacon_node
|
||||||
|
|
||||||
|
notify-on-failure:
|
||||||
|
name: Notify Discord on Failure
|
||||||
|
needs: compile_nimbus
|
||||||
|
if: failure()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Discord notification
|
||||||
|
uses: ./.github/actions/discord_notify
|
||||||
|
with:
|
||||||
|
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||||
|
|
||||||
15
.github/workflows/daily_sat.yml
vendored
15
.github/workflows/daily_sat.yml
vendored
@@ -1,15 +0,0 @@
|
|||||||
name: Daily SAT
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "30 6 * * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
test_amd64:
|
|
||||||
name: Daily SAT
|
|
||||||
uses: ./.github/workflows/daily_common.yml
|
|
||||||
with:
|
|
||||||
nim: "[{'ref': '8754469f4947844c5938f56e1fba846c349354b5', 'memory_management': 'refc'}]"
|
|
||||||
cpu: "['amd64']"
|
|
||||||
use_sat_solver: true
|
|
||||||
23
.github/workflows/dependencies.yml
vendored
23
.github/workflows/dependencies.yml
vendored
@@ -17,10 +17,13 @@ jobs:
|
|||||||
target:
|
target:
|
||||||
- repository: status-im/nimbus-eth2
|
- repository: status-im/nimbus-eth2
|
||||||
ref: unstable
|
ref: unstable
|
||||||
|
secret: ACTIONS_GITHUB_TOKEN_NIMBUS_ETH2
|
||||||
- repository: waku-org/nwaku
|
- repository: waku-org/nwaku
|
||||||
ref: master
|
ref: master
|
||||||
|
secret: ACTIONS_GITHUB_TOKEN_NWAKU
|
||||||
- repository: codex-storage/nim-codex
|
- repository: codex-storage/nim-codex
|
||||||
ref: master
|
ref: master
|
||||||
|
secret: ACTIONS_GITHUB_TOKEN_NIM_CODEX
|
||||||
steps:
|
steps:
|
||||||
- name: Clone target repository
|
- name: Clone target repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -29,7 +32,7 @@ jobs:
|
|||||||
ref: ${{ matrix.target.ref}}
|
ref: ${{ matrix.target.ref}}
|
||||||
path: nbc
|
path: nbc
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.ACTIONS_GITHUB_TOKEN }}
|
token: ${{ secrets[matrix.target.secret] }}
|
||||||
|
|
||||||
- name: Checkout this ref in target repository
|
- name: Checkout this ref in target repository
|
||||||
run: |
|
run: |
|
||||||
@@ -44,7 +47,19 @@ jobs:
|
|||||||
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
git config --global user.email "${{ github.actor }}@users.noreply.github.com"
|
||||||
git config --global user.name = "${{ github.actor }}"
|
git config --global user.name = "${{ github.actor }}"
|
||||||
git commit --allow-empty -a -m "auto-bump nim-libp2p"
|
git commit --allow-empty -a -m "auto-bump nim-libp2p"
|
||||||
git branch -D nim-libp2p-auto-bump-${GITHUB_REF##*/} || true
|
git branch -D nim-libp2p-auto-bump-${{ matrix.target.ref }} || true
|
||||||
git switch -c nim-libp2p-auto-bump-${GITHUB_REF##*/}
|
git switch -c nim-libp2p-auto-bump-${{ matrix.target.ref }}
|
||||||
git push -f origin nim-libp2p-auto-bump-${GITHUB_REF##*/}
|
git push -f origin nim-libp2p-auto-bump-${{ matrix.target.ref }}
|
||||||
|
notify-on-failure:
|
||||||
|
name: Notify Discord on Failure
|
||||||
|
needs: [bumper]
|
||||||
|
if: failure()
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Discord notification
|
||||||
|
uses: ./.github/actions/discord_notify
|
||||||
|
with:
|
||||||
|
webhook_url: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||||
2
.github/workflows/documentation.yml
vendored
2
.github/workflows/documentation.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: jiro4989/setup-nim-action@v1
|
- uses: jiro4989/setup-nim-action@v1
|
||||||
with:
|
with:
|
||||||
nim-version: '1.6.x'
|
nim-version: '2.2.x'
|
||||||
|
|
||||||
- name: Generate doc
|
- name: Generate doc
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
60
.github/workflows/examples.yml
vendored
Normal file
60
.github/workflows/examples.yml
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
name: Examples
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
merge_group:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
examples:
|
||||||
|
timeout-minutes: 30
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
name: "Build Examples"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Setup Nim
|
||||||
|
uses: "./.github/actions/install_nim"
|
||||||
|
with:
|
||||||
|
shell: bash
|
||||||
|
os: linux
|
||||||
|
cpu: amd64
|
||||||
|
nim_ref: version-2-2
|
||||||
|
|
||||||
|
- name: Restore deps from cache
|
||||||
|
id: deps-cache
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: nimbledeps
|
||||||
|
key: nimbledeps-${{ hashFiles('.pinned') }}
|
||||||
|
|
||||||
|
- name: Install deps
|
||||||
|
if: ${{ steps.deps-cache.outputs.cache-hit != 'true' }}
|
||||||
|
run: |
|
||||||
|
nimble install_pinned
|
||||||
|
|
||||||
|
- name: Build and run examples
|
||||||
|
run: |
|
||||||
|
nim --version
|
||||||
|
nimble --version
|
||||||
|
gcc --version
|
||||||
|
|
||||||
|
NIMFLAGS="${NIMFLAGS} --mm:${{ matrix.nim.memory_management }}"
|
||||||
|
nimble examples
|
||||||
62
.github/workflows/interop.yml
vendored
62
.github/workflows/interop.yml
vendored
@@ -27,31 +27,63 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: docker/setup-buildx-action@v3
|
- uses: docker/setup-buildx-action@v3
|
||||||
- name: Build image
|
- name: Build image
|
||||||
run: docker buildx build --load -t nim-libp2p-head -f tests/transport-interop/Dockerfile .
|
run: docker buildx build --load -t nim-libp2p-head -f interop/transport/Dockerfile .
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
uses: libp2p/test-plans/.github/actions/run-transport-interop-test@master
|
uses: libp2p/test-plans/.github/actions/run-transport-interop-test@master
|
||||||
with:
|
with:
|
||||||
test-filter: nim-libp2p-head
|
test-filter: nim-libp2p-head
|
||||||
extra-versions: ${{ github.workspace }}/tests/transport-interop/version.json
|
# without suffix action fails because "hole-punching-interop" artifacts have
|
||||||
|
# the same name as "transport-interop" artifacts
|
||||||
|
test-results-suffix: transport-interop
|
||||||
|
extra-versions: ${{ github.workspace }}/interop/transport/version.json
|
||||||
s3-cache-bucket: ${{ vars.S3_LIBP2P_BUILD_CACHE_BUCKET_NAME }}
|
s3-cache-bucket: ${{ vars.S3_LIBP2P_BUILD_CACHE_BUCKET_NAME }}
|
||||||
s3-access-key-id: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_ACCESS_KEY_ID }}
|
s3-access-key-id: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_ACCESS_KEY_ID }}
|
||||||
s3-secret-access-key: ${{ secrets.S3_LIBP2P_BUILD_CACHE_AWS_SECRET_ACCESS_KEY }}
|
s3-secret-access-key: ${{ secrets.S3_LIBP2P_BUILD_CACHE_AWS_SECRET_ACCESS_KEY }}
|
||||||
aws-region: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_REGION }}
|
aws-region: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_REGION }}
|
||||||
|
|
||||||
run-hole-punching-interop:
|
# nim-libp2p#1367: hole punching tests are temporary disabled as they keep failing
|
||||||
name: Run hole-punching interoperability tests
|
# and issue does not seem to be on nim-libp2p side
|
||||||
|
# run-hole-punching-interop:
|
||||||
|
# name: Run hole-punching interoperability tests
|
||||||
|
# runs-on: ubuntu-22.04
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v4
|
||||||
|
# - uses: docker/setup-buildx-action@v3
|
||||||
|
# - name: Build image
|
||||||
|
# run: docker buildx build --load -t nim-libp2p-head -f interop/hole-punching/Dockerfile .
|
||||||
|
# - name: Run tests
|
||||||
|
# uses: libp2p/test-plans/.github/actions/run-interop-hole-punch-test@master
|
||||||
|
# with:
|
||||||
|
# test-filter: nim-libp2p-head
|
||||||
|
# extra-versions: ${{ github.workspace }}/interop/hole-punching/version.json
|
||||||
|
# s3-cache-bucket: ${{ vars.S3_LIBP2P_BUILD_CACHE_BUCKET_NAME }}
|
||||||
|
# s3-access-key-id: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_ACCESS_KEY_ID }}
|
||||||
|
# s3-secret-access-key: ${{ secrets.S3_LIBP2P_BUILD_CACHE_AWS_SECRET_ACCESS_KEY }}
|
||||||
|
# aws-region: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_REGION }}
|
||||||
|
run-autonatv2-interop:
|
||||||
|
name: Run AutoNATv2 interoperability tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: docker/setup-buildx-action@v3
|
|
||||||
- name: Build image
|
- name: Set up Go
|
||||||
run: docker buildx build --load -t nim-libp2p-head -f tests/hole-punching-interop/Dockerfile .
|
uses: actions/setup-go@v5
|
||||||
- name: Run tests
|
|
||||||
uses: libp2p/test-plans/.github/actions/run-interop-hole-punch-test@master
|
|
||||||
with:
|
with:
|
||||||
test-filter: nim-libp2p-head
|
go-version: "1.25"
|
||||||
extra-versions: ${{ github.workspace }}/tests/hole-punching-interop/version.json
|
|
||||||
s3-cache-bucket: ${{ vars.S3_LIBP2P_BUILD_CACHE_BUCKET_NAME }}
|
- name: Set up Nim
|
||||||
s3-access-key-id: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_ACCESS_KEY_ID }}
|
uses: jiro4989/setup-nim-action@v1
|
||||||
s3-secret-access-key: ${{ secrets.S3_LIBP2P_BUILD_CACHE_AWS_SECRET_ACCESS_KEY }}
|
with:
|
||||||
aws-region: ${{ vars.S3_LIBP2P_BUILD_CACHE_AWS_REGION }}
|
nim-version: "stable"
|
||||||
|
|
||||||
|
- name: Run Go and Nim together
|
||||||
|
run: |
|
||||||
|
nimble install
|
||||||
|
cd interop/autonatv2/go-peer
|
||||||
|
git clone https://github.com/libp2p/go-libp2p
|
||||||
|
cd go-libp2p
|
||||||
|
git apply ../disable-filtering-of-private-ip-addresses.patch
|
||||||
|
cd ..
|
||||||
|
go run testautonatv2.go &
|
||||||
|
cd ../nim-peer
|
||||||
|
nim r src/nim_peer.nim $(cat ../go-peer/peer.id)
|
||||||
|
|||||||
21
.github/workflows/linters.yml
vendored
21
.github/workflows/linters.yml
vendored
@@ -18,17 +18,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 2 # In PR, has extra merge commit: ^1 = PR, ^2 = base
|
fetch-depth: 2 # In PR, has extra merge commit: ^1 = PR, ^2 = base
|
||||||
|
|
||||||
- name: Setup NPH
|
- name: Check `nph` formatting
|
||||||
# Pin nph to a specific version to avoid sudden style differences.
|
uses: arnetheduck/nph-action@v1
|
||||||
# Updating nph version should be accompanied with running the new version on the fluffy directory.
|
with:
|
||||||
run: |
|
version: 0.6.1
|
||||||
VERSION="v0.5.1"
|
options: "examples libp2p tests interop tools *.nim*"
|
||||||
ARCHIVE="nph-linux_x64.tar.gz"
|
fail: true
|
||||||
curl -L "https://github.com/arnetheduck/nph/releases/download/${VERSION}/${ARCHIVE}" -o ${ARCHIVE}
|
suggest: true
|
||||||
tar -xzf ${ARCHIVE}
|
|
||||||
|
|
||||||
- name: Check style
|
|
||||||
run: |
|
|
||||||
shopt -s extglob # Enable extended globbing
|
|
||||||
./nph examples libp2p tests tools *.@(nim|nims|nimble)
|
|
||||||
git diff --exit-code
|
|
||||||
|
|||||||
94
.github/workflows/performance.yml
vendored
Normal file
94
.github/workflows/performance.yml
vendored
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
name: Performance
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
merge_group:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
performance:
|
||||||
|
timeout-minutes: 20
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
env:
|
||||||
|
VACP2P: "vacp2p"
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
|
PR_NUMBER: ${{ github.event.number }}
|
||||||
|
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||||
|
MARKER: "<!-- perf-summary-marker -->"
|
||||||
|
COMMENT_SUMMARY_PATH: "/tmp/perf-summary.md"
|
||||||
|
SHARED_VOLUME_PATH: "performance/output"
|
||||||
|
DOCKER_STATS_PREFIX: "docker_stats_"
|
||||||
|
PUBLISH_BRANCH_NAME: "performance_plots"
|
||||||
|
CHECKOUT_SUBFOLDER_SUBPLOTS: "subplots"
|
||||||
|
PUBLISH_DIR_PLOTS: "plots"
|
||||||
|
CHECKOUT_SUBFOLDER_HISTORY: "history"
|
||||||
|
PUBLISH_DIR_LATENCY_HISTORY: "latency_history"
|
||||||
|
LATENCY_HISTORY_PATH: "history/latency_history"
|
||||||
|
LATENCY_HISTORY_PREFIX: "pr"
|
||||||
|
LATENCY_HISTORY_PLOT_FILENAME: "latency_history_all_scenarios.png"
|
||||||
|
|
||||||
|
name: "Performance"
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Build Docker Image with cache
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: performance/Dockerfile
|
||||||
|
tags: test-node:latest
|
||||||
|
load: true
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
- name: Run
|
||||||
|
run: |
|
||||||
|
./performance/runner.sh
|
||||||
|
|
||||||
|
- name: Process latency and docker stats
|
||||||
|
uses: ./.github/actions/process_stats
|
||||||
|
|
||||||
|
- name: Publish history
|
||||||
|
if: github.repository_owner == env.VACP2P
|
||||||
|
uses: ./.github/actions/publish_history
|
||||||
|
|
||||||
|
- name: Generate plots
|
||||||
|
if: github.repository_owner == env.VACP2P
|
||||||
|
uses: ./.github/actions/generate_plots
|
||||||
|
|
||||||
|
- name: Post/Update PR comment
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
uses: ./.github/actions/add_comment
|
||||||
|
|
||||||
|
- name: Upload performance artifacts
|
||||||
|
if: success() || failure()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: performance-artifacts
|
||||||
|
path: |
|
||||||
|
performance/output/pr*_latency.csv
|
||||||
|
performance/output/*.png
|
||||||
|
history/latency_history/*.png
|
||||||
|
if-no-files-found: ignore
|
||||||
|
retention-days: 7
|
||||||
35
.github/workflows/pr_lint.yml
vendored
Normal file
35
.github/workflows/pr_lint.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: "Conventional Commits"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- edited
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
name: Validate PR title
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- uses: amannn/action-semantic-pull-request@v5
|
||||||
|
id: lint_pr_title
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- uses: marocchino/sticky-pull-request-comment@v2
|
||||||
|
# When the previous steps fails, the workflow would stop. By adding this
|
||||||
|
# condition you can continue the execution with the populated error message.
|
||||||
|
if: always() && (steps.lint_pr_title.outputs.error_message != null)
|
||||||
|
with:
|
||||||
|
header: pr-title-lint-error
|
||||||
|
message: |
|
||||||
|
Pull requests titles must follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||||
|
|
||||||
|
# Delete a previous comment when the issue has been resolved
|
||||||
|
- if: ${{ steps.lint_pr_title.outputs.error_message == null }}
|
||||||
|
uses: marocchino/sticky-pull-request-comment@v2
|
||||||
|
with:
|
||||||
|
header: pr-title-lint-error
|
||||||
|
delete: true
|
||||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -17,3 +17,11 @@ examples/*.md
|
|||||||
nimble.develop
|
nimble.develop
|
||||||
nimble.paths
|
nimble.paths
|
||||||
go-libp2p-daemon/
|
go-libp2p-daemon/
|
||||||
|
|
||||||
|
# Ignore all test build files in tests folder (auto generated when running tests).
|
||||||
|
# First rule (`tests/**/test*[^.]*`) will ignore all binaries: has prefix test + does not have dot in name.
|
||||||
|
# Second and third rules are here to un-ignores all files with extension and Docker file,
|
||||||
|
# because it appears that vs code is skipping text search is some tests files without these rules.
|
||||||
|
tests/**/test*[^.]*
|
||||||
|
!tests/**/*.*
|
||||||
|
!tests/**/Dockerfile
|
||||||
36
.pinned
36
.pinned
@@ -1,19 +1,21 @@
|
|||||||
bearssl;https://github.com/status-im/nim-bearssl@#667b40440a53a58e9f922e29e20818720c62d9ac
|
bearssl;https://github.com/status-im/nim-bearssl@#34d712933a4e0f91f5e66bc848594a581504a215
|
||||||
chronicles;https://github.com/status-im/nim-chronicles@#32ac8679680ea699f7dbc046e8e0131cac97d41a
|
chronicles;https://github.com/status-im/nim-chronicles@#61759a5e8df8f4d68bcd1b4b8c1adab3e72bbd8d
|
||||||
chronos;https://github.com/status-im/nim-chronos@#c04576d829b8a0a1b12baaa8bc92037501b3a4a0
|
chronos;https://github.com/status-im/nim-chronos@#b55e2816eb45f698ddaca8d8473e401502562db2
|
||||||
dnsclient;https://github.com/ba0f3/dnsclient.nim@#23214235d4784d24aceed99bbfe153379ea557c8
|
dnsclient;https://github.com/ba0f3/dnsclient.nim@#23214235d4784d24aceed99bbfe153379ea557c8
|
||||||
faststreams;https://github.com/status-im/nim-faststreams@#720fc5e5c8e428d9d0af618e1e27c44b42350309
|
faststreams;https://github.com/status-im/nim-faststreams@#c51315d0ae5eb2594d0bf41181d0e1aca1b3c01d
|
||||||
httputils;https://github.com/status-im/nim-http-utils@#3b491a40c60aad9e8d3407443f46f62511e63b18
|
httputils;https://github.com/status-im/nim-http-utils@#79cbab1460f4c0cdde2084589d017c43a3d7b4f1
|
||||||
json_serialization;https://github.com/status-im/nim-json-serialization@#85b7ea093cb85ee4f433a617b97571bd709d30df
|
json_serialization;https://github.com/status-im/nim-json-serialization@#2b1c5eb11df3647a2cee107cd4cce3593cbb8bcf
|
||||||
metrics;https://github.com/status-im/nim-metrics@#6142e433fc8ea9b73379770a788017ac528d46ff
|
metrics;https://github.com/status-im/nim-metrics@#6142e433fc8ea9b73379770a788017ac528d46ff
|
||||||
ngtcp2;https://github.com/status-im/nim-ngtcp2@#6834f4756b6af58356ac9c4fef3d71db3c3ae5fe
|
ngtcp2;https://github.com/status-im/nim-ngtcp2@#9456daa178c655bccd4a3c78ad3b8cce1f0add73
|
||||||
nimcrypto;https://github.com/cheatfate/nimcrypto@#1c8d6e3caf3abc572136ae9a1da81730c4eb4288
|
nimcrypto;https://github.com/cheatfate/nimcrypto@#19c41d6be4c00b4a2c8000583bd30cf8ceb5f4b1
|
||||||
quic;https://github.com/status-im/nim-quic.git@#ddcb31ffb74b5460ab37fd13547eca90594248bc
|
quic;https://github.com/vacp2p/nim-quic@#9370190ded18d78a5a9990f57aa8cbbf947f3891
|
||||||
results;https://github.com/arnetheduck/nim-results@#f3c666a272c69d70cb41e7245e7f6844797303ad
|
results;https://github.com/arnetheduck/nim-results@#df8113dda4c2d74d460a8fa98252b0b771bf1f27
|
||||||
secp256k1;https://github.com/status-im/nim-secp256k1@#7246d91c667f4cc3759fdd50339caa45a2ecd8be
|
secp256k1;https://github.com/status-im/nim-secp256k1@#f808ed5e7a7bfc42204ec7830f14b7a42b63c284
|
||||||
serialization;https://github.com/status-im/nim-serialization@#4bdbc29e54fe54049950e352bb969aab97173b35
|
serialization;https://github.com/status-im/nim-serialization@#548d0adc9797a10b2db7f788b804330306293088
|
||||||
stew;https://github.com/status-im/nim-stew@#3159137d9a3110edb4024145ce0ba778975de40e
|
stew;https://github.com/status-im/nim-stew@#0db179256cf98eb9ce9ee7b9bc939f219e621f77
|
||||||
testutils;https://github.com/status-im/nim-testutils@#dfc4c1b39f9ded9baf6365014de2b4bfb4dafc34
|
testutils;https://github.com/status-im/nim-testutils@#9e842bd58420d23044bc55e16088e8abbe93ce51
|
||||||
unittest2;https://github.com/status-im/nim-unittest2@#2300fa9924a76e6c96bc4ea79d043e3a0f27120c
|
unittest2;https://github.com/status-im/nim-unittest2@#8b51e99b4a57fcfb31689230e75595f024543024
|
||||||
websock;https://github.com/status-im/nim-websock@#f8ed9b40a5ff27ad02a3c237c4905b0924e3f982
|
websock;https://github.com/status-im/nim-websock@#d5cd89062cd2d168ef35193c7d29d2102921d97e
|
||||||
zlib;https://github.com/status-im/nim-zlib@#38b72eda9d70067df4a953f56b5ed59630f2a17b
|
zlib;https://github.com/status-im/nim-zlib@#daa8723fd32299d4ca621c837430c29a5a11e19a
|
||||||
|
jwt;https://github.com/vacp2p/nim-jwt@#18f8378de52b241f321c1f9ea905456e89b95c6f
|
||||||
|
bearssl_pkey_decoder;https://github.com/vacp2p/bearssl_pkey_decoder@#21dd3710df9345ed2ad8bf8f882761e07863b8e0
|
||||||
|
|||||||
181
README.md
181
README.md
@@ -20,39 +20,124 @@
|
|||||||
- [Background](#background)
|
- [Background](#background)
|
||||||
- [Install](#install)
|
- [Install](#install)
|
||||||
- [Getting Started](#getting-started)
|
- [Getting Started](#getting-started)
|
||||||
- [Go-libp2p-daemon](#go-libp2p-daemon)
|
|
||||||
- [Modules](#modules)
|
|
||||||
- [Users](#users)
|
|
||||||
- [Stability](#stability)
|
|
||||||
- [Development](#development)
|
- [Development](#development)
|
||||||
- [Contribute](#contribute)
|
- [Contribute](#contribute)
|
||||||
- [Contributors](#contributors)
|
- [Contributors](#contributors)
|
||||||
- [Core Maintainers](#core-maintainers)
|
- [Core Maintainers](#core-maintainers)
|
||||||
|
- [Modules](#modules)
|
||||||
|
- [Users](#users)
|
||||||
|
- [Stability](#stability)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
## Background
|
## Background
|
||||||
libp2p is a [Peer-to-Peer](https://en.wikipedia.org/wiki/Peer-to-peer) networking stack, with [implementations](https://github.com/libp2p/libp2p#implementations) in multiple languages derived from the same [specifications.](https://github.com/libp2p/specs)
|
libp2p is a [Peer-to-Peer](https://en.wikipedia.org/wiki/Peer-to-peer) networking stack, with [implementations](https://github.com/libp2p/libp2p#implementations) in multiple languages derived from the same [specifications.](https://github.com/libp2p/specs)
|
||||||
|
|
||||||
Building large scale peer-to-peer systems has been complex and difficult in the last 15 years and libp2p is a way to fix that. It's striving to be a modular stack, with sane and secure defaults, useful protocols, while remain open and extensible.
|
Building large scale peer-to-peer systems has been complex and difficult in the last 15 years and libp2p is a way to fix that. It strives to be a modular stack with secure defaults and useful protocols, while remaining open and extensible.
|
||||||
This implementation in native Nim, relying on [chronos](https://github.com/status-im/nim-chronos) for async. It's used in production by a few [projects](#users)
|
This is a native Nim implementation, using [chronos](https://github.com/status-im/nim-chronos) for asynchronous execution. It's used in production by a few [projects](#users)
|
||||||
|
|
||||||
Learn more about libp2p at [**libp2p.io**](https://libp2p.io) and follow libp2p's documentation [**docs.libp2p.io**](https://docs.libp2p.io).
|
Learn more about libp2p at [**libp2p.io**](https://libp2p.io) and follow libp2p's documentation [**docs.libp2p.io**](https://docs.libp2p.io).
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
**Prerequisite**
|
|
||||||
- [Nim](https://nim-lang.org/install.html)
|
> The currently supported Nim versions are 2.0 and 2.2.
|
||||||
> The currently supported Nim version is 1.6.18.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
nimble install libp2p
|
nimble install libp2p
|
||||||
```
|
```
|
||||||
|
You'll find the nim-libp2p documentation [here](https://vacp2p.github.io/nim-libp2p/docs/). See [examples](./examples) for simple usage patterns.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
You'll find the nim-libp2p documentation [here](https://vacp2p.github.io/nim-libp2p/docs/).
|
Try out the chat example. Full code can be found [here](https://github.com/vacp2p/nim-libp2p/blob/master/examples/directchat.nim):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nim c -r --threads:on examples/directchat.nim
|
||||||
|
```
|
||||||
|
|
||||||
|
This will output a peer ID such as `QmbmHfVvouKammmQDJck4hz33WvVktNEe7pasxz2HgseRu` which you can use in another instance to connect to it.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./examples/directchat
|
||||||
|
/connect QmbmHfVvouKammmQDJck4hz33WvVktNEe7pasxz2HgseRu # change this hash by the hash you were given
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now chat between the instances!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Development
|
||||||
|
Clone the repository and install the dependencies:
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/vacp2p/nim-libp2p
|
||||||
|
cd nim-libp2p
|
||||||
|
nimble install -dy
|
||||||
|
```
|
||||||
|
You can use `nix develop` to start a shell with Nim and Nimble.
|
||||||
|
|
||||||
|
nimble 0.20.1 is required for running `testnative`. At time of writing, this is not available in nixpkgs: If using `nix develop`, follow up with `nimble install nimble`, and use that (typically `~/.nimble/bin/nimble`).
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
Remember you'll need to build the `go-libp2p-daemon` binary to run the `nim-libp2p` tests.
|
Run unit tests:
|
||||||
To do so, please follow the installation instructions in [daemonapi.md](examples/go-daemon/daemonapi.md).
|
```sh
|
||||||
|
# run all the unit tests
|
||||||
|
nimble test
|
||||||
|
```
|
||||||
|
|
||||||
|
For a list of all available test suites, use:
|
||||||
|
```
|
||||||
|
nimble tasks
|
||||||
|
```
|
||||||
|
|
||||||
|
### Contribute
|
||||||
|
|
||||||
|
The libp2p implementation in Nim is a work in progress. We welcome contributors to help out! Specifically, you can:
|
||||||
|
- Go through the modules and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it.
|
||||||
|
- **Perform code reviews**. Feel free to let us know if you found anything that can a) speed up the project development b) ensure better quality and c) reduce possible future bugs.
|
||||||
|
- **Add tests**. Help nim-libp2p to be more robust by adding more tests to the [tests folder](tests/).
|
||||||
|
- **Small PRs**. Try to keep PRs atomic and digestible. This makes the review process and pinpointing bugs easier.
|
||||||
|
- **Code format**. Code should be formatted with [nph](https://github.com/arnetheduck/nph) and follow the [Status Nim Style Guide](https://status-im.github.io/nim-style-guide/).
|
||||||
|
- **Join the Conversation**. Connect with other contributors in our [community channel](https://discord.com/channels/1204447718093750272/1351621032263417946). Ask questions, share ideas, get support, and stay informed about the latest updates from the maintainers.
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
<a href="https://github.com/vacp2p/nim-libp2p/graphs/contributors"><img src="https://contrib.rocks/image?repo=vacp2p/nim-libp2p" alt="nim-libp2p contributors"></a>
|
||||||
|
|
||||||
|
### Core Maintainers
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/richard-ramos"><img src="https://avatars.githubusercontent.com/u/1106587?v=4?s=100" width="100px;" alt="Richard"/><br /><sub><b>Richard</b></sub></a></td>
|
||||||
|
<td align="center"><a href="https://github.com/vladopajic"><img src="https://avatars.githubusercontent.com/u/4353513?v=4?s=100" width="100px;" alt="Vlado"/><br /><sub><b>Vlado</b></sub></a></td>
|
||||||
|
<td align="center"><a href="https://github.com/gmelodie"><img src="https://avatars.githubusercontent.com/u/8129788?v=4?s=100" width="100px;" alt="Gabe"/><br /><sub><b>Gabe</b></sub></a></td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
### Compile time flags
|
||||||
|
|
||||||
|
Enable quic transport support
|
||||||
|
```bash
|
||||||
|
nim c -d:libp2p_quic_support some_file.nim
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable autotls support
|
||||||
|
```bash
|
||||||
|
nim c -d:libp2p_autotls_support some_file.nim
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable expensive metrics (ie, metrics with per-peer cardinality):
|
||||||
|
```bash
|
||||||
|
nim c -d:libp2p_expensive_metrics some_file.nim
|
||||||
|
```
|
||||||
|
|
||||||
|
Set list of known libp2p agents for metrics:
|
||||||
|
```bash
|
||||||
|
nim c -d:libp2p_agents_metrics -d:KnownLibP2PAgents=nimbus,lighthouse,lodestar,prysm,teku some_file.nim
|
||||||
|
```
|
||||||
|
|
||||||
|
Specify gossipsub specific topics to measure in the metrics:
|
||||||
|
```bash
|
||||||
|
nim c -d:KnownLibP2PTopics=topic1,topic2,topic3 some_file.nim
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
List of packages modules implemented in nim-libp2p:
|
List of packages modules implemented in nim-libp2p:
|
||||||
@@ -64,12 +149,12 @@ List of packages modules implemented in nim-libp2p:
|
|||||||
| [connmanager](libp2p/connmanager.nim) | Connection manager |
|
| [connmanager](libp2p/connmanager.nim) | Connection manager |
|
||||||
| [identify / push identify](libp2p/protocols/identify.nim) | [Identify](https://docs.libp2p.io/concepts/fundamentals/protocols/#identify) protocol |
|
| [identify / push identify](libp2p/protocols/identify.nim) | [Identify](https://docs.libp2p.io/concepts/fundamentals/protocols/#identify) protocol |
|
||||||
| [ping](libp2p/protocols/ping.nim) | [Ping](https://docs.libp2p.io/concepts/fundamentals/protocols/#ping) protocol |
|
| [ping](libp2p/protocols/ping.nim) | [Ping](https://docs.libp2p.io/concepts/fundamentals/protocols/#ping) protocol |
|
||||||
| [libp2p-daemon-client](libp2p/daemon/daemonapi.nim) | [go-daemon](https://github.com/libp2p/go-libp2p-daemon) nim wrapper |
|
|
||||||
| [interop-libp2p](tests/testinterop.nim) | Interop tests |
|
|
||||||
| **Transports** | |
|
| **Transports** | |
|
||||||
| [libp2p-tcp](libp2p/transports/tcptransport.nim) | TCP transport |
|
| [libp2p-tcp](libp2p/transports/tcptransport.nim) | TCP transport |
|
||||||
| [libp2p-ws](libp2p/transports/wstransport.nim) | WebSocket & WebSocket Secure transport |
|
| [libp2p-ws](libp2p/transports/wstransport.nim) | WebSocket & WebSocket Secure transport |
|
||||||
| [libp2p-tor](libp2p/transports/tortransport.nim) | Tor Transport |
|
| [libp2p-tor](libp2p/transports/tortransport.nim) | Tor Transport |
|
||||||
|
| [libp2p-quic](libp2p/transports/quictransport.nim) | Quic Transport |
|
||||||
|
| [libp2p-memory](libp2p/transports/memorytransport.nim) | Memory Transport |
|
||||||
| **Secure Channels** | |
|
| **Secure Channels** | |
|
||||||
| [libp2p-noise](libp2p/protocols/secure/noise.nim) | [Noise](https://docs.libp2p.io/concepts/secure-comm/noise/) secure channel |
|
| [libp2p-noise](libp2p/protocols/secure/noise.nim) | [Noise](https://docs.libp2p.io/concepts/secure-comm/noise/) secure channel |
|
||||||
| [libp2p-plaintext](libp2p/protocols/secure/plaintext.nim) | Plain Text for development purposes |
|
| [libp2p-plaintext](libp2p/protocols/secure/plaintext.nim) | Plain Text for development purposes |
|
||||||
@@ -78,10 +163,10 @@ List of packages modules implemented in nim-libp2p:
|
|||||||
| [libp2p-yamux](libp2p/muxers/yamux/yamux.nim) | [Yamux](https://docs.libp2p.io/concepts/multiplex/yamux/) multiplexer |
|
| [libp2p-yamux](libp2p/muxers/yamux/yamux.nim) | [Yamux](https://docs.libp2p.io/concepts/multiplex/yamux/) multiplexer |
|
||||||
| **Data Types** | |
|
| **Data Types** | |
|
||||||
| [peer-id](libp2p/peerid.nim) | [Cryptographic identifiers](https://docs.libp2p.io/concepts/fundamentals/peers/#peer-id) |
|
| [peer-id](libp2p/peerid.nim) | [Cryptographic identifiers](https://docs.libp2p.io/concepts/fundamentals/peers/#peer-id) |
|
||||||
| [peer-store](libp2p/peerstore.nim) | ["Address book" of known peers](https://docs.libp2p.io/concepts/fundamentals/peers/#peer-store) |
|
| [peer-store](libp2p/peerstore.nim) | [Address book of known peers](https://docs.libp2p.io/concepts/fundamentals/peers/#peer-store) |
|
||||||
| [multiaddress](libp2p/multiaddress.nim) | [Composable network addresses](https://github.com/multiformats/multiaddr) |
|
| [multiaddress](libp2p/multiaddress.nim) | [Composable network addresses](https://github.com/multiformats/multiaddr) |
|
||||||
| [signed envelope](libp2p/signed_envelope.nim) | [Signed generic data container](https://github.com/libp2p/specs/blob/master/RFC/0002-signed-envelopes.md) |
|
| [signed-envelope](libp2p/signed_envelope.nim) | [Signed generic data container](https://github.com/libp2p/specs/blob/master/RFC/0002-signed-envelopes.md) |
|
||||||
| [routing record](libp2p/routing_record.nim) | [Signed peer dialing informations](https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md) |
|
| [routing-record](libp2p/routing_record.nim) | [Signed peer dialing informations](https://github.com/libp2p/specs/blob/master/RFC/0003-routing-records.md) |
|
||||||
| [discovery manager](libp2p/discovery/discoverymngr.nim) | Discovery Manager |
|
| [discovery manager](libp2p/discovery/discoverymngr.nim) | Discovery Manager |
|
||||||
| **Utilities** | |
|
| **Utilities** | |
|
||||||
| [libp2p-crypto](libp2p/crypto) | Cryptographic backend |
|
| [libp2p-crypto](libp2p/crypto) | Cryptographic backend |
|
||||||
@@ -107,67 +192,7 @@ The versioning follows [semver](https://semver.org/), with some additions:
|
|||||||
- Some of libp2p procedures are marked as `.public.`, they will remain compatible during each `MAJOR` version
|
- Some of libp2p procedures are marked as `.public.`, they will remain compatible during each `MAJOR` version
|
||||||
- The rest of the procedures are considered internal, and can change at any `MINOR` version (but remain compatible for each new `PATCH`)
|
- The rest of the procedures are considered internal, and can change at any `MINOR` version (but remain compatible for each new `PATCH`)
|
||||||
|
|
||||||
We aim to be compatible at all time with at least 2 Nim `MINOR` versions, currently `1.6 & 2.0`
|
We aim to be compatible at all time with at least 2 Nim `MINOR` versions, currently `2.0 & 2.2`
|
||||||
|
|
||||||
## Development
|
|
||||||
Clone and Install dependencies:
|
|
||||||
```sh
|
|
||||||
git clone https://github.com/vacp2p/nim-libp2p
|
|
||||||
cd nim-libp2p
|
|
||||||
# to use dependencies computed by nimble
|
|
||||||
nimble install -dy
|
|
||||||
# OR to install the dependencies versions used in CI
|
|
||||||
nimble install_pinned
|
|
||||||
```
|
|
||||||
|
|
||||||
Run unit tests:
|
|
||||||
```sh
|
|
||||||
# run all the unit tests
|
|
||||||
nimble test
|
|
||||||
```
|
|
||||||
This requires the go daemon to be available. To only run native tests, use `nimble testnative`.
|
|
||||||
Or use `nimble tasks` to show all available tasks.
|
|
||||||
|
|
||||||
### Contribute
|
|
||||||
|
|
||||||
The libp2p implementation in Nim is a work in progress. We welcome contributors to help out! Specifically, you can:
|
|
||||||
- Go through the modules and **check out existing issues**. This would be especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it.
|
|
||||||
- **Perform code reviews**. Feel free to let us know if you found anything that can a) speed up the project development b) ensure better quality and c) reduce possible future bugs.
|
|
||||||
- **Add tests**. Help nim-libp2p to be more robust by adding more tests to the [tests folder](tests/).
|
|
||||||
- **Small PRs**. Try to keep PRs atomic and digestible. This makes the review process and pinpointing bugs easier.
|
|
||||||
- **Code format**. Please format code using [nph](https://github.com/arnetheduck/nph) v0.5.1. This will ensure a consistent codebase and make PRs easier to review. A CI rule has been added to ensure that future commits are all formatted using the same nph version.
|
|
||||||
The code follows the [Status Nim Style Guide](https://status-im.github.io/nim-style-guide/).
|
|
||||||
|
|
||||||
### Contributors
|
|
||||||
<a href="https://github.com/vacp2p/nim-libp2p/graphs/contributors"><img src="https://contrib.rocks/image?repo=vacp2p/nim-libp2p" alt="nim-libp2p contributors"></a>
|
|
||||||
|
|
||||||
### Core Maintainers
|
|
||||||
<table>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/Menduist"><img src="https://avatars.githubusercontent.com/u/13471753?v=4?s=100" width="100px;" alt="Tanguy"/><br /><sub><b>Tanguy (Menduist)</b></sub></a></td>
|
|
||||||
<td align="center"><a href="https://github.com/lchenut"><img src="https://avatars.githubusercontent.com/u/11214565?v=4?s=100" width="100px;" alt="Ludovic"/><br /><sub><b>Ludovic</b></sub></a></td>
|
|
||||||
<td align="center"><a href="https://github.com/diegomrsantos"><img src="https://avatars.githubusercontent.com/u/7316595?v=4?s=100" width="100px;" alt="Diego"/><br /><sub><b>Diego</b></sub></a></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
### Compile time flags
|
|
||||||
|
|
||||||
Enable expensive metrics (ie, metrics with per-peer cardinality):
|
|
||||||
```bash
|
|
||||||
nim c -d:libp2p_expensive_metrics some_file.nim
|
|
||||||
```
|
|
||||||
|
|
||||||
Set list of known libp2p agents for metrics:
|
|
||||||
```bash
|
|
||||||
nim c -d:libp2p_agents_metrics -d:KnownLibP2PAgents=nimbus,lighthouse,lodestar,prysm,teku some_file.nim
|
|
||||||
```
|
|
||||||
|
|
||||||
Specify gossipsub specific topics to measure in the metrics:
|
|
||||||
```bash
|
|
||||||
nim c -d:KnownLibP2PTopics=topic1,topic2,topic3 some_file.nim
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ if dirExists("nimbledeps/pkgs"):
|
|||||||
if dirExists("nimbledeps/pkgs2"):
|
if dirExists("nimbledeps/pkgs2"):
|
||||||
switch("NimblePath", "nimbledeps/pkgs2")
|
switch("NimblePath", "nimbledeps/pkgs2")
|
||||||
|
|
||||||
|
switch("warningAsError", "UnusedImport:on")
|
||||||
|
switch("warningAsError", "UseBase:on")
|
||||||
switch("warning", "CaseTransition:off")
|
switch("warning", "CaseTransition:off")
|
||||||
switch("warning", "ObservableStores:off")
|
switch("warning", "ObservableStores:off")
|
||||||
switch("warning", "LockLevel:off")
|
switch("warning", "LockLevel:off")
|
||||||
|
|
||||||
--styleCheck:
|
--styleCheck:
|
||||||
usages
|
usages
|
||||||
switch("warningAsError", "UseBase:on")
|
|
||||||
--styleCheck:
|
--styleCheck:
|
||||||
error
|
error
|
||||||
--mm:
|
--mm:
|
||||||
@@ -22,7 +24,7 @@ if defined(windows) and not defined(vcc):
|
|||||||
--define:
|
--define:
|
||||||
nimRawSetjmp
|
nimRawSetjmp
|
||||||
|
|
||||||
# begin Nimble config (version 1)
|
# begin Nimble config (version 2)
|
||||||
when fileExists("nimble.paths"):
|
when withDir(thisDir(), system.fileExists("nimble.paths")):
|
||||||
include "nimble.paths"
|
include "nimble.paths"
|
||||||
# end Nimble config
|
# end Nimble config
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# nim-libp2p examples
|
# nim-libp2p examples
|
||||||
|
|
||||||
In this folder, you'll find the sources of the [nim-libp2p website](https://status-im.github.io/nim-libp2p/docs/)
|
In this folder, you'll find the sources of the [nim-libp2p website](https://vacp2p.github.io/nim-libp2p/docs/)
|
||||||
|
|
||||||
We recommand to follow the tutorials on the website, but feel free to grok the sources here!
|
We recommand to follow the tutorials on the website, but feel free to grok the sources here!
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{.used.}
|
||||||
## # Circuit Relay example
|
## # Circuit Relay example
|
||||||
##
|
##
|
||||||
## Circuit Relay can be used when a node cannot reach another node
|
## Circuit Relay can be used when a node cannot reach another node
|
||||||
@@ -26,15 +27,22 @@ proc main() {.async.} =
|
|||||||
let customProtoCodec = "/test"
|
let customProtoCodec = "/test"
|
||||||
var proto = new LPProtocol
|
var proto = new LPProtocol
|
||||||
proto.codec = customProtoCodec
|
proto.codec = customProtoCodec
|
||||||
proto.handler = proc(conn: Connection, proto: string) {.async.} =
|
proto.handler = proc(
|
||||||
var msg = string.fromBytes(await conn.readLp(1024))
|
conn: Connection, proto: string
|
||||||
echo "1 - Dst Received: ", msg
|
) {.async: (raises: [CancelledError]).} =
|
||||||
assert "test1" == msg
|
try:
|
||||||
await conn.writeLp("test2")
|
var msg = string.fromBytes(await conn.readLp(1024))
|
||||||
msg = string.fromBytes(await conn.readLp(1024))
|
echo "1 - Dst Received: ", msg
|
||||||
echo "2 - Dst Received: ", msg
|
assert "test1" == msg
|
||||||
assert "test3" == msg
|
await conn.writeLp("test2")
|
||||||
await conn.writeLp("test4")
|
msg = string.fromBytes(await conn.readLp(1024))
|
||||||
|
echo "2 - Dst Received: ", msg
|
||||||
|
assert "test3" == msg
|
||||||
|
await conn.writeLp("test4")
|
||||||
|
except CancelledError as e:
|
||||||
|
raise e
|
||||||
|
except CatchableError as e:
|
||||||
|
echo "exception in handler", e.msg
|
||||||
|
|
||||||
let
|
let
|
||||||
relay = Relay.new()
|
relay = Relay.new()
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{.used.}
|
||||||
when not (compileOption("threads")):
|
when not (compileOption("threads")):
|
||||||
{.fatal: "Please, compile this program with the --threads:on option!".}
|
{.fatal: "Please, compile this program with the --threads:on option!".}
|
||||||
|
|
||||||
@@ -43,12 +44,17 @@ proc new(T: typedesc[ChatProto], c: Chat): T =
|
|||||||
let chatproto = T()
|
let chatproto = T()
|
||||||
|
|
||||||
# create handler for incoming connection
|
# create handler for incoming connection
|
||||||
proc handle(stream: Connection, proto: string) {.async.} =
|
proc handle(stream: Connection, proto: string) {.async: (raises: [CancelledError]).} =
|
||||||
if c.connected and not c.conn.closed:
|
try:
|
||||||
c.writeStdout "a chat session is already in progress - refusing incoming peer!"
|
if c.connected and not c.conn.closed:
|
||||||
await stream.close()
|
c.writeStdout "a chat session is already in progress - refusing incoming peer!"
|
||||||
else:
|
else:
|
||||||
await c.handlePeer(stream)
|
await c.handlePeer(stream)
|
||||||
|
except CancelledError as e:
|
||||||
|
raise e
|
||||||
|
except CatchableError as e:
|
||||||
|
echo "exception in handler", e.msg
|
||||||
|
finally:
|
||||||
await stream.close()
|
await stream.close()
|
||||||
|
|
||||||
# assign the new handler
|
# assign the new handler
|
||||||
|
|||||||
3
examples/examples_build.nim
Normal file
3
examples/examples_build.nim
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{.used.}
|
||||||
|
|
||||||
|
import directchat, tutorial_6_game
|
||||||
5
examples/examples_run.nim
Normal file
5
examples/examples_run.nim
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{.used.}
|
||||||
|
|
||||||
|
import
|
||||||
|
helloworld, circuitrelay, tutorial_1_connect, tutorial_2_customproto,
|
||||||
|
tutorial_3_protobuf, tutorial_4_gossipsub, tutorial_5_discovery
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import chronos, nimcrypto, strutils
|
|
||||||
import ../../libp2p/daemon/daemonapi
|
|
||||||
import ../hexdump
|
|
||||||
|
|
||||||
const PubSubTopic = "test-net"
|
|
||||||
|
|
||||||
proc dumpSubscribedPeers(api: DaemonAPI) {.async.} =
|
|
||||||
var peers = await api.pubsubListPeers(PubSubTopic)
|
|
||||||
echo "= List of connected and subscribed peers:"
|
|
||||||
for item in peers:
|
|
||||||
echo item.pretty()
|
|
||||||
|
|
||||||
proc dumpAllPeers(api: DaemonAPI) {.async.} =
|
|
||||||
var peers = await api.listPeers()
|
|
||||||
echo "Current connected peers count = ", len(peers)
|
|
||||||
for item in peers:
|
|
||||||
echo item.peer.pretty()
|
|
||||||
|
|
||||||
proc monitor(api: DaemonAPI) {.async.} =
|
|
||||||
while true:
|
|
||||||
echo "Dumping all peers"
|
|
||||||
await dumpAllPeers(api)
|
|
||||||
await sleepAsync(5000)
|
|
||||||
|
|
||||||
proc main() {.async.} =
|
|
||||||
echo "= Starting P2P bootnode"
|
|
||||||
var api = await newDaemonApi({DHTFull, PSGossipSub})
|
|
||||||
var id = await api.identity()
|
|
||||||
echo "= P2P bootnode ", id.peer.pretty(), " started."
|
|
||||||
let mcip4 = multiCodec("ip4")
|
|
||||||
let mcip6 = multiCodec("ip6")
|
|
||||||
echo "= You can use one of this addresses to bootstrap your nodes:"
|
|
||||||
for item in id.addresses:
|
|
||||||
if item.protoCode() == mcip4 or item.protoCode() == mcip6:
|
|
||||||
echo $item & "/ipfs/" & id.peer.pretty()
|
|
||||||
|
|
||||||
asyncSpawn monitor(api)
|
|
||||||
|
|
||||||
proc pubsubLogger(
|
|
||||||
api: DaemonAPI, ticket: PubsubTicket, message: PubSubMessage
|
|
||||||
): Future[bool] {.async.} =
|
|
||||||
let msglen = len(message.data)
|
|
||||||
echo "= Recieved pubsub message with length ",
|
|
||||||
msglen, " bytes from peer ", message.peer.pretty()
|
|
||||||
echo dumpHex(message.data)
|
|
||||||
await api.dumpSubscribedPeers()
|
|
||||||
result = true
|
|
||||||
|
|
||||||
var ticket = await api.pubsubSubscribe(PubSubTopic, pubsubLogger)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
waitFor(main())
|
|
||||||
while true:
|
|
||||||
poll()
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
import chronos, nimcrypto, strutils
|
|
||||||
import ../../libp2p/daemon/daemonapi
|
|
||||||
|
|
||||||
## nim c -r --threads:on chat.nim
|
|
||||||
when not (compileOption("threads")):
|
|
||||||
{.fatal: "Please, compile this program with the --threads:on option!".}
|
|
||||||
|
|
||||||
const ServerProtocols = @["/test-chat-stream"]
|
|
||||||
|
|
||||||
type CustomData = ref object
|
|
||||||
api: DaemonAPI
|
|
||||||
remotes: seq[StreamTransport]
|
|
||||||
consoleFd: AsyncFD
|
|
||||||
serveFut: Future[void]
|
|
||||||
|
|
||||||
proc threadMain(wfd: AsyncFD) {.thread.} =
|
|
||||||
## This procedure performs reading from `stdin` and sends data over
|
|
||||||
## pipe to main thread.
|
|
||||||
var transp = fromPipe(wfd)
|
|
||||||
|
|
||||||
while true:
|
|
||||||
var line = stdin.readLine()
|
|
||||||
let res = waitFor transp.write(line & "\r\n")
|
|
||||||
|
|
||||||
proc serveThread(udata: CustomData) {.async.} =
|
|
||||||
## This procedure perform reading on pipe and sends data to remote clients.
|
|
||||||
var transp = fromPipe(udata.consoleFd)
|
|
||||||
|
|
||||||
proc remoteReader(transp: StreamTransport) {.async.} =
|
|
||||||
while true:
|
|
||||||
var line = await transp.readLine()
|
|
||||||
if len(line) == 0:
|
|
||||||
break
|
|
||||||
echo ">> ", line
|
|
||||||
|
|
||||||
while true:
|
|
||||||
try:
|
|
||||||
var line = await transp.readLine()
|
|
||||||
if line.startsWith("/connect"):
|
|
||||||
var parts = line.split(" ")
|
|
||||||
if len(parts) == 2:
|
|
||||||
var peerId = PeerId.init(parts[1])
|
|
||||||
var address = MultiAddress.init(multiCodec("p2p-circuit"))
|
|
||||||
address &= MultiAddress.init(multiCodec("p2p"), peerId)
|
|
||||||
echo "= Searching for peer ", peerId.pretty()
|
|
||||||
var id = await udata.api.dhtFindPeer(peerId)
|
|
||||||
echo "= Peer " & parts[1] & " found at addresses:"
|
|
||||||
for item in id.addresses:
|
|
||||||
echo $item
|
|
||||||
echo "= Connecting to peer ", $address
|
|
||||||
await udata.api.connect(peerId, @[address], 30)
|
|
||||||
echo "= Opening stream to peer chat ", parts[1]
|
|
||||||
var stream = await udata.api.openStream(peerId, ServerProtocols)
|
|
||||||
udata.remotes.add(stream.transp)
|
|
||||||
echo "= Connected to peer chat ", parts[1]
|
|
||||||
asyncSpawn remoteReader(stream.transp)
|
|
||||||
elif line.startsWith("/search"):
|
|
||||||
var parts = line.split(" ")
|
|
||||||
if len(parts) == 2:
|
|
||||||
var peerId = PeerId.init(parts[1])
|
|
||||||
echo "= Searching for peer ", peerId.pretty()
|
|
||||||
var id = await udata.api.dhtFindPeer(peerId)
|
|
||||||
echo "= Peer " & parts[1] & " found at addresses:"
|
|
||||||
for item in id.addresses:
|
|
||||||
echo $item
|
|
||||||
elif line.startsWith("/consearch"):
|
|
||||||
var parts = line.split(" ")
|
|
||||||
if len(parts) == 2:
|
|
||||||
var peerId = PeerId.init(parts[1])
|
|
||||||
echo "= Searching for peers connected to peer ", parts[1]
|
|
||||||
var peers = await udata.api.dhtFindPeersConnectedToPeer(peerId)
|
|
||||||
echo "= Found ", len(peers), " connected to peer ", parts[1]
|
|
||||||
for item in peers:
|
|
||||||
var peer = item.peer
|
|
||||||
var addresses = newSeq[string]()
|
|
||||||
var relay = false
|
|
||||||
for a in item.addresses:
|
|
||||||
addresses.add($a)
|
|
||||||
if a.protoName() == "/p2p-circuit":
|
|
||||||
relay = true
|
|
||||||
break
|
|
||||||
if relay:
|
|
||||||
echo peer.pretty(), " * ", " [", addresses.join(", "), "]"
|
|
||||||
else:
|
|
||||||
echo peer.pretty(), " [", addresses.join(", "), "]"
|
|
||||||
elif line.startsWith("/exit"):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
var msg = line & "\r\n"
|
|
||||||
echo "<< ", line
|
|
||||||
var pending = newSeq[Future[int]]()
|
|
||||||
for item in udata.remotes:
|
|
||||||
pending.add(item.write(msg))
|
|
||||||
if len(pending) > 0:
|
|
||||||
var results = await all(pending)
|
|
||||||
except:
|
|
||||||
echo getCurrentException().msg
|
|
||||||
|
|
||||||
proc main() {.async.} =
|
|
||||||
var data = new CustomData
|
|
||||||
data.remotes = newSeq[StreamTransport]()
|
|
||||||
|
|
||||||
var (rfd, wfd) = createAsyncPipe()
|
|
||||||
if rfd == asyncInvalidPipe or wfd == asyncInvalidPipe:
|
|
||||||
raise newException(ValueError, "Could not initialize pipe!")
|
|
||||||
|
|
||||||
data.consoleFd = rfd
|
|
||||||
|
|
||||||
data.serveFut = serveThread(data)
|
|
||||||
var thread: Thread[AsyncFD]
|
|
||||||
thread.createThread(threadMain, wfd)
|
|
||||||
|
|
||||||
echo "= Starting P2P node"
|
|
||||||
data.api = await newDaemonApi({DHTFull, Bootstrap})
|
|
||||||
await sleepAsync(3000)
|
|
||||||
var id = await data.api.identity()
|
|
||||||
|
|
||||||
proc streamHandler(api: DaemonAPI, stream: P2PStream) {.async.} =
|
|
||||||
echo "= Peer ", stream.peer.pretty(), " joined chat"
|
|
||||||
data.remotes.add(stream.transp)
|
|
||||||
while true:
|
|
||||||
var line = await stream.transp.readLine()
|
|
||||||
if len(line) == 0:
|
|
||||||
break
|
|
||||||
echo ">> ", line
|
|
||||||
|
|
||||||
await data.api.addHandler(ServerProtocols, streamHandler)
|
|
||||||
echo "= Your PeerId is ", id.peer.pretty()
|
|
||||||
await data.serveFut
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
waitFor(main())
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# Table of Contents
|
|
||||||
- [Introduction](#introduction)
|
|
||||||
- [Prerequisites](#prerequisites)
|
|
||||||
- [Installation](#installation)
|
|
||||||
- [Script](#script)
|
|
||||||
- [Usage](#usage)
|
|
||||||
- [Example](#example)
|
|
||||||
- [Getting Started](#getting-started)
|
|
||||||
|
|
||||||
# Introduction
|
|
||||||
This is a libp2p-backed daemon wrapping the functionalities of go-libp2p for use in Nim. <br>
|
|
||||||
For more information about the go daemon, check out [this repository](https://github.com/libp2p/go-libp2p-daemon).
|
|
||||||
> **Required only** for running the tests.
|
|
||||||
|
|
||||||
# Prerequisites
|
|
||||||
Go with version `1.15.15`.
|
|
||||||
> You will *likely* be able to build `go-libp2p-daemon` with different Go versions, but **they haven't been tested**.
|
|
||||||
|
|
||||||
# Installation
|
|
||||||
Follow one of the methods below:
|
|
||||||
|
|
||||||
## Script
|
|
||||||
Run the build script while having the `go` command pointing to the correct Go version.
|
|
||||||
We recommend using `1.15.15`, as previously stated.
|
|
||||||
```sh
|
|
||||||
./scripts/build_p2pd.sh
|
|
||||||
```
|
|
||||||
If everything goes correctly, the binary (`p2pd`) should be built and placed in the correct directory.
|
|
||||||
If you find any issues, please head into our discord and ask for our assistance.
|
|
||||||
|
|
||||||
After successfully building the binary, remember to add it to your path so it can be found. You can do that by running:
|
|
||||||
```sh
|
|
||||||
export PATH="$PATH:$HOME/go/bin"
|
|
||||||
```
|
|
||||||
> **Tip:** To make this change permanent, add the command above to your `.bashrc` file.
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
|
|
||||||
## Example
|
|
||||||
Examples can be found in the [examples folder](https://github.com/status-im/nim-libp2p/tree/readme/examples/go-daemon)
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
Try out the chat example. Full code can be found [here](https://github.com/status-im/nim-libp2p/blob/master/examples/chat.nim):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nim c -r --threads:on examples/directchat.nim
|
|
||||||
```
|
|
||||||
|
|
||||||
This will output a peer ID such as `QmbmHfVvouKammmQDJck4hz33WvVktNEe7pasxz2HgseRu` which you can use in another instance to connect to it.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./examples/directchat
|
|
||||||
/connect QmbmHfVvouKammmQDJck4hz33WvVktNEe7pasxz2HgseRu
|
|
||||||
```
|
|
||||||
|
|
||||||
You can now chat between the instances!
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import chronos, nimcrypto, strutils, os
|
|
||||||
import ../../libp2p/daemon/daemonapi
|
|
||||||
|
|
||||||
const PubSubTopic = "test-net"
|
|
||||||
|
|
||||||
proc main(bn: string) {.async.} =
|
|
||||||
echo "= Starting P2P node"
|
|
||||||
var bootnodes = bn.split(",")
|
|
||||||
var api = await newDaemonApi(
|
|
||||||
{DHTFull, PSGossipSub, WaitBootstrap}, bootstrapNodes = bootnodes, peersRequired = 1
|
|
||||||
)
|
|
||||||
var id = await api.identity()
|
|
||||||
echo "= P2P node ", id.peer.pretty(), " started:"
|
|
||||||
for item in id.addresses:
|
|
||||||
echo item
|
|
||||||
|
|
||||||
proc pubsubLogger(
|
|
||||||
api: DaemonAPI, ticket: PubsubTicket, message: PubSubMessage
|
|
||||||
): Future[bool] {.async.} =
|
|
||||||
let msglen = len(message.data)
|
|
||||||
echo "= Recieved pubsub message with length ",
|
|
||||||
msglen, " bytes from peer ", message.peer.pretty(), ": "
|
|
||||||
var strdata = cast[string](message.data)
|
|
||||||
echo strdata
|
|
||||||
result = true
|
|
||||||
|
|
||||||
var ticket = await api.pubsubSubscribe(PubSubTopic, pubsubLogger)
|
|
||||||
|
|
||||||
# Waiting for gossipsub interval
|
|
||||||
while true:
|
|
||||||
var peers = await api.pubsubListPeers(PubSubTopic)
|
|
||||||
if len(peers) > 0:
|
|
||||||
break
|
|
||||||
await sleepAsync(1000)
|
|
||||||
|
|
||||||
var data = "HELLO\r\n"
|
|
||||||
var msgData = cast[seq[byte]](data)
|
|
||||||
await api.pubsubPublish(PubSubTopic, msgData)
|
|
||||||
|
|
||||||
when isMainModule:
|
|
||||||
if paramCount() != 1:
|
|
||||||
echo "Please supply bootnodes!"
|
|
||||||
else:
|
|
||||||
waitFor(main(paramStr(1)))
|
|
||||||
while true:
|
|
||||||
poll()
|
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
{.used.}
|
||||||
|
|
||||||
import chronos # an efficient library for async
|
import chronos # an efficient library for async
|
||||||
import stew/byteutils # various utils
|
import stew/byteutils # various utils
|
||||||
import libp2p
|
import libp2p
|
||||||
@@ -11,12 +13,17 @@ type TestProto = ref object of LPProtocol # declare a custom protocol
|
|||||||
|
|
||||||
proc new(T: typedesc[TestProto]): T =
|
proc new(T: typedesc[TestProto]): T =
|
||||||
# every incoming connections will be in handled in this closure
|
# every incoming connections will be in handled in this closure
|
||||||
proc handle(conn: Connection, proto: string) {.async.} =
|
proc handle(conn: Connection, proto: string) {.async: (raises: [CancelledError]).} =
|
||||||
echo "Got from remote - ", string.fromBytes(await conn.readLp(1024))
|
try:
|
||||||
await conn.writeLp("Roger p2p!")
|
echo "Got from remote - ", string.fromBytes(await conn.readLp(1024))
|
||||||
|
await conn.writeLp("Roger p2p!")
|
||||||
# We must close the connections ourselves when we're done with it
|
except CancelledError as e:
|
||||||
await conn.close()
|
raise e
|
||||||
|
except CatchableError as e:
|
||||||
|
echo "exception in handler", e.msg
|
||||||
|
finally:
|
||||||
|
# We must close the connections ourselves when we're done with it
|
||||||
|
await conn.close()
|
||||||
|
|
||||||
return T.new(codecs = @[TestCodec], handler = handle)
|
return T.new(codecs = @[TestCodec], handler = handle)
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,4 @@
|
|||||||
Welcome to the nim-libp2p documentation!
|
Welcome to the nim-libp2p documentation!
|
||||||
|
|
||||||
Here, you'll find [tutorials](tutorial_1_connect.md) to help you get started, as well as
|
Here, you'll find [tutorials](tutorial_1_connect.md) to help you get started, as well as
|
||||||
the [full reference](https://status-im.github.io/nim-libp2p/master/libp2p.html).
|
the [full reference](https://vacp2p.github.io/nim-libp2p/master/libp2p.html).
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{.used.}
|
||||||
## # Simple ping tutorial
|
## # Simple ping tutorial
|
||||||
##
|
##
|
||||||
## Hi all, welcome to the first nim-libp2p tutorial!
|
## Hi all, welcome to the first nim-libp2p tutorial!
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{.used.}
|
||||||
## # Custom protocol in libp2p
|
## # Custom protocol in libp2p
|
||||||
##
|
##
|
||||||
## In the [previous tutorial](tutorial_1_connect.md), we've looked at how to create a simple ping program using the `nim-libp2p`.
|
## In the [previous tutorial](tutorial_1_connect.md), we've looked at how to create a simple ping program using the `nim-libp2p`.
|
||||||
@@ -25,12 +26,17 @@ type TestProto = ref object of LPProtocol
|
|||||||
|
|
||||||
proc new(T: typedesc[TestProto]): T =
|
proc new(T: typedesc[TestProto]): T =
|
||||||
# every incoming connections will in be handled in this closure
|
# every incoming connections will in be handled in this closure
|
||||||
proc handle(conn: Connection, proto: string) {.async.} =
|
proc handle(conn: Connection, proto: string) {.async: (raises: [CancelledError]).} =
|
||||||
# Read up to 1024 bytes from this connection, and transform them into
|
# Read up to 1024 bytes from this connection, and transform them into
|
||||||
# a string
|
# a string
|
||||||
echo "Got from remote - ", string.fromBytes(await conn.readLp(1024))
|
try:
|
||||||
# We must close the connections ourselves when we're done with it
|
echo "Got from remote - ", string.fromBytes(await conn.readLp(1024))
|
||||||
await conn.close()
|
except CancelledError as e:
|
||||||
|
raise e
|
||||||
|
except CatchableError as e:
|
||||||
|
echo "exception in handler", e.msg
|
||||||
|
finally:
|
||||||
|
await conn.close()
|
||||||
|
|
||||||
return T.new(codecs = @[TestCodec], handler = handle)
|
return T.new(codecs = @[TestCodec], handler = handle)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{.used.}
|
||||||
## # Protobuf usage
|
## # Protobuf usage
|
||||||
##
|
##
|
||||||
## In the [previous tutorial](tutorial_2_customproto.md), we created a simple "ping" protocol.
|
## In the [previous tutorial](tutorial_2_customproto.md), we created a simple "ping" protocol.
|
||||||
@@ -108,12 +109,18 @@ type
|
|||||||
|
|
||||||
proc new(_: typedesc[MetricProto], cb: MetricCallback): MetricProto =
|
proc new(_: typedesc[MetricProto], cb: MetricCallback): MetricProto =
|
||||||
var res: MetricProto
|
var res: MetricProto
|
||||||
proc handle(conn: Connection, proto: string) {.async.} =
|
proc handle(conn: Connection, proto: string) {.async: (raises: [CancelledError]).} =
|
||||||
let
|
try:
|
||||||
metrics = await res.metricGetter()
|
let
|
||||||
asProtobuf = metrics.encode()
|
metrics = await res.metricGetter()
|
||||||
await conn.writeLp(asProtobuf.buffer)
|
asProtobuf = metrics.encode()
|
||||||
await conn.close()
|
await conn.writeLp(asProtobuf.buffer)
|
||||||
|
except CancelledError as e:
|
||||||
|
raise e
|
||||||
|
except CatchableError as e:
|
||||||
|
echo "exception in handler", e.msg
|
||||||
|
finally:
|
||||||
|
await conn.close()
|
||||||
|
|
||||||
res = MetricProto.new(@["/metric-getter/1.0.0"], handle)
|
res = MetricProto.new(@["/metric-getter/1.0.0"], handle)
|
||||||
res.metricGetter = cb
|
res.metricGetter = cb
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{.used.}
|
||||||
## # GossipSub
|
## # GossipSub
|
||||||
##
|
##
|
||||||
## In this tutorial, we'll build a simple GossipSub network
|
## In this tutorial, we'll build a simple GossipSub network
|
||||||
@@ -79,8 +80,7 @@ proc oneNode(node: Node, rng: ref HmacDrbgContext) {.async.} =
|
|||||||
let decoded = MetricList.decode(message.data)
|
let decoded = MetricList.decode(message.data)
|
||||||
if decoded.isErr:
|
if decoded.isErr:
|
||||||
return ValidationResult.Reject
|
return ValidationResult.Reject
|
||||||
return ValidationResult.Accept
|
return ValidationResult.Accept,
|
||||||
,
|
|
||||||
)
|
)
|
||||||
# This "validator" will attach to the `metrics` topic and make sure
|
# This "validator" will attach to the `metrics` topic and make sure
|
||||||
# that every message in this topic is valid. This allows us to stop
|
# that every message in this topic is valid. This allows us to stop
|
||||||
@@ -93,7 +93,8 @@ proc oneNode(node: Node, rng: ref HmacDrbgContext) {.async.} =
|
|||||||
node.gossip.subscribe(
|
node.gossip.subscribe(
|
||||||
"metrics",
|
"metrics",
|
||||||
proc(topic: string, data: seq[byte]) {.async.} =
|
proc(topic: string, data: seq[byte]) {.async.} =
|
||||||
echo MetricList.decode(data).tryGet()
|
let m = MetricList.decode(data).expect("metric can be decoded")
|
||||||
|
echo m
|
||||||
,
|
,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -158,8 +159,8 @@ waitFor(main())
|
|||||||
## This is John receiving & logging everyone's metrics.
|
## This is John receiving & logging everyone's metrics.
|
||||||
##
|
##
|
||||||
## ## Going further
|
## ## Going further
|
||||||
## Building efficient & safe GossipSub networks is a tricky subject. By tweaking the [gossip params](https://status-im.github.io/nim-libp2p/master/libp2p/protocols/pubsub/gossipsub/types.html#GossipSubParams)
|
## Building efficient & safe GossipSub networks is a tricky subject. By tweaking the [gossip params](https://vacp2p.github.io/nim-libp2p/master/libp2p/protocols/pubsub/gossipsub/types.html#GossipSubParams)
|
||||||
## and [topic params](https://status-im.github.io/nim-libp2p/master/libp2p/protocols/pubsub/gossipsub/types.html#TopicParams),
|
## and [topic params](https://vacp2p.github.io/nim-libp2p/master/libp2p/protocols/pubsub/gossipsub/types.html#TopicParams),
|
||||||
## you can achieve very different properties.
|
## you can achieve very different properties.
|
||||||
##
|
##
|
||||||
## Also see reports for [GossipSub v1.1](https://gateway.ipfs.io/ipfs/QmRAFP5DBnvNjdYSbWhEhVRJJDFCLpPyvew5GwCCB4VxM4)
|
## Also see reports for [GossipSub v1.1](https://gateway.ipfs.io/ipfs/QmRAFP5DBnvNjdYSbWhEhVRJJDFCLpPyvew5GwCCB4VxM4)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{.used.}
|
||||||
## # Discovery Manager
|
## # Discovery Manager
|
||||||
##
|
##
|
||||||
## In the [previous tutorial](tutorial_4_gossipsub.md), we built a custom protocol using [protobuf](https://developers.google.com/protocol-buffers) and
|
## In the [previous tutorial](tutorial_4_gossipsub.md), we built a custom protocol using [protobuf](https://developers.google.com/protocol-buffers) and
|
||||||
@@ -34,9 +35,15 @@ proc createSwitch(rdv: RendezVous = RendezVous.new()): Switch =
|
|||||||
const DumbCodec = "/dumb/proto/1.0.0"
|
const DumbCodec = "/dumb/proto/1.0.0"
|
||||||
type DumbProto = ref object of LPProtocol
|
type DumbProto = ref object of LPProtocol
|
||||||
proc new(T: typedesc[DumbProto], nodeNumber: int): T =
|
proc new(T: typedesc[DumbProto], nodeNumber: int): T =
|
||||||
proc handle(conn: Connection, proto: string) {.async.} =
|
proc handle(conn: Connection, proto: string) {.async: (raises: [CancelledError]).} =
|
||||||
echo "Node", nodeNumber, " received: ", string.fromBytes(await conn.readLp(1024))
|
try:
|
||||||
await conn.close()
|
echo "Node", nodeNumber, " received: ", string.fromBytes(await conn.readLp(1024))
|
||||||
|
except CancelledError as e:
|
||||||
|
raise e
|
||||||
|
except CatchableError as e:
|
||||||
|
echo "exception in handler", e.msg
|
||||||
|
finally:
|
||||||
|
await conn.close()
|
||||||
|
|
||||||
return T.new(codecs = @[DumbCodec], handler = handle)
|
return T.new(codecs = @[DumbCodec], handler = handle)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{.used.}
|
||||||
## # Tron example
|
## # Tron example
|
||||||
##
|
##
|
||||||
## In this tutorial, we will create a video game based on libp2p, using
|
## In this tutorial, we will create a video game based on libp2p, using
|
||||||
@@ -152,21 +153,26 @@ proc draw(g: Game) =
|
|||||||
## peer know that we are available, check that he is also available,
|
## peer know that we are available, check that he is also available,
|
||||||
## and launch the game.
|
## and launch the game.
|
||||||
proc new(T: typedesc[GameProto], g: Game): T =
|
proc new(T: typedesc[GameProto], g: Game): T =
|
||||||
proc handle(conn: Connection, proto: string) {.async.} =
|
proc handle(conn: Connection, proto: string) {.async: (raises: [CancelledError]).} =
|
||||||
defer:
|
defer:
|
||||||
await conn.closeWithEof()
|
await conn.closeWithEof()
|
||||||
if g.peerFound.finished or g.hasCandidate:
|
try:
|
||||||
await conn.close()
|
if g.peerFound.finished or g.hasCandidate:
|
||||||
return
|
await conn.close()
|
||||||
g.hasCandidate = true
|
return
|
||||||
await conn.writeLp("ok")
|
g.hasCandidate = true
|
||||||
if "ok" != string.fromBytes(await conn.readLp(1024)):
|
await conn.writeLp("ok")
|
||||||
g.hasCandidate = false
|
if "ok" != string.fromBytes(await conn.readLp(1024)):
|
||||||
return
|
g.hasCandidate = false
|
||||||
g.peerFound.complete(conn)
|
return
|
||||||
# The handler of a protocol must wait for the stream to
|
g.peerFound.complete(conn)
|
||||||
# be finished before returning
|
# The handler of a protocol must wait for the stream to
|
||||||
await conn.join()
|
# be finished before returning
|
||||||
|
await conn.join()
|
||||||
|
except CancelledError as e:
|
||||||
|
raise e
|
||||||
|
except CatchableError as e:
|
||||||
|
echo "exception in handler", e.msg
|
||||||
|
|
||||||
return T.new(codecs = @["/tron/1.0.0"], handler = handle)
|
return T.new(codecs = @["/tron/1.0.0"], handler = handle)
|
||||||
|
|
||||||
@@ -214,8 +220,7 @@ proc networking(g: Game) {.async.} =
|
|||||||
# We are "player 2"
|
# We are "player 2"
|
||||||
swap(g.localPlayer, g.remotePlayer)
|
swap(g.localPlayer, g.remotePlayer)
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
discard
|
discard,
|
||||||
,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await switch.start()
|
await switch.start()
|
||||||
@@ -268,14 +273,11 @@ nico.init("Status", "Tron")
|
|||||||
nico.createWindow("Tron", mapSize * 4, mapSize * 4, 4, false)
|
nico.createWindow("Tron", mapSize * 4, mapSize * 4, 4, false)
|
||||||
nico.run(
|
nico.run(
|
||||||
proc() =
|
proc() =
|
||||||
discard
|
discard,
|
||||||
,
|
|
||||||
proc(dt: float32) =
|
proc(dt: float32) =
|
||||||
game.update(dt)
|
game.update(dt),
|
||||||
,
|
|
||||||
proc() =
|
proc() =
|
||||||
game.draw()
|
game.draw(),
|
||||||
,
|
|
||||||
)
|
)
|
||||||
waitFor(netFut.cancelAndWait())
|
waitFor(netFut.cancelAndWait())
|
||||||
|
|
||||||
|
|||||||
27
flake.lock
generated
Normal file
27
flake.lock
generated
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1752620740,
|
||||||
|
"narHash": "sha256-f3pO+9lg66mV7IMmmIqG4PL3223TYMlnlw+pnpelbss=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "32a4e87942101f1c9f9865e04dc3ddb175f5f32e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-25.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
34
flake.nix
Normal file
34
flake.nix
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
description = "nim-libp2p dev shell flake";
|
||||||
|
|
||||||
|
nixConfig = {
|
||||||
|
extra-substituters = [ "https://nix-cache.status.im/" ];
|
||||||
|
extra-trusted-public-keys = [ "nix-cache.status.im-1:x/93lOfLU+duPplwMSBR+OlY4+mo+dCN7n0mr4oPwgY=" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }:
|
||||||
|
let
|
||||||
|
stableSystems = [
|
||||||
|
"x86_64-linux" "aarch64-linux" "armv7a-linux"
|
||||||
|
"x86_64-darwin" "aarch64-darwin"
|
||||||
|
"x86_64-windows"
|
||||||
|
];
|
||||||
|
forEach = nixpkgs.lib.genAttrs;
|
||||||
|
forAllSystems = forEach stableSystems;
|
||||||
|
pkgsFor = forEach stableSystems (
|
||||||
|
system: import nixpkgs { inherit system; }
|
||||||
|
);
|
||||||
|
in rec {
|
||||||
|
devShells = forAllSystems (system: {
|
||||||
|
default = pkgsFor.${system}.mkShell {
|
||||||
|
nativeBuildInputs = with pkgsFor.${system}; [
|
||||||
|
nim-2_2 nimble openssl.dev
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
From 29bac4cd8f28abfb9efb481d800b7c2e855d9b03 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Gabriel Cruz <gabe@gmelodie.com>
|
||||||
|
Date: Wed, 17 Sep 2025 10:42:14 -0300
|
||||||
|
Subject: [PATCH] disable filtering of private ip addresses
|
||||||
|
|
||||||
|
---
|
||||||
|
p2p/protocol/autonatv2/autonat.go | 24 +-----------------------
|
||||||
|
p2p/protocol/autonatv2/server.go | 9 ++++++---
|
||||||
|
2 files changed, 7 insertions(+), 26 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/p2p/protocol/autonatv2/autonat.go b/p2p/protocol/autonatv2/autonat.go
|
||||||
|
index 24883052..00a6211f 100644
|
||||||
|
--- a/p2p/protocol/autonatv2/autonat.go
|
||||||
|
+++ b/p2p/protocol/autonatv2/autonat.go
|
||||||
|
@@ -16,7 +16,6 @@ import (
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
logging "github.com/libp2p/go-libp2p/gologshim"
|
||||||
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
- manet "github.com/multiformats/go-multiaddr/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
@@ -180,21 +179,7 @@ func (an *AutoNAT) Close() {
|
||||||
|
// GetReachability makes a single dial request for checking reachability for requested addresses
|
||||||
|
func (an *AutoNAT) GetReachability(ctx context.Context, reqs []Request) (Result, error) {
|
||||||
|
var filteredReqs []Request
|
||||||
|
- if !an.allowPrivateAddrs {
|
||||||
|
- filteredReqs = make([]Request, 0, len(reqs))
|
||||||
|
- for _, r := range reqs {
|
||||||
|
- if manet.IsPublicAddr(r.Addr) {
|
||||||
|
- filteredReqs = append(filteredReqs, r)
|
||||||
|
- } else {
|
||||||
|
- log.Error("private address in reachability check", "address", r.Addr)
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
- if len(filteredReqs) == 0 {
|
||||||
|
- return Result{}, ErrPrivateAddrs
|
||||||
|
- }
|
||||||
|
- } else {
|
||||||
|
- filteredReqs = reqs
|
||||||
|
- }
|
||||||
|
+ filteredReqs = reqs
|
||||||
|
an.mx.Lock()
|
||||||
|
now := time.Now()
|
||||||
|
var p peer.ID
|
||||||
|
@@ -215,13 +200,6 @@ func (an *AutoNAT) GetReachability(ctx context.Context, reqs []Request) (Result,
|
||||||
|
log.Debug("reachability check failed", "peer", p, "err", err)
|
||||||
|
return res, fmt.Errorf("reachability check with %s failed: %w", p, err)
|
||||||
|
}
|
||||||
|
- // restore the correct index in case we'd filtered private addresses
|
||||||
|
- for i, r := range reqs {
|
||||||
|
- if r.Addr.Equal(res.Addr) {
|
||||||
|
- res.Idx = i
|
||||||
|
- break
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
log.Debug("reachability check successful", "peer", p)
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
diff --git a/p2p/protocol/autonatv2/server.go b/p2p/protocol/autonatv2/server.go
|
||||||
|
index 167d3d8e..e6d1e492 100644
|
||||||
|
--- a/p2p/protocol/autonatv2/server.go
|
||||||
|
+++ b/p2p/protocol/autonatv2/server.go
|
||||||
|
@@ -196,9 +197,6 @@ func (as *server) serveDialRequest(s network.Stream) EventDialRequestCompleted {
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
- if !as.allowPrivateAddrs && !manet.IsPublicAddr(a) {
|
||||||
|
- continue
|
||||||
|
- }
|
||||||
|
if !as.dialerHost.Network().CanDial(p, a) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
--
|
||||||
|
2.51.0
|
||||||
|
|
||||||
97
interop/autonatv2/go-peer/go.mod
Normal file
97
interop/autonatv2/go-peer/go.mod
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
module go-peer
|
||||||
|
|
||||||
|
go 1.25.1
|
||||||
|
|
||||||
|
require github.com/libp2p/go-libp2p v0.43.0
|
||||||
|
replace github.com/libp2p/go-libp2p => ./go-libp2p
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/benbjohnson/clock v1.3.5 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
|
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
||||||
|
github.com/flynn/noise v1.1.0 // indirect
|
||||||
|
github.com/francoispqt/gojay v1.2.13 // indirect
|
||||||
|
github.com/google/gopacket v1.1.19 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/huin/goupnp v1.3.0 // indirect
|
||||||
|
github.com/ipfs/go-cid v0.5.0 // indirect
|
||||||
|
github.com/ipfs/go-log/v2 v2.6.0 // indirect
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
|
||||||
|
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||||
|
github.com/koron/go-ssdp v0.0.6 // indirect
|
||||||
|
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
|
||||||
|
github.com/libp2p/go-flow-metrics v0.2.0 // indirect
|
||||||
|
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
|
||||||
|
github.com/libp2p/go-msgio v0.3.0 // indirect
|
||||||
|
github.com/libp2p/go-netroute v0.2.2 // indirect
|
||||||
|
github.com/libp2p/go-reuseport v0.4.0 // indirect
|
||||||
|
github.com/libp2p/go-yamux/v5 v5.0.1 // indirect
|
||||||
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/miekg/dns v1.1.66 // indirect
|
||||||
|
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
|
||||||
|
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
|
||||||
|
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||||
|
github.com/mr-tron/base58 v1.2.0 // indirect
|
||||||
|
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||||
|
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||||
|
github.com/multiformats/go-multiaddr v0.16.0 // indirect
|
||||||
|
github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect
|
||||||
|
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
|
||||||
|
github.com/multiformats/go-multibase v0.2.0 // indirect
|
||||||
|
github.com/multiformats/go-multicodec v0.9.1 // indirect
|
||||||
|
github.com/multiformats/go-multihash v0.2.3 // indirect
|
||||||
|
github.com/multiformats/go-multistream v0.6.1 // indirect
|
||||||
|
github.com/multiformats/go-varint v0.0.7 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||||
|
github.com/pion/datachannel v1.5.10 // indirect
|
||||||
|
github.com/pion/dtls/v2 v2.2.12 // indirect
|
||||||
|
github.com/pion/dtls/v3 v3.0.6 // indirect
|
||||||
|
github.com/pion/ice/v4 v4.0.10 // indirect
|
||||||
|
github.com/pion/interceptor v0.1.40 // indirect
|
||||||
|
github.com/pion/logging v0.2.3 // indirect
|
||||||
|
github.com/pion/mdns/v2 v2.0.7 // indirect
|
||||||
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
|
github.com/pion/rtcp v1.2.15 // indirect
|
||||||
|
github.com/pion/rtp v1.8.19 // indirect
|
||||||
|
github.com/pion/sctp v1.8.39 // indirect
|
||||||
|
github.com/pion/sdp/v3 v3.0.13 // indirect
|
||||||
|
github.com/pion/srtp/v3 v3.0.6 // indirect
|
||||||
|
github.com/pion/stun v0.6.1 // indirect
|
||||||
|
github.com/pion/stun/v3 v3.0.0 // indirect
|
||||||
|
github.com/pion/transport/v2 v2.2.10 // indirect
|
||||||
|
github.com/pion/transport/v3 v3.0.7 // indirect
|
||||||
|
github.com/pion/turn/v4 v4.0.2 // indirect
|
||||||
|
github.com/pion/webrtc/v4 v4.1.2 // indirect
|
||||||
|
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
|
github.com/prometheus/common v0.64.0 // indirect
|
||||||
|
github.com/prometheus/procfs v0.16.1 // indirect
|
||||||
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
|
github.com/quic-go/quic-go v0.54.0 // indirect
|
||||||
|
github.com/quic-go/webtransport-go v0.9.0 // indirect
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
|
github.com/wlynxg/anet v0.0.5 // indirect
|
||||||
|
go.uber.org/dig v1.19.0 // indirect
|
||||||
|
go.uber.org/fx v1.24.0 // indirect
|
||||||
|
go.uber.org/mock v0.5.2 // indirect
|
||||||
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
|
go.uber.org/zap v1.27.0 // indirect
|
||||||
|
golang.org/x/crypto v0.39.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect
|
||||||
|
golang.org/x/mod v0.25.0 // indirect
|
||||||
|
golang.org/x/net v0.41.0 // indirect
|
||||||
|
golang.org/x/sync v0.15.0 // indirect
|
||||||
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
|
golang.org/x/text v0.26.0 // indirect
|
||||||
|
golang.org/x/time v0.12.0 // indirect
|
||||||
|
golang.org/x/tools v0.34.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
|
lukechampine.com/blake3 v1.4.1 // indirect
|
||||||
|
)
|
||||||
441
interop/autonatv2/go-peer/go.sum
Normal file
441
interop/autonatv2/go-peer/go.sum
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
||||||
|
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||||
|
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
||||||
|
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
||||||
|
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
||||||
|
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
|
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||||
|
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||||
|
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
|
||||||
|
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
|
||||||
|
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||||
|
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||||
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
|
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
||||||
|
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
||||||
|
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
||||||
|
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
|
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||||
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
|
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||||
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
|
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||||
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||||
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
||||||
|
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
||||||
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||||
|
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||||
|
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||||
|
github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
|
||||||
|
github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk=
|
||||||
|
github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg=
|
||||||
|
github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8=
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
|
||||||
|
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||||
|
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
|
||||||
|
github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk=
|
||||||
|
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||||
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||||
|
github.com/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU=
|
||||||
|
github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
||||||
|
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||||
|
github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw=
|
||||||
|
github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc=
|
||||||
|
github.com/libp2p/go-libp2p v0.43.0 h1:b2bg2cRNmY4HpLK8VHYQXLX2d3iND95OjodLFymvqXU=
|
||||||
|
github.com/libp2p/go-libp2p v0.43.0/go.mod h1:IiSqAXDyP2sWH+J2gs43pNmB/y4FOi2XQPbsb+8qvzc=
|
||||||
|
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
|
||||||
|
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
|
||||||
|
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
|
||||||
|
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
|
||||||
|
github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0=
|
||||||
|
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
|
||||||
|
github.com/libp2p/go-netroute v0.2.2 h1:Dejd8cQ47Qx2kRABg6lPwknU7+nBnFRpko45/fFPuZ8=
|
||||||
|
github.com/libp2p/go-netroute v0.2.2/go.mod h1:Rntq6jUAH0l9Gg17w5bFGhcC9a+vk4KNXs6s7IljKYE=
|
||||||
|
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
|
||||||
|
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
|
||||||
|
github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg=
|
||||||
|
github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU=
|
||||||
|
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||||
|
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
|
||||||
|
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||||
|
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||||
|
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||||
|
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=
|
||||||
|
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms=
|
||||||
|
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=
|
||||||
|
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU=
|
||||||
|
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc=
|
||||||
|
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s=
|
||||||
|
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
|
||||||
|
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
|
||||||
|
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||||
|
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||||
|
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||||
|
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||||
|
github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
|
||||||
|
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||||
|
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||||
|
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||||
|
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
|
||||||
|
github.com/multiformats/go-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc=
|
||||||
|
github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0=
|
||||||
|
github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M=
|
||||||
|
github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc=
|
||||||
|
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
|
||||||
|
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
|
||||||
|
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
|
||||||
|
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
|
||||||
|
github.com/multiformats/go-multicodec v0.9.1 h1:x/Fuxr7ZuR4jJV4Os5g444F7xC4XmyUaT/FWtE+9Zjo=
|
||||||
|
github.com/multiformats/go-multicodec v0.9.1/go.mod h1:LLWNMtyV5ithSBUo3vFIMaeDy+h3EbkMTek1m+Fybbo=
|
||||||
|
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
|
||||||
|
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
|
||||||
|
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
|
||||||
|
github.com/multiformats/go-multistream v0.6.1 h1:4aoX5v6T+yWmc2raBHsTvzmFhOI8WVOer28DeBBEYdQ=
|
||||||
|
github.com/multiformats/go-multistream v0.6.1/go.mod h1:ksQf6kqHAb6zIsyw7Zm+gAuVo57Qbq84E27YlYqavqw=
|
||||||
|
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
|
||||||
|
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
|
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||||
|
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||||
|
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
||||||
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||||
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||||
|
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
|
||||||
|
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
|
||||||
|
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||||
|
github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
|
||||||
|
github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
|
||||||
|
github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E=
|
||||||
|
github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU=
|
||||||
|
github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4=
|
||||||
|
github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
|
||||||
|
github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4=
|
||||||
|
github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic=
|
||||||
|
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||||
|
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
|
||||||
|
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
|
||||||
|
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
|
||||||
|
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
|
||||||
|
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||||
|
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||||
|
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
|
||||||
|
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
|
||||||
|
github.com/pion/rtp v1.8.19 h1:jhdO/3XhL/aKm/wARFVmvTfq0lC/CvN1xwYKmduly3c=
|
||||||
|
github.com/pion/rtp v1.8.19/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk=
|
||||||
|
github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE=
|
||||||
|
github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE=
|
||||||
|
github.com/pion/sdp/v3 v3.0.13 h1:uN3SS2b+QDZnWXgdr69SM8KB4EbcnPnPf2Laxhty/l4=
|
||||||
|
github.com/pion/sdp/v3 v3.0.13/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
|
||||||
|
github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4=
|
||||||
|
github.com/pion/srtp/v3 v3.0.6/go.mod h1:BxvziG3v/armJHAaJ87euvkhHqWe9I7iiOy50K2QkhY=
|
||||||
|
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
||||||
|
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
||||||
|
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
|
||||||
|
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
|
||||||
|
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
|
||||||
|
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||||
|
github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q=
|
||||||
|
github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
|
||||||
|
github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1o0=
|
||||||
|
github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo=
|
||||||
|
github.com/pion/turn/v4 v4.0.2 h1:ZqgQ3+MjP32ug30xAbD6Mn+/K4Sxi3SdNOTFf+7mpps=
|
||||||
|
github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7IToA1zs=
|
||||||
|
github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54=
|
||||||
|
github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
|
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||||
|
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||||
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
|
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
|
github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4=
|
||||||
|
github.com/prometheus/common v0.64.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||||
|
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
|
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||||
|
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||||
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
|
github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
|
||||||
|
github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
|
||||||
|
github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70=
|
||||||
|
github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||||
|
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
||||||
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
||||||
|
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
||||||
|
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
||||||
|
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||||
|
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||||
|
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
||||||
|
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
||||||
|
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
||||||
|
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
||||||
|
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
||||||
|
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
||||||
|
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
||||||
|
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||||
|
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
||||||
|
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
||||||
|
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
||||||
|
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
||||||
|
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
||||||
|
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
||||||
|
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
||||||
|
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
||||||
|
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||||
|
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||||
|
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||||
|
github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||||
|
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||||
|
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||||
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
||||||
|
go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4=
|
||||||
|
go.uber.org/dig v1.19.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
|
||||||
|
go.uber.org/fx v1.24.0 h1:wE8mruvpg2kiiL1Vqd0CC+tr0/24XIB10Iwp2lLWzkg=
|
||||||
|
go.uber.org/fx v1.24.0/go.mod h1:AmDeGyS+ZARGKM4tlH4FY2Jr63VjbEDJHtqXTGP5hbo=
|
||||||
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
|
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||||
|
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||||
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
|
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||||
|
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||||
|
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
||||||
|
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
||||||
|
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||||
|
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||||
|
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||||
|
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||||
|
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4=
|
||||||
|
golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||||
|
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
|
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||||
|
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||||
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
|
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||||
|
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||||
|
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||||
|
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||||
|
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||||
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||||
|
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||||
|
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||||
|
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
|
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||||
|
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||||
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
|
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||||
|
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||||
|
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
||||||
|
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||||
|
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||||
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
||||||
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
|
||||||
|
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
|
||||||
|
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
||||||
|
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
||||||
1
interop/autonatv2/go-peer/peer.id
Normal file
1
interop/autonatv2/go-peer/peer.id
Normal file
@@ -0,0 +1 @@
|
|||||||
|
12D3KooWSnUDxXeeEnerD1Wf35R5b8bjTMzdAz838aDUUY8GJPGa
|
||||||
2
interop/autonatv2/go-peer/peer.key
Normal file
2
interop/autonatv2/go-peer/peer.key
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
@i
|
||||||
|
(>%ËÁø‡®PM”ܘXE~§|# õ“ýºØ®ü\íÇØ¬åsqzïÔDSݺvöLË(±Úð…•(×
|
||||||
97
interop/autonatv2/go-peer/testautonatv2.go
Normal file
97
interop/autonatv2/go-peer/testautonatv2.go
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
libp2p "github.com/libp2p/go-libp2p"
|
||||||
|
crypto "github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
peer "github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
privKeyFile = "peer.key"
|
||||||
|
peerIDFile = "peer.id"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadOrCreateIdentity() (crypto.PrivKey, peer.ID, error) {
|
||||||
|
if _, err := os.Stat(privKeyFile); err == nil {
|
||||||
|
// Load private key
|
||||||
|
data, err := ioutil.ReadFile(privKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to read private key: %w", err)
|
||||||
|
}
|
||||||
|
priv, err := crypto.UnmarshalPrivateKey(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to unmarshal private key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load peer ID as string
|
||||||
|
peerData, err := ioutil.ReadFile(peerIDFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to read peer ID: %w", err)
|
||||||
|
}
|
||||||
|
pid, err := peer.Decode(string(peerData))
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to decode peer ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return priv, pid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new keypair
|
||||||
|
priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to generate keypair: %w", err)
|
||||||
|
}
|
||||||
|
pid, err := peer.IDFromPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to derive peer ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save private key
|
||||||
|
privBytes, err := crypto.MarshalPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to marshal private key: %w", err)
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(privKeyFile, privBytes, 0600); err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to write private key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save peer ID in canonical string form
|
||||||
|
if err := ioutil.WriteFile(peerIDFile, []byte(pid.String()), 0644); err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to write peer ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return priv, pid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
priv, pid, err := loadOrCreateIdentity()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Identity setup failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := libp2p.New(
|
||||||
|
libp2p.Identity(priv),
|
||||||
|
libp2p.EnableAutoNATv2(),
|
||||||
|
libp2p.ListenAddrStrings(
|
||||||
|
"/ip4/0.0.0.0/tcp/4040",
|
||||||
|
"/ip6/::/tcp/4040",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to create host: %v", err)
|
||||||
|
}
|
||||||
|
defer h.Close()
|
||||||
|
|
||||||
|
fmt.Println("Peer ID:", pid.String())
|
||||||
|
fmt.Println("Listen addresses:", h.Addrs())
|
||||||
|
fmt.Println("AutoNATv2 client started.")
|
||||||
|
|
||||||
|
select {}
|
||||||
|
}
|
||||||
|
|
||||||
4
interop/autonatv2/nim-peer/config.nims
Normal file
4
interop/autonatv2/nim-peer/config.nims
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# begin Nimble config (version 2)
|
||||||
|
when withDir(thisDir(), system.fileExists("nimble.paths")):
|
||||||
|
include "nimble.paths"
|
||||||
|
# end Nimble config
|
||||||
10
interop/autonatv2/nim-peer/nim_peer.nimble
Normal file
10
interop/autonatv2/nim-peer/nim_peer.nimble
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
version = "0.1.0"
|
||||||
|
author = "Status Research & Development Gmb"
|
||||||
|
description = "AutoNATv2 peer for interop testing"
|
||||||
|
license = "MIT"
|
||||||
|
srcDir = "src"
|
||||||
|
bin = @["nim_peer"]
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
|
||||||
|
requires "nim >= 2.3.1", "libp2p"
|
||||||
64
interop/autonatv2/nim-peer/src/nim_peer.nim
Normal file
64
interop/autonatv2/nim-peer/src/nim_peer.nim
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import net, os, chronos, libp2p
|
||||||
|
import libp2p/protocols/connectivity/autonatv2/service
|
||||||
|
import libp2p/protocols/connectivity/autonatv2/types
|
||||||
|
|
||||||
|
proc waitForService(
|
||||||
|
host: string, port: Port, retries: int = 20, delay: Duration = 500.milliseconds
|
||||||
|
): Future[bool] {.async.} =
|
||||||
|
for i in 0 ..< retries:
|
||||||
|
try:
|
||||||
|
var s = newSocket()
|
||||||
|
s.connect(host, port)
|
||||||
|
s.close()
|
||||||
|
return true
|
||||||
|
except OSError:
|
||||||
|
discard
|
||||||
|
await sleepAsync(delay)
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc main() {.async.} =
|
||||||
|
if paramCount() != 1:
|
||||||
|
quit("Usage: nim r src/nim_peer.nim <peerid>", 1)
|
||||||
|
|
||||||
|
# ensure go peer is started
|
||||||
|
await sleepAsync(3.seconds)
|
||||||
|
|
||||||
|
let dstPeerId = PeerId.init(paramStr(1)).get()
|
||||||
|
|
||||||
|
var src = SwitchBuilder
|
||||||
|
.new()
|
||||||
|
.withRng(newRng())
|
||||||
|
.withAddresses(@[MultiAddress.init("/ip4/0.0.0.0/tcp/3030").tryGet()])
|
||||||
|
.withAutonatV2Server()
|
||||||
|
.withAutonatV2(
|
||||||
|
serviceConfig = AutonatV2ServiceConfig.new(scheduleInterval = Opt.some(1.seconds))
|
||||||
|
)
|
||||||
|
.withTcpTransport()
|
||||||
|
.withYamux()
|
||||||
|
.withNoise()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
let awaiter = newFuture[void]()
|
||||||
|
|
||||||
|
proc statusAndConfidenceHandler(
|
||||||
|
networkReachability: NetworkReachability, confidence: Opt[float]
|
||||||
|
) {.async: (raises: [CancelledError]).} =
|
||||||
|
if networkReachability != NetworkReachability.Unknown and confidence.isSome() and
|
||||||
|
confidence.get() >= 0.3:
|
||||||
|
if not awaiter.finished:
|
||||||
|
awaiter.complete()
|
||||||
|
|
||||||
|
let service = cast[AutonatV2Service](src.services[1])
|
||||||
|
service.setStatusAndConfidenceHandler(statusAndConfidenceHandler)
|
||||||
|
|
||||||
|
await src.start()
|
||||||
|
await src.connect(dstPeerId, @[MultiAddress.init("/ip4/127.0.0.1/tcp/4040").get()])
|
||||||
|
|
||||||
|
await awaiter
|
||||||
|
echo service.networkReachability
|
||||||
|
|
||||||
|
when isMainModule:
|
||||||
|
if waitFor(waitForService("127.0.0.1", Port(4040))):
|
||||||
|
waitFor(main())
|
||||||
|
else:
|
||||||
|
quit("timeout waiting for service", 1)
|
||||||
19
interop/hole-punching/Dockerfile
Normal file
19
interop/hole-punching/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# syntax=docker/dockerfile:1.5-labs
|
||||||
|
FROM nimlang/nim:latest as builder
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
||||||
|
COPY .pinned libp2p.nimble nim-libp2p/
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/cache/apt apt-get update && apt-get install -y libssl-dev
|
||||||
|
|
||||||
|
RUN cd nim-libp2p && nimble install_pinned && nimble install redis -y
|
||||||
|
|
||||||
|
COPY . nim-libp2p/
|
||||||
|
|
||||||
|
RUN cd nim-libp2p && nim c --skipParentCfg --NimblePath:./nimbledeps/pkgs2 --mm:refc -d:chronicles_log_level=DEBUG -d:chronicles_default_output_device=stderr -d:release --threads:off --skipProjCfg -o:hole-punching-tests ./interop/hole-punching/hole_punching.nim
|
||||||
|
|
||||||
|
FROM --platform=linux/amd64 debian:bullseye-slim
|
||||||
|
RUN --mount=type=cache,target=/var/cache/apt apt-get update && apt-get install -y dnsutils jq curl tcpdump iproute2 libssl-dev
|
||||||
|
COPY --from=builder /workspace/nim-libp2p/hole-punching-tests /usr/bin/hole-punch-client
|
||||||
|
ENV RUST_BACKTRACE=1
|
||||||
138
interop/hole-punching/hole_punching.nim
Normal file
138
interop/hole-punching/hole_punching.nim
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import std/[os, options, strformat, sequtils]
|
||||||
|
import redis
|
||||||
|
import chronos, chronicles
|
||||||
|
import
|
||||||
|
../../libp2p/[
|
||||||
|
builders,
|
||||||
|
switch,
|
||||||
|
multicodec,
|
||||||
|
observedaddrmanager,
|
||||||
|
services/hpservice,
|
||||||
|
services/autorelayservice,
|
||||||
|
protocols/connectivity/autonat/client as aclient,
|
||||||
|
protocols/connectivity/relay/client as rclient,
|
||||||
|
protocols/connectivity/relay/relay,
|
||||||
|
protocols/connectivity/autonat/service,
|
||||||
|
protocols/ping,
|
||||||
|
]
|
||||||
|
import ../../tests/[stubs/autonatclientstub, errorhelpers]
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "hp interop node"
|
||||||
|
|
||||||
|
proc createSwitch(r: Relay = nil, hpService: Service = nil): Switch =
|
||||||
|
let rng = newRng()
|
||||||
|
var builder = SwitchBuilder
|
||||||
|
.new()
|
||||||
|
.withRng(rng)
|
||||||
|
.withAddresses(@[MultiAddress.init("/ip4/0.0.0.0/tcp/0").tryGet()])
|
||||||
|
.withObservedAddrManager(ObservedAddrManager.new(maxSize = 1, minCount = 1))
|
||||||
|
.withTcpTransport({ServerFlags.TcpNoDelay})
|
||||||
|
.withYamux()
|
||||||
|
.withAutonat()
|
||||||
|
.withNoise()
|
||||||
|
|
||||||
|
if hpService != nil:
|
||||||
|
builder = builder.withServices(@[hpService])
|
||||||
|
|
||||||
|
if r != nil:
|
||||||
|
builder = builder.withCircuitRelay(r)
|
||||||
|
|
||||||
|
let s = builder.build()
|
||||||
|
s.mount(Ping.new(rng = rng))
|
||||||
|
return s
|
||||||
|
|
||||||
|
proc main() {.async.} =
|
||||||
|
let relayClient = RelayClient.new()
|
||||||
|
let autoRelayService = AutoRelayService.new(1, relayClient, nil, newRng())
|
||||||
|
let autonatClientStub = AutonatClientStub.new(expectedDials = 1)
|
||||||
|
autonatClientStub.answer = NotReachable
|
||||||
|
let autonatService = AutonatService.new(autonatClientStub, newRng(), maxQueueSize = 1)
|
||||||
|
let hpservice = HPService.new(autonatService, autoRelayService)
|
||||||
|
|
||||||
|
let
|
||||||
|
isListener = getEnv("MODE") == "listen"
|
||||||
|
switch = createSwitch(relayClient, hpservice)
|
||||||
|
auxSwitch = createSwitch()
|
||||||
|
redisClient = open("redis", 6379.Port)
|
||||||
|
|
||||||
|
debug "Connected to redis"
|
||||||
|
|
||||||
|
await switch.start()
|
||||||
|
await auxSwitch.start()
|
||||||
|
|
||||||
|
let relayAddr =
|
||||||
|
try:
|
||||||
|
redisClient.bLPop(@["RELAY_TCP_ADDRESS"], 0)
|
||||||
|
except Exception as e:
|
||||||
|
raise newException(CatchableError, e.msg)
|
||||||
|
|
||||||
|
debug "All relay addresses", relayAddr
|
||||||
|
|
||||||
|
# This is necessary to make the autonat service work. It will ask this peer for our reachability which the autonat
|
||||||
|
# client stub will answer NotReachable.
|
||||||
|
await switch.connect(auxSwitch.peerInfo.peerId, auxSwitch.peerInfo.addrs)
|
||||||
|
|
||||||
|
# Wait for autonat to be NotReachable
|
||||||
|
while autonatService.networkReachability != NetworkReachability.NotReachable:
|
||||||
|
await sleepAsync(100.milliseconds)
|
||||||
|
|
||||||
|
# This will trigger the autonat relay service to make a reservation.
|
||||||
|
let relayMA = MultiAddress.init(relayAddr[1]).tryGet()
|
||||||
|
|
||||||
|
try:
|
||||||
|
debug "Dialing relay...", relayMA
|
||||||
|
let relayId = await switch.connect(relayMA).wait(30.seconds)
|
||||||
|
debug "Connected to relay", relayId
|
||||||
|
except AsyncTimeoutError as e:
|
||||||
|
raise newException(CatchableError, "Connection to relay timed out: " & e.msg, e)
|
||||||
|
|
||||||
|
# Wait for our relay address to be published
|
||||||
|
while not switch.peerInfo.addrs.anyIt(it.contains(multiCodec("p2p-circuit")).tryGet()):
|
||||||
|
await sleepAsync(100.milliseconds)
|
||||||
|
|
||||||
|
if isListener:
|
||||||
|
let listenerPeerId = switch.peerInfo.peerId
|
||||||
|
discard redisClient.rPush("LISTEN_CLIENT_PEER_ID", $listenerPeerId)
|
||||||
|
debug "Pushed listener client peer id to redis", listenerPeerId
|
||||||
|
|
||||||
|
# Nothing to do anymore, wait to be killed
|
||||||
|
await sleepAsync(2.minutes)
|
||||||
|
else:
|
||||||
|
let listenerId =
|
||||||
|
try:
|
||||||
|
PeerId.init(redisClient.bLPop(@["LISTEN_CLIENT_PEER_ID"], 0)[1]).tryGet()
|
||||||
|
except Exception as e:
|
||||||
|
raise newException(CatchableError, "Exception init peer: " & e.msg, e)
|
||||||
|
|
||||||
|
debug "Got listener peer id", listenerId
|
||||||
|
let listenerRelayAddr = MultiAddress.init($relayMA & "/p2p-circuit").tryGet()
|
||||||
|
|
||||||
|
debug "Dialing listener relay address", listenerRelayAddr
|
||||||
|
await switch.connect(listenerId, @[listenerRelayAddr])
|
||||||
|
|
||||||
|
# wait for hole-punching to complete in the background
|
||||||
|
await sleepAsync(5000.milliseconds)
|
||||||
|
|
||||||
|
let conn = switch.connManager.selectMuxer(listenerId).connection
|
||||||
|
let channel = await switch.dial(listenerId, @[listenerRelayAddr], PingCodec)
|
||||||
|
let delay = await Ping.new().ping(channel)
|
||||||
|
await allFuturesThrowing(
|
||||||
|
channel.close(), conn.close(), switch.stop(), auxSwitch.stop()
|
||||||
|
)
|
||||||
|
echo &"""{{"rtt_to_holepunched_peer_millis":{delay.millis}}}"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc mainAsync(): Future[string] {.async.} =
|
||||||
|
# mainAsync wraps main and returns some value, as otherwise
|
||||||
|
# 'waitFor(fut)' has no type (or is ambiguous)
|
||||||
|
await main()
|
||||||
|
return "done"
|
||||||
|
|
||||||
|
discard waitFor(mainAsync().wait(4.minutes))
|
||||||
|
except AsyncTimeoutError as e:
|
||||||
|
error "Program execution timed out", description = e.msg
|
||||||
|
quit(-1)
|
||||||
|
except CatchableError as e:
|
||||||
|
error "Unexpected error", description = e.msg
|
||||||
|
quit(-1)
|
||||||
18
interop/transport/Dockerfile
Normal file
18
interop/transport/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# syntax=docker/dockerfile:1.5-labs
|
||||||
|
FROM nimlang/nim:latest as builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY .pinned libp2p.nimble nim-libp2p/
|
||||||
|
|
||||||
|
RUN --mount=type=cache,target=/var/cache/apt apt-get update && apt-get install -y libssl-dev
|
||||||
|
|
||||||
|
RUN cd nim-libp2p && nimble install_pinned && nimble install redis -y
|
||||||
|
|
||||||
|
COPY . nim-libp2p/
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
cd nim-libp2p && \
|
||||||
|
nim c --skipProjCfg --skipParentCfg --NimblePath:./nimbledeps/pkgs2 -p:nim-libp2p --mm:refc -d:libp2p_quic_support -d:chronicles_log_level=WARN -d:chronicles_default_output_device=stderr --threads:off ./interop/transport/main.nim
|
||||||
|
|
||||||
|
ENTRYPOINT ["/app/nim-libp2p/interop/transport/main"]
|
||||||
@@ -42,29 +42,26 @@ proc main() {.async.} =
|
|||||||
discard switchBuilder.withTcpTransport().withAddress(
|
discard switchBuilder.withTcpTransport().withAddress(
|
||||||
MultiAddress.init("/ip4/" & ip & "/tcp/0").tryGet()
|
MultiAddress.init("/ip4/" & ip & "/tcp/0").tryGet()
|
||||||
)
|
)
|
||||||
of "ws":
|
of "quic-v1":
|
||||||
discard switchBuilder
|
discard switchBuilder.withQuicTransport().withAddress(
|
||||||
.withTransport(
|
MultiAddress.init("/ip4/" & ip & "/udp/0/quic-v1").tryGet()
|
||||||
proc(upgr: Upgrade): Transport =
|
)
|
||||||
WsTransport.new(upgr)
|
of "ws":
|
||||||
|
discard switchBuilder.withWsTransport().withAddress(
|
||||||
|
MultiAddress.init("/ip4/" & ip & "/tcp/0/ws").tryGet()
|
||||||
)
|
)
|
||||||
.withAddress(MultiAddress.init("/ip4/" & ip & "/tcp/0/ws").tryGet())
|
|
||||||
else:
|
else:
|
||||||
doAssert false
|
doAssert false
|
||||||
|
|
||||||
case secureChannel
|
case secureChannel
|
||||||
of "noise":
|
of "noise":
|
||||||
discard switchBuilder.withNoise()
|
discard switchBuilder.withNoise()
|
||||||
else:
|
|
||||||
doAssert false
|
|
||||||
|
|
||||||
case muxer
|
case muxer
|
||||||
of "yamux":
|
of "yamux":
|
||||||
discard switchBuilder.withYamux()
|
discard switchBuilder.withYamux()
|
||||||
of "mplex":
|
of "mplex":
|
||||||
discard switchBuilder.withMplex()
|
discard switchBuilder.withMplex()
|
||||||
else:
|
|
||||||
doAssert false
|
|
||||||
|
|
||||||
let
|
let
|
||||||
rng = newRng()
|
rng = newRng()
|
||||||
@@ -83,7 +80,7 @@ proc main() {.async.} =
|
|||||||
try:
|
try:
|
||||||
redisClient.bLPop(@["listenerAddr"], testTimeout.seconds.int)[1]
|
redisClient.bLPop(@["listenerAddr"], testTimeout.seconds.int)[1]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise newException(CatchableError, e.msg)
|
raise newException(CatchableError, "Exception calling bLPop: " & e.msg, e)
|
||||||
let
|
let
|
||||||
remoteAddr = MultiAddress.init(listenerAddr).tryGet()
|
remoteAddr = MultiAddress.init(listenerAddr).tryGet()
|
||||||
dialingStart = Moment.now()
|
dialingStart = Moment.now()
|
||||||
@@ -99,7 +96,18 @@ proc main() {.async.} =
|
|||||||
pingRTTMilllis: float(pingDelay.milliseconds),
|
pingRTTMilllis: float(pingDelay.milliseconds),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
quit(0)
|
|
||||||
|
|
||||||
discard waitFor(main().withTimeout(testTimeout))
|
try:
|
||||||
quit(1)
|
proc mainAsync(): Future[string] {.async.} =
|
||||||
|
# mainAsync wraps main and returns some value, as otherwise
|
||||||
|
# 'waitFor(fut)' has no type (or is ambiguous)
|
||||||
|
await main()
|
||||||
|
return "done"
|
||||||
|
|
||||||
|
discard waitFor(mainAsync().wait(testTimeout))
|
||||||
|
except AsyncTimeoutError as e:
|
||||||
|
error "Program execution timed out", description = e.msg
|
||||||
|
quit(-1)
|
||||||
|
except CatchableError as e:
|
||||||
|
error "Unexpected error", description = e.msg
|
||||||
|
quit(-1)
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
"containerImageID": "nim-libp2p-head",
|
"containerImageID": "nim-libp2p-head",
|
||||||
"transports": [
|
"transports": [
|
||||||
"tcp",
|
"tcp",
|
||||||
"ws"
|
"ws",
|
||||||
|
"quic-v1"
|
||||||
],
|
],
|
||||||
"secureChannels": [
|
"secureChannels": [
|
||||||
"noise"
|
"noise"
|
||||||
@@ -17,7 +17,7 @@ when defined(nimdoc):
|
|||||||
## stay backward compatible during the Major version, whereas private ones can
|
## stay backward compatible during the Major version, whereas private ones can
|
||||||
## change at each new Minor version.
|
## change at each new Minor version.
|
||||||
##
|
##
|
||||||
## If you're new to nim-libp2p, you can find a tutorial `here<https://status-im.github.io/nim-libp2p/docs/tutorial_1_connect/>`_
|
## If you're new to nim-libp2p, you can find a tutorial `here<https://vacp2p.github.io/nim-libp2p/docs/tutorial_1_connect/>`_
|
||||||
## that can help you get started.
|
## that can help you get started.
|
||||||
|
|
||||||
# Import stuff for doc
|
# Import stuff for doc
|
||||||
@@ -52,7 +52,6 @@ else:
|
|||||||
stream/connection,
|
stream/connection,
|
||||||
transports/transport,
|
transports/transport,
|
||||||
transports/tcptransport,
|
transports/tcptransport,
|
||||||
transports/quictransport,
|
|
||||||
protocols/secure/noise,
|
protocols/secure/noise,
|
||||||
cid,
|
cid,
|
||||||
multihash,
|
multihash,
|
||||||
@@ -71,3 +70,7 @@ else:
|
|||||||
minprotobuf, switch, peerid, peerinfo, connection, multiaddress, crypto, lpstream,
|
minprotobuf, switch, peerid, peerinfo, connection, multiaddress, crypto, lpstream,
|
||||||
bufferstream, muxer, mplex, transport, tcptransport, noise, errors, cid, multihash,
|
bufferstream, muxer, mplex, transport, tcptransport, noise, errors, cid, multihash,
|
||||||
multicodec, builders, pubsub
|
multicodec, builders, pubsub
|
||||||
|
|
||||||
|
when defined(libp2p_quic_support):
|
||||||
|
import libp2p/transports/quictransport
|
||||||
|
export quictransport
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
mode = ScriptMode.Verbose
|
mode = ScriptMode.Verbose
|
||||||
|
|
||||||
packageName = "libp2p"
|
packageName = "libp2p"
|
||||||
version = "1.7.0"
|
version = "1.13.0"
|
||||||
author = "Status Research & Development GmbH"
|
author = "Status Research & Development GmbH"
|
||||||
description = "LibP2P implementation"
|
description = "LibP2P implementation"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
skipDirs = @["tests", "examples", "Nim", "tools", "scripts", "docs"]
|
skipDirs = @["tests", "examples", "Nim", "tools", "scripts", "docs"]
|
||||||
|
|
||||||
requires "nim >= 1.6.0",
|
requires "nim >= 2.0.0",
|
||||||
"nimcrypto >= 0.6.0 & < 0.7.0", "dnsclient >= 0.3.0 & < 0.4.0", "bearssl >= 0.2.5",
|
"nimcrypto >= 0.6.0 & < 0.7.0", "dnsclient >= 0.3.0 & < 0.4.0", "bearssl >= 0.2.5",
|
||||||
"chronicles >= 0.10.2", "chronos >= 4.0.3", "metrics", "secp256k1", "stew#head",
|
"chronicles >= 0.11.0 & < 0.12.0", "chronos >= 4.0.4", "metrics", "secp256k1",
|
||||||
"websock", "unittest2",
|
"stew >= 0.4.0", "websock >= 0.2.0", "unittest2", "results", "quic >= 0.2.16",
|
||||||
"https://github.com/status-im/nim-quic.git#ddcb31ffb74b5460ab37fd13547eca90594248bc"
|
"https://github.com/vacp2p/nim-jwt.git#18f8378de52b241f321c1f9ea905456e89b95c6f"
|
||||||
|
|
||||||
let nimc = getEnv("NIMC", "nim") # Which nim compiler to use
|
let nimc = getEnv("NIMC", "nim") # Which nim compiler to use
|
||||||
let lang = getEnv("NIMLANG", "c") # Which backend (c/cpp/js)
|
let lang = getEnv("NIMLANG", "c") # Which backend (c/cpp/js)
|
||||||
@@ -25,16 +25,12 @@ let cfg =
|
|||||||
|
|
||||||
import hashes, strutils
|
import hashes, strutils
|
||||||
|
|
||||||
proc runTest(
|
proc runTest(filename: string, moreoptions: string = "") =
|
||||||
filename: string, verify: bool = true, sign: bool = true, moreoptions: string = ""
|
|
||||||
) =
|
|
||||||
var excstr = nimc & " " & lang & " -d:debug " & cfg & " " & flags
|
var excstr = nimc & " " & lang & " -d:debug " & cfg & " " & flags
|
||||||
excstr.add(" -d:libp2p_pubsub_sign=" & $sign)
|
|
||||||
excstr.add(" -d:libp2p_pubsub_verify=" & $verify)
|
|
||||||
excstr.add(" " & moreoptions & " ")
|
excstr.add(" " & moreoptions & " ")
|
||||||
if getEnv("CICOV").len > 0:
|
if getEnv("CICOV").len > 0:
|
||||||
excstr &= " --nimcache:nimcache/" & filename & "-" & $excstr.hash
|
excstr &= " --nimcache:nimcache/" & filename & "-" & $excstr.hash
|
||||||
exec excstr & " -r " & " tests/" & filename
|
exec excstr & " -r -d:libp2p_quic_support -d:libp2p_autotls_support tests/" & filename
|
||||||
rmFile "tests/" & filename.toExe
|
rmFile "tests/" & filename.toExe
|
||||||
|
|
||||||
proc buildSample(filename: string, run = false, extraFlags = "") =
|
proc buildSample(filename: string, run = false, extraFlags = "") =
|
||||||
@@ -53,58 +49,19 @@ proc tutorialToMd(filename: string) =
|
|||||||
task testnative, "Runs libp2p native tests":
|
task testnative, "Runs libp2p native tests":
|
||||||
runTest("testnative")
|
runTest("testnative")
|
||||||
|
|
||||||
task testdaemon, "Runs daemon tests":
|
|
||||||
runTest("testdaemon")
|
|
||||||
|
|
||||||
task testinterop, "Runs interop tests":
|
|
||||||
runTest("testinterop")
|
|
||||||
|
|
||||||
task testpubsub, "Runs pubsub tests":
|
task testpubsub, "Runs pubsub tests":
|
||||||
runTest(
|
runTest("pubsub/testpubsub", "-d:libp2p_gossipsub_1_4")
|
||||||
"pubsub/testgossipinternal",
|
|
||||||
sign = false,
|
|
||||||
verify = false,
|
|
||||||
moreoptions = "-d:pubsub_internal_testing",
|
|
||||||
)
|
|
||||||
runTest("pubsub/testpubsub")
|
|
||||||
runTest("pubsub/testpubsub", sign = false, verify = false)
|
|
||||||
runTest(
|
|
||||||
"pubsub/testpubsub",
|
|
||||||
sign = false,
|
|
||||||
verify = false,
|
|
||||||
moreoptions = "-d:libp2p_pubsub_anonymize=true",
|
|
||||||
)
|
|
||||||
|
|
||||||
task testpubsub_slim, "Runs pubsub tests":
|
|
||||||
runTest(
|
|
||||||
"pubsub/testgossipinternal",
|
|
||||||
sign = false,
|
|
||||||
verify = false,
|
|
||||||
moreoptions = "-d:pubsub_internal_testing",
|
|
||||||
)
|
|
||||||
runTest("pubsub/testpubsub")
|
|
||||||
|
|
||||||
task testfilter, "Run PKI filter test":
|
task testfilter, "Run PKI filter test":
|
||||||
runTest("testpkifilter", moreoptions = "-d:libp2p_pki_schemes=\"secp256k1\"")
|
runTest("testpkifilter")
|
||||||
runTest("testpkifilter", moreoptions = "-d:libp2p_pki_schemes=\"secp256k1;ed25519\"")
|
|
||||||
runTest(
|
|
||||||
"testpkifilter", moreoptions = "-d:libp2p_pki_schemes=\"secp256k1;ed25519;ecnist\""
|
|
||||||
)
|
|
||||||
runTest("testpkifilter", moreoptions = "-d:libp2p_pki_schemes=")
|
runTest("testpkifilter", moreoptions = "-d:libp2p_pki_schemes=")
|
||||||
|
|
||||||
task test, "Runs the test suite":
|
task testintegration, "Runs integraion tests":
|
||||||
exec "nimble testnative"
|
runTest("testintegration")
|
||||||
exec "nimble testpubsub"
|
|
||||||
exec "nimble testdaemon"
|
|
||||||
exec "nimble testinterop"
|
|
||||||
exec "nimble testfilter"
|
|
||||||
exec "nimble examples_build"
|
|
||||||
|
|
||||||
task test_slim, "Runs the (slimmed down) test suite":
|
task test, "Runs the test suite":
|
||||||
exec "nimble testnative"
|
runTest("testall")
|
||||||
exec "nimble testpubsub_slim"
|
|
||||||
exec "nimble testfilter"
|
exec "nimble testfilter"
|
||||||
exec "nimble examples_build"
|
|
||||||
|
|
||||||
task website, "Build the website":
|
task website, "Build the website":
|
||||||
tutorialToMd("examples/tutorial_1_connect.nim")
|
tutorialToMd("examples/tutorial_1_connect.nim")
|
||||||
@@ -116,19 +73,12 @@ task website, "Build the website":
|
|||||||
tutorialToMd("examples/circuitrelay.nim")
|
tutorialToMd("examples/circuitrelay.nim")
|
||||||
exec "mkdocs build"
|
exec "mkdocs build"
|
||||||
|
|
||||||
task examples_build, "Build the samples":
|
task examples, "Build and run examples":
|
||||||
buildSample("directchat")
|
exec "nimble install -y nimpng"
|
||||||
buildSample("helloworld", true)
|
exec "nimble install -y nico --passNim=--skipParentCfg"
|
||||||
buildSample("circuitrelay", true)
|
buildSample("examples_build", false, "--styleCheck:off") # build only
|
||||||
buildSample("tutorial_1_connect", true)
|
|
||||||
buildSample("tutorial_2_customproto", true)
|
buildSample("examples_run", true)
|
||||||
buildSample("tutorial_3_protobuf", true)
|
|
||||||
buildSample("tutorial_4_gossipsub", true)
|
|
||||||
buildSample("tutorial_5_discovery", true)
|
|
||||||
exec "nimble install -y nimpng@#HEAD"
|
|
||||||
# this is to fix broken build on 1.7.3, remove it when nimpng version 0.3.2 or later is released
|
|
||||||
exec "nimble install -y nico@#af99dd60bf2b395038ece815ea1012330a80d6e6"
|
|
||||||
buildSample("tutorial_6_game", false, "--styleCheck:off")
|
|
||||||
|
|
||||||
# pin system
|
# pin system
|
||||||
# while nimble lockfile
|
# while nimble lockfile
|
||||||
|
|||||||
533
libp2p/autotls/acme/api.nim
Normal file
533
libp2p/autotls/acme/api.nim
Normal file
@@ -0,0 +1,533 @@
|
|||||||
|
import json, uri
|
||||||
|
from times import DateTime, parse
|
||||||
|
import chronos/apps/http/httpclient, results, chronicles
|
||||||
|
|
||||||
|
import ./utils
|
||||||
|
import ../../crypto/crypto
|
||||||
|
import ../../crypto/rsa
|
||||||
|
|
||||||
|
export ACMEError
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "libp2p acme api"
|
||||||
|
|
||||||
|
const
|
||||||
|
LetsEncryptURL* = "https://acme-v02.api.letsencrypt.org"
|
||||||
|
LetsEncryptURLStaging* = "https://acme-staging-v02.api.letsencrypt.org"
|
||||||
|
Alg = "RS256"
|
||||||
|
DefaultChalCompletedRetries = 10
|
||||||
|
DefaultChalCompletedRetryTime = 1.seconds
|
||||||
|
DefaultFinalizeRetries = 10
|
||||||
|
DefaultFinalizeRetryTime = 1.seconds
|
||||||
|
DefaultRandStringSize = 256
|
||||||
|
ACMEHttpHeaders = [("Content-Type", "application/jose+json")]
|
||||||
|
|
||||||
|
type Authorization* = string
|
||||||
|
type Domain* = string
|
||||||
|
type Kid* = string
|
||||||
|
type Nonce* = string
|
||||||
|
|
||||||
|
type ACMEDirectory* = object
|
||||||
|
newNonce*: string
|
||||||
|
newOrder*: string
|
||||||
|
newAccount*: string
|
||||||
|
|
||||||
|
type ACMEApi* = ref object of RootObj
|
||||||
|
directory: Opt[ACMEDirectory]
|
||||||
|
session: HttpSessionRef
|
||||||
|
acmeServerURL*: Uri
|
||||||
|
|
||||||
|
type HTTPResponse* = object
|
||||||
|
body*: JsonNode
|
||||||
|
headers*: HttpTable
|
||||||
|
|
||||||
|
type JWK = object
|
||||||
|
kty: string
|
||||||
|
n: string
|
||||||
|
e: string
|
||||||
|
|
||||||
|
# whether the request uses Kid or not
|
||||||
|
type ACMERequestType = enum
|
||||||
|
ACMEJwkRequest
|
||||||
|
ACMEKidRequest
|
||||||
|
|
||||||
|
type ACMERequestHeader = object
|
||||||
|
alg: string
|
||||||
|
typ: string
|
||||||
|
nonce: Nonce
|
||||||
|
url: string
|
||||||
|
case kind: ACMERequestType
|
||||||
|
of ACMEJwkRequest:
|
||||||
|
jwk: JWK
|
||||||
|
of ACMEKidRequest:
|
||||||
|
kid: Kid
|
||||||
|
|
||||||
|
type Email = string
|
||||||
|
|
||||||
|
type ACMERegisterRequest* = object
|
||||||
|
termsOfServiceAgreed: bool
|
||||||
|
contact: seq[Email]
|
||||||
|
|
||||||
|
type ACMEAccountStatus = enum
|
||||||
|
valid = "valid"
|
||||||
|
deactivated = "deactivated"
|
||||||
|
revoked = "revoked"
|
||||||
|
|
||||||
|
type ACMERegisterResponseBody = object
|
||||||
|
status*: ACMEAccountStatus
|
||||||
|
|
||||||
|
type ACMERegisterResponse* = object
|
||||||
|
kid*: Kid
|
||||||
|
status*: ACMEAccountStatus
|
||||||
|
|
||||||
|
type ACMEChallengeStatus* {.pure.} = enum
|
||||||
|
PENDING = "pending"
|
||||||
|
PROCESSING = "processing"
|
||||||
|
VALID = "valid"
|
||||||
|
INVALID = "invalid"
|
||||||
|
|
||||||
|
type ACMEOrderStatus* {.pure.} = enum
|
||||||
|
PENDING = "pending"
|
||||||
|
READY = "ready"
|
||||||
|
PROCESSING = "processing"
|
||||||
|
VALID = "valid"
|
||||||
|
INVALID = "invalid"
|
||||||
|
|
||||||
|
type ACMEChallengeType* {.pure.} = enum
|
||||||
|
DNS01 = "dns-01"
|
||||||
|
HTTP01 = "http-01"
|
||||||
|
TLSALPN01 = "tls-alpn-01"
|
||||||
|
|
||||||
|
type ACMEChallengeToken* = string
|
||||||
|
|
||||||
|
type ACMEChallenge* = object
|
||||||
|
url*: string
|
||||||
|
`type`*: ACMEChallengeType
|
||||||
|
status*: ACMEChallengeStatus
|
||||||
|
token*: ACMEChallengeToken
|
||||||
|
|
||||||
|
type ACMEChallengeIdentifier = object
|
||||||
|
`type`: string
|
||||||
|
value: string
|
||||||
|
|
||||||
|
type ACMEChallengeRequest = object
|
||||||
|
identifiers: seq[ACMEChallengeIdentifier]
|
||||||
|
|
||||||
|
type ACMEChallengeResponseBody = object
|
||||||
|
status: ACMEOrderStatus
|
||||||
|
authorizations: seq[Authorization]
|
||||||
|
finalize: string
|
||||||
|
|
||||||
|
type ACMEChallengeResponse* = object
|
||||||
|
status*: ACMEOrderStatus
|
||||||
|
authorizations*: seq[Authorization]
|
||||||
|
finalize*: string
|
||||||
|
order*: string
|
||||||
|
|
||||||
|
type ACMEChallengeResponseWrapper* = object
|
||||||
|
finalize*: string
|
||||||
|
order*: string
|
||||||
|
dns01*: ACMEChallenge
|
||||||
|
|
||||||
|
type ACMEAuthorizationsResponse* = object
|
||||||
|
challenges*: seq[ACMEChallenge]
|
||||||
|
|
||||||
|
type ACMECompletedResponse* = object
|
||||||
|
url: string
|
||||||
|
|
||||||
|
type ACMECheckKind* = enum
|
||||||
|
ACMEOrderCheck
|
||||||
|
ACMEChallengeCheck
|
||||||
|
|
||||||
|
type ACMECheckResponse* = object
|
||||||
|
case kind: ACMECheckKind
|
||||||
|
of ACMEOrderCheck:
|
||||||
|
orderStatus: ACMEOrderStatus
|
||||||
|
of ACMEChallengeCheck:
|
||||||
|
chalStatus: ACMEChallengeStatus
|
||||||
|
retryAfter: Duration
|
||||||
|
|
||||||
|
type ACMEFinalizeResponse* = object
|
||||||
|
status: ACMEOrderStatus
|
||||||
|
|
||||||
|
type ACMEOrderResponse* = object
|
||||||
|
certificate: string
|
||||||
|
expires: string
|
||||||
|
|
||||||
|
type ACMECertificateResponse* = object
|
||||||
|
rawCertificate*: string
|
||||||
|
certificateExpiry*: DateTime
|
||||||
|
|
||||||
|
type ACMECertificate* = object
|
||||||
|
rawCertificate*: string
|
||||||
|
certificateExpiry*: DateTime
|
||||||
|
certKeyPair*: KeyPair
|
||||||
|
|
||||||
|
when defined(libp2p_autotls_support):
|
||||||
|
import options, sequtils, strutils, jwt, bearssl/pem
|
||||||
|
|
||||||
|
template handleError*(msg: string, body: untyped): untyped =
|
||||||
|
try:
|
||||||
|
body
|
||||||
|
except ACMEError as exc:
|
||||||
|
raise exc
|
||||||
|
except CancelledError as exc:
|
||||||
|
raise exc
|
||||||
|
except JsonKindError as exc:
|
||||||
|
raise newException(ACMEError, msg & ": Failed to decode JSON", exc)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise newException(ACMEError, msg & ": Failed to decode JSON", exc)
|
||||||
|
except HttpError as exc:
|
||||||
|
raise newException(ACMEError, msg & ": Failed to connect to ACME server", exc)
|
||||||
|
except CatchableError as exc:
|
||||||
|
raise newException(ACMEError, msg & ": Unexpected error", exc)
|
||||||
|
|
||||||
|
method post*(
|
||||||
|
self: ACMEApi, uri: Uri, payload: string
|
||||||
|
): Future[HTTPResponse] {.
|
||||||
|
async: (raises: [ACMEError, HttpError, CancelledError]), base
|
||||||
|
.}
|
||||||
|
|
||||||
|
method get*(
|
||||||
|
self: ACMEApi, uri: Uri
|
||||||
|
): Future[HTTPResponse] {.
|
||||||
|
async: (raises: [ACMEError, HttpError, CancelledError]), base
|
||||||
|
.}
|
||||||
|
|
||||||
|
proc new*(
|
||||||
|
T: typedesc[ACMEApi], acmeServerURL: Uri = parseUri(LetsEncryptURL)
|
||||||
|
): ACMEApi =
|
||||||
|
let session = HttpSessionRef.new()
|
||||||
|
|
||||||
|
ACMEApi(
|
||||||
|
session: session, directory: Opt.none(ACMEDirectory), acmeServerURL: acmeServerURL
|
||||||
|
)
|
||||||
|
|
||||||
|
proc getDirectory(
|
||||||
|
self: ACMEApi
|
||||||
|
): Future[ACMEDirectory] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
handleError("getDirectory"):
|
||||||
|
self.directory.valueOr:
|
||||||
|
let acmeResponse = await self.get(self.acmeServerURL / "directory")
|
||||||
|
let directory = acmeResponse.body.to(ACMEDirectory)
|
||||||
|
self.directory = Opt.some(directory)
|
||||||
|
directory
|
||||||
|
|
||||||
|
method requestNonce*(
|
||||||
|
self: ACMEApi
|
||||||
|
): Future[Nonce] {.async: (raises: [ACMEError, CancelledError]), base.} =
|
||||||
|
handleError("requestNonce"):
|
||||||
|
let acmeResponse = await self.get(parseUri((await self.getDirectory()).newNonce))
|
||||||
|
Nonce(acmeResponse.headers.keyOrError("Replay-Nonce"))
|
||||||
|
|
||||||
|
# TODO: save n and e in account so we don't have to recalculate every time
|
||||||
|
proc acmeHeader(
|
||||||
|
self: ACMEApi, uri: Uri, key: KeyPair, needsJwk: bool, kid: Opt[Kid]
|
||||||
|
): Future[ACMERequestHeader] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
if not needsJwk and kid.isNone():
|
||||||
|
raise newException(ACMEError, "kid not set")
|
||||||
|
|
||||||
|
if key.pubkey.scheme != PKScheme.RSA or key.seckey.scheme != PKScheme.RSA:
|
||||||
|
raise newException(ACMEError, "Unsupported signing key type")
|
||||||
|
|
||||||
|
let newNonce = await self.requestNonce()
|
||||||
|
if needsJwk:
|
||||||
|
let pubkey = key.pubkey.rsakey
|
||||||
|
let nArray = @(getArray(pubkey.buffer, pubkey.key.n, pubkey.key.nlen))
|
||||||
|
let eArray = @(getArray(pubkey.buffer, pubkey.key.e, pubkey.key.elen))
|
||||||
|
ACMERequestHeader(
|
||||||
|
kind: ACMEJwkRequest,
|
||||||
|
alg: Alg,
|
||||||
|
typ: "JWT",
|
||||||
|
nonce: newNonce,
|
||||||
|
url: $uri,
|
||||||
|
jwk: JWK(kty: "RSA", n: base64UrlEncode(nArray), e: base64UrlEncode(eArray)),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
ACMERequestHeader(
|
||||||
|
kind: ACMEKidRequest,
|
||||||
|
alg: Alg,
|
||||||
|
typ: "JWT",
|
||||||
|
nonce: newNonce,
|
||||||
|
url: $uri,
|
||||||
|
kid: kid.get(),
|
||||||
|
)
|
||||||
|
|
||||||
|
method post*(
|
||||||
|
self: ACMEApi, uri: Uri, payload: string
|
||||||
|
): Future[HTTPResponse] {.
|
||||||
|
async: (raises: [ACMEError, HttpError, CancelledError]), base
|
||||||
|
.} =
|
||||||
|
let rawResponse = await HttpClientRequestRef
|
||||||
|
.post(self.session, $uri, body = payload, headers = ACMEHttpHeaders)
|
||||||
|
.get()
|
||||||
|
.send()
|
||||||
|
let body = await rawResponse.getResponseBody()
|
||||||
|
HTTPResponse(body: body, headers: rawResponse.headers)
|
||||||
|
|
||||||
|
method get*(
|
||||||
|
self: ACMEApi, uri: Uri
|
||||||
|
): Future[HTTPResponse] {.
|
||||||
|
async: (raises: [ACMEError, HttpError, CancelledError]), base
|
||||||
|
.} =
|
||||||
|
let rawResponse = await HttpClientRequestRef.get(self.session, $uri).get().send()
|
||||||
|
let body = await rawResponse.getResponseBody()
|
||||||
|
HTTPResponse(body: body, headers: rawResponse.headers)
|
||||||
|
|
||||||
|
proc createSignedAcmeRequest(
|
||||||
|
self: ACMEApi,
|
||||||
|
uri: Uri,
|
||||||
|
payload: auto,
|
||||||
|
key: KeyPair,
|
||||||
|
needsJwk: bool = false,
|
||||||
|
kid: Opt[Kid] = Opt.none(Kid),
|
||||||
|
): Future[string] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
if key.pubkey.scheme != PKScheme.RSA or key.seckey.scheme != PKScheme.RSA:
|
||||||
|
raise newException(ACMEError, "Unsupported signing key type")
|
||||||
|
|
||||||
|
let acmeHeader = await self.acmeHeader(uri, key, needsJwk, kid)
|
||||||
|
handleError("createSignedAcmeRequest"):
|
||||||
|
var token = toJWT(%*{"header": acmeHeader, "claims": payload})
|
||||||
|
let derPrivKey = key.seckey.rsakey.getBytes.get
|
||||||
|
let pemPrivKey: string = pemEncode(derPrivKey, "PRIVATE KEY")
|
||||||
|
token.sign(pemPrivKey)
|
||||||
|
$token.toFlattenedJson()
|
||||||
|
|
||||||
|
proc requestRegister*(
|
||||||
|
self: ACMEApi, key: KeyPair
|
||||||
|
): Future[ACMERegisterResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
let registerRequest = ACMERegisterRequest(termsOfServiceAgreed: true)
|
||||||
|
handleError("acmeRegister"):
|
||||||
|
let payload = await self.createSignedAcmeRequest(
|
||||||
|
parseUri((await self.getDirectory()).newAccount),
|
||||||
|
registerRequest,
|
||||||
|
key,
|
||||||
|
needsJwk = true,
|
||||||
|
)
|
||||||
|
let acmeResponse =
|
||||||
|
await self.post(parseUri((await self.getDirectory()).newAccount), payload)
|
||||||
|
let acmeResponseBody = acmeResponse.body.to(ACMERegisterResponseBody)
|
||||||
|
|
||||||
|
ACMERegisterResponse(
|
||||||
|
status: acmeResponseBody.status,
|
||||||
|
kid: acmeResponse.headers.keyOrError("location"),
|
||||||
|
)
|
||||||
|
|
||||||
|
proc requestNewOrder*(
|
||||||
|
self: ACMEApi, domains: seq[Domain], key: KeyPair, kid: Kid
|
||||||
|
): Future[ACMEChallengeResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
# request challenge from ACME server
|
||||||
|
let orderRequest = ACMEChallengeRequest(
|
||||||
|
identifiers: domains.mapIt(ACMEChallengeIdentifier(`type`: "dns", value: it))
|
||||||
|
)
|
||||||
|
handleError("requestNewOrder"):
|
||||||
|
let payload = await self.createSignedAcmeRequest(
|
||||||
|
parseUri((await self.getDirectory()).newOrder),
|
||||||
|
orderRequest,
|
||||||
|
key,
|
||||||
|
kid = Opt.some(kid),
|
||||||
|
)
|
||||||
|
let acmeResponse =
|
||||||
|
await self.post(parseUri((await self.getDirectory()).newOrder), payload)
|
||||||
|
let challengeResponseBody = acmeResponse.body.to(ACMEChallengeResponseBody)
|
||||||
|
if challengeResponseBody.authorizations.len == 0:
|
||||||
|
raise newException(ACMEError, "Authorizations field is empty")
|
||||||
|
ACMEChallengeResponse(
|
||||||
|
status: challengeResponseBody.status,
|
||||||
|
authorizations: challengeResponseBody.authorizations,
|
||||||
|
finalize: challengeResponseBody.finalize,
|
||||||
|
order: acmeResponse.headers.keyOrError("location"),
|
||||||
|
)
|
||||||
|
|
||||||
|
proc requestAuthorizations*(
|
||||||
|
self: ACMEApi, authorizations: seq[Authorization], key: KeyPair, kid: Kid
|
||||||
|
): Future[ACMEAuthorizationsResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
handleError("requestAuthorizations"):
|
||||||
|
doAssert authorizations.len > 0
|
||||||
|
let acmeResponse = await self.get(parseUri(authorizations[0]))
|
||||||
|
acmeResponse.body.to(ACMEAuthorizationsResponse)
|
||||||
|
|
||||||
|
proc requestChallenge*(
|
||||||
|
self: ACMEApi, domains: seq[Domain], key: KeyPair, kid: Kid
|
||||||
|
): Future[ACMEChallengeResponseWrapper] {.
|
||||||
|
async: (raises: [ACMEError, CancelledError])
|
||||||
|
.} =
|
||||||
|
let orderResponse = await self.requestNewOrder(domains, key, kid)
|
||||||
|
if orderResponse.status != ACMEOrderStatus.PENDING and
|
||||||
|
orderResponse.status != ACMEOrderStatus.READY:
|
||||||
|
# ready is a valid status when renewing certs before expiry
|
||||||
|
raise
|
||||||
|
newException(ACMEError, "Invalid new order status: " & $orderResponse.status)
|
||||||
|
|
||||||
|
let authorizationsResponse =
|
||||||
|
await self.requestAuthorizations(orderResponse.authorizations, key, kid)
|
||||||
|
if authorizationsResponse.challenges.len == 0:
|
||||||
|
raise newException(ACMEError, "No challenges received")
|
||||||
|
|
||||||
|
return ACMEChallengeResponseWrapper(
|
||||||
|
finalize: orderResponse.finalize,
|
||||||
|
order: orderResponse.order,
|
||||||
|
dns01: authorizationsResponse.challenges.filterIt(
|
||||||
|
it.`type` == ACMEChallengeType.DNS01
|
||||||
|
)[0],
|
||||||
|
# getting the first element is safe since we checked that authorizationsResponse.challenges.len != 0
|
||||||
|
)
|
||||||
|
|
||||||
|
proc requestCheck*(
|
||||||
|
self: ACMEApi, checkURL: Uri, checkKind: ACMECheckKind, key: KeyPair, kid: Kid
|
||||||
|
): Future[ACMECheckResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
handleError("requestCheck"):
|
||||||
|
let acmeResponse = await self.get(checkURL)
|
||||||
|
let retryAfter =
|
||||||
|
try:
|
||||||
|
parseInt(acmeResponse.headers.keyOrError("Retry-After")).seconds
|
||||||
|
except ValueError:
|
||||||
|
DefaultChalCompletedRetryTime
|
||||||
|
|
||||||
|
case checkKind
|
||||||
|
of ACMEOrderCheck:
|
||||||
|
try:
|
||||||
|
ACMECheckResponse(
|
||||||
|
kind: checkKind,
|
||||||
|
orderStatus: parseEnum[ACMEOrderStatus](acmeResponse.body["status"].getStr),
|
||||||
|
retryAfter: retryAfter,
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
raise newException(
|
||||||
|
ACMEError, "Invalid order status: " & acmeResponse.body["status"].getStr
|
||||||
|
)
|
||||||
|
of ACMEChallengeCheck:
|
||||||
|
try:
|
||||||
|
ACMECheckResponse(
|
||||||
|
kind: checkKind,
|
||||||
|
chalStatus:
|
||||||
|
parseEnum[ACMEChallengeStatus](acmeResponse.body["status"].getStr),
|
||||||
|
retryAfter: retryAfter,
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
raise newException(
|
||||||
|
ACMEError, "Invalid order status: " & acmeResponse.body["status"].getStr
|
||||||
|
)
|
||||||
|
|
||||||
|
proc sendChallengeCompleted*(
|
||||||
|
self: ACMEApi, chalURL: Uri, key: KeyPair, kid: Kid
|
||||||
|
): Future[ACMECompletedResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
handleError("sendChallengeCompleted"):
|
||||||
|
let payload =
|
||||||
|
await self.createSignedAcmeRequest(chalURL, %*{}, key, kid = Opt.some(kid))
|
||||||
|
let acmeResponse = await self.post(chalURL, payload)
|
||||||
|
acmeResponse.body.to(ACMECompletedResponse)
|
||||||
|
|
||||||
|
proc checkChallengeCompleted*(
|
||||||
|
self: ACMEApi,
|
||||||
|
checkURL: Uri,
|
||||||
|
key: KeyPair,
|
||||||
|
kid: Kid,
|
||||||
|
retries: int = DefaultChalCompletedRetries,
|
||||||
|
): Future[bool] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
for i in 0 .. retries:
|
||||||
|
let checkResponse =
|
||||||
|
await self.requestCheck(checkURL, ACMEChallengeCheck, key, kid)
|
||||||
|
case checkResponse.chalStatus
|
||||||
|
of ACMEChallengeStatus.PENDING:
|
||||||
|
await sleepAsync(checkResponse.retryAfter) # try again after some delay
|
||||||
|
of ACMEChallengeStatus.VALID:
|
||||||
|
return true
|
||||||
|
else:
|
||||||
|
raise newException(
|
||||||
|
ACMEError,
|
||||||
|
"Failed challenge completion: expected 'valid', got '" &
|
||||||
|
$checkResponse.chalStatus & "'",
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc completeChallenge*(
|
||||||
|
self: ACMEApi,
|
||||||
|
chalURL: Uri,
|
||||||
|
key: KeyPair,
|
||||||
|
kid: Kid,
|
||||||
|
retries: int = DefaultChalCompletedRetries,
|
||||||
|
): Future[bool] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
let completedResponse = await self.sendChallengeCompleted(chalURL, key, kid)
|
||||||
|
# check until acme server is done (poll validation)
|
||||||
|
return await self.checkChallengeCompleted(chalURL, key, kid, retries = retries)
|
||||||
|
|
||||||
|
proc requestFinalize*(
|
||||||
|
self: ACMEApi,
|
||||||
|
domain: Domain,
|
||||||
|
finalize: Uri,
|
||||||
|
certKeyPair: KeyPair,
|
||||||
|
key: KeyPair,
|
||||||
|
kid: Kid,
|
||||||
|
): Future[ACMEFinalizeResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
handleError("requestFinalize"):
|
||||||
|
let payload = await self.createSignedAcmeRequest(
|
||||||
|
finalize, %*{"csr": createCSR(domain, certKeyPair)}, key, kid = Opt.some(kid)
|
||||||
|
)
|
||||||
|
let acmeResponse = await self.post(finalize, payload)
|
||||||
|
# server responds with updated order response
|
||||||
|
acmeResponse.body.to(ACMEFinalizeResponse)
|
||||||
|
|
||||||
|
proc checkCertFinalized*(
|
||||||
|
self: ACMEApi,
|
||||||
|
order: Uri,
|
||||||
|
key: KeyPair,
|
||||||
|
kid: Kid,
|
||||||
|
retries: int = DefaultChalCompletedRetries,
|
||||||
|
): Future[bool] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
for i in 0 .. retries:
|
||||||
|
let checkResponse = await self.requestCheck(order, ACMEOrderCheck, key, kid)
|
||||||
|
case checkResponse.orderStatus
|
||||||
|
of ACMEOrderStatus.VALID:
|
||||||
|
return true
|
||||||
|
of ACMEOrderStatus.PROCESSING:
|
||||||
|
await sleepAsync(checkResponse.retryAfter) # try again after some delay
|
||||||
|
else:
|
||||||
|
error "Failed certificate finalization",
|
||||||
|
description = "expected 'valid', got '" & $checkResponse.orderStatus & "'"
|
||||||
|
return false # do not try again
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc certificateFinalized*(
|
||||||
|
self: ACMEApi,
|
||||||
|
domain: Domain,
|
||||||
|
finalize: Uri,
|
||||||
|
order: Uri,
|
||||||
|
certKeyPair: KeyPair,
|
||||||
|
key: KeyPair,
|
||||||
|
kid: Kid,
|
||||||
|
retries: int = DefaultFinalizeRetries,
|
||||||
|
): Future[bool] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
let finalizeResponse =
|
||||||
|
await self.requestFinalize(domain, finalize, certKeyPair, key, kid)
|
||||||
|
# keep checking order until cert is valid (done)
|
||||||
|
return await self.checkCertFinalized(order, key, kid, retries = retries)
|
||||||
|
|
||||||
|
proc requestGetOrder*(
|
||||||
|
self: ACMEApi, order: Uri
|
||||||
|
): Future[ACMEOrderResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
handleError("requestGetOrder"):
|
||||||
|
let acmeResponse = await self.get(order)
|
||||||
|
acmeResponse.body.to(ACMEOrderResponse)
|
||||||
|
|
||||||
|
proc downloadCertificate*(
|
||||||
|
self: ACMEApi, order: Uri
|
||||||
|
): Future[ACMECertificateResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
let orderResponse = await self.requestGetOrder(order)
|
||||||
|
|
||||||
|
handleError("downloadCertificate"):
|
||||||
|
let rawResponse = await HttpClientRequestRef
|
||||||
|
.get(self.session, orderResponse.certificate)
|
||||||
|
.get()
|
||||||
|
.send()
|
||||||
|
ACMECertificateResponse(
|
||||||
|
rawCertificate: bytesToString(await rawResponse.getBodyBytes()),
|
||||||
|
certificateExpiry: parse(orderResponse.expires, "yyyy-MM-dd'T'HH:mm:ss'Z'"),
|
||||||
|
)
|
||||||
|
|
||||||
|
proc close*(self: ACMEApi) {.async: (raises: [CancelledError]).} =
|
||||||
|
await self.session.closeWait()
|
||||||
|
|
||||||
|
else:
|
||||||
|
{.hint: "autotls disabled. Use -d:libp2p_autotls_support".}
|
||||||
98
libp2p/autotls/acme/client.nim
Normal file
98
libp2p/autotls/acme/client.nim
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# Nim-Libp2p
|
||||||
|
# Copyright (c) 2025 Status Research & Development GmbH
|
||||||
|
# Licensed under either of
|
||||||
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
# at your option.
|
||||||
|
# This file may not be copied, modified, or distributed except according to
|
||||||
|
# those terms.
|
||||||
|
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
|
import chronicles
|
||||||
|
import ../../crypto/crypto
|
||||||
|
import ./api
|
||||||
|
|
||||||
|
export api
|
||||||
|
|
||||||
|
type KeyAuthorization* = string
|
||||||
|
|
||||||
|
type ACMEClient* = ref object
|
||||||
|
api: ACMEApi
|
||||||
|
key*: KeyPair
|
||||||
|
kid*: Kid
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "libp2p acme client"
|
||||||
|
|
||||||
|
when defined(libp2p_autotls_support):
|
||||||
|
import uri
|
||||||
|
import chronos, results, stew/byteutils
|
||||||
|
import ../../crypto/rsa
|
||||||
|
import ./utils
|
||||||
|
|
||||||
|
proc new*(
|
||||||
|
T: typedesc[ACMEClient],
|
||||||
|
rng: ref HmacDrbgContext = newRng(),
|
||||||
|
api: ACMEApi = ACMEApi.new(acmeServerURL = parseUri(LetsEncryptURL)),
|
||||||
|
key: Opt[KeyPair] = Opt.none(KeyPair),
|
||||||
|
kid: Kid = Kid(""),
|
||||||
|
): T {.raises: [].} =
|
||||||
|
let key = key.valueOr:
|
||||||
|
KeyPair.random(PKScheme.RSA, rng[]).get()
|
||||||
|
T(api: api, key: key, kid: kid)
|
||||||
|
|
||||||
|
proc getOrInitKid*(
|
||||||
|
self: ACMEClient
|
||||||
|
): Future[Kid] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
if self.kid.len == 0:
|
||||||
|
let registerResponse = await self.api.requestRegister(self.key)
|
||||||
|
self.kid = registerResponse.kid
|
||||||
|
return self.kid
|
||||||
|
|
||||||
|
proc genKeyAuthorization*(self: ACMEClient, token: string): KeyAuthorization =
|
||||||
|
base64UrlEncode(@(sha256.digest((token & "." & thumbprint(self.key)).toBytes).data))
|
||||||
|
|
||||||
|
proc getChallenge*(
|
||||||
|
self: ACMEClient, domains: seq[api.Domain]
|
||||||
|
): Future[ACMEChallengeResponseWrapper] {.
|
||||||
|
async: (raises: [ACMEError, CancelledError])
|
||||||
|
.} =
|
||||||
|
await self.api.requestChallenge(domains, self.key, await self.getOrInitKid())
|
||||||
|
|
||||||
|
proc getCertificate*(
|
||||||
|
self: ACMEClient,
|
||||||
|
domain: api.Domain,
|
||||||
|
certKeyPair: KeyPair,
|
||||||
|
challenge: ACMEChallengeResponseWrapper,
|
||||||
|
): Future[ACMECertificateResponse] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
let chalURL = parseUri(challenge.dns01.url)
|
||||||
|
let orderURL = parseUri(challenge.order)
|
||||||
|
let finalizeURL = parseUri(challenge.finalize)
|
||||||
|
trace "Sending challenge completed notification"
|
||||||
|
discard await self.api.sendChallengeCompleted(
|
||||||
|
chalURL, self.key, await self.getOrInitKid()
|
||||||
|
)
|
||||||
|
|
||||||
|
trace "Checking for completed challenge"
|
||||||
|
let completed = await self.api.checkChallengeCompleted(
|
||||||
|
chalURL, self.key, await self.getOrInitKid()
|
||||||
|
)
|
||||||
|
if not completed:
|
||||||
|
raise newException(
|
||||||
|
ACMEError, "Failed to signal ACME server about challenge completion"
|
||||||
|
)
|
||||||
|
|
||||||
|
trace "Waiting for certificate to be finalized"
|
||||||
|
let finalized = await self.api.certificateFinalized(
|
||||||
|
domain, finalizeURL, orderURL, certKeyPair, self.key, await self.getOrInitKid()
|
||||||
|
)
|
||||||
|
if not finalized:
|
||||||
|
raise
|
||||||
|
newException(ACMEError, "Failed to finalize certificate for domain " & domain)
|
||||||
|
|
||||||
|
trace "Downloading certificate"
|
||||||
|
await self.api.downloadCertificate(orderURL)
|
||||||
|
|
||||||
|
proc close*(self: ACMEClient) {.async: (raises: [CancelledError]).} =
|
||||||
|
await self.api.close()
|
||||||
40
libp2p/autotls/acme/mockapi.nim
Normal file
40
libp2p/autotls/acme/mockapi.nim
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import uri
|
||||||
|
import chronos, chronos/apps/http/httpclient, json
|
||||||
|
import ./api, ./utils
|
||||||
|
|
||||||
|
export api
|
||||||
|
|
||||||
|
type MockACMEApi* = ref object of ACMEApi
|
||||||
|
mockedResponses*: seq[HTTPResponse]
|
||||||
|
|
||||||
|
proc new*(
|
||||||
|
T: typedesc[MockACMEApi]
|
||||||
|
): Future[T] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
let directory = ACMEDirectory(
|
||||||
|
newNonce: LetsEncryptURL & "/new-nonce",
|
||||||
|
newOrder: LetsEncryptURL & "/new-order",
|
||||||
|
newAccount: LetsEncryptURL & "/new-account",
|
||||||
|
)
|
||||||
|
MockACMEApi(
|
||||||
|
session: HttpSessionRef.new(),
|
||||||
|
directory: Opt.some(directory),
|
||||||
|
acmeServerURL: parseUri(LetsEncryptURL),
|
||||||
|
)
|
||||||
|
|
||||||
|
when defined(libp2p_autotls_support):
|
||||||
|
method requestNonce*(
|
||||||
|
self: MockACMEApi
|
||||||
|
): Future[Nonce] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
return $self.acmeServerURL & "/acme/1234"
|
||||||
|
|
||||||
|
method post*(
|
||||||
|
self: MockACMEApi, uri: Uri, payload: string
|
||||||
|
): Future[HTTPResponse] {.async: (raises: [ACMEError, HttpError, CancelledError]).} =
|
||||||
|
result = self.mockedResponses[0]
|
||||||
|
self.mockedResponses.delete(0)
|
||||||
|
|
||||||
|
method get*(
|
||||||
|
self: MockACMEApi, uri: Uri
|
||||||
|
): Future[HTTPResponse] {.async: (raises: [ACMEError, HttpError, CancelledError]).} =
|
||||||
|
result = self.mockedResponses[0]
|
||||||
|
self.mockedResponses.delete(0)
|
||||||
73
libp2p/autotls/acme/utils.nim
Normal file
73
libp2p/autotls/acme/utils.nim
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
import ../../errors
|
||||||
|
|
||||||
|
type ACMEError* = object of LPError
|
||||||
|
|
||||||
|
when defined(libp2p_autotls_support):
|
||||||
|
import base64, strutils, chronos/apps/http/httpclient, json
|
||||||
|
import ../../transports/tls/certificate_ffi
|
||||||
|
import ../../transports/tls/certificate
|
||||||
|
import ../../crypto/crypto
|
||||||
|
import ../../crypto/rsa
|
||||||
|
|
||||||
|
proc keyOrError*(table: HttpTable, key: string): string {.raises: [ValueError].} =
|
||||||
|
if not table.contains(key):
|
||||||
|
raise newException(ValueError, "key " & key & " not present in headers")
|
||||||
|
table.getString(key)
|
||||||
|
|
||||||
|
proc base64UrlEncode*(data: seq[byte]): string =
|
||||||
|
## Encodes data using base64url (RFC 4648 §5) — no padding, URL-safe
|
||||||
|
var encoded = base64.encode(data, safe = true)
|
||||||
|
encoded.removeSuffix("=")
|
||||||
|
encoded.removeSuffix("=")
|
||||||
|
return encoded
|
||||||
|
|
||||||
|
proc thumbprint*(key: KeyPair): string =
|
||||||
|
doAssert key.seckey.scheme == PKScheme.RSA, "unsupported keytype"
|
||||||
|
let pubkey = key.pubkey.rsakey
|
||||||
|
let nArray = @(getArray(pubkey.buffer, pubkey.key.n, pubkey.key.nlen))
|
||||||
|
let eArray = @(getArray(pubkey.buffer, pubkey.key.e, pubkey.key.elen))
|
||||||
|
|
||||||
|
let n = base64UrlEncode(nArray)
|
||||||
|
let e = base64UrlEncode(eArray)
|
||||||
|
let keyJson = %*{"e": e, "kty": "RSA", "n": n}
|
||||||
|
let digest = sha256.digest($keyJson)
|
||||||
|
return base64UrlEncode(@(digest.data))
|
||||||
|
|
||||||
|
proc getResponseBody*(
|
||||||
|
response: HttpClientResponseRef
|
||||||
|
): Future[JsonNode] {.async: (raises: [ACMEError, CancelledError]).} =
|
||||||
|
try:
|
||||||
|
let bodyBytes = await response.getBodyBytes()
|
||||||
|
if bodyBytes.len > 0:
|
||||||
|
return bytesToString(bodyBytes).parseJson()
|
||||||
|
return %*{} # empty body
|
||||||
|
except CancelledError as exc:
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
raise newException(
|
||||||
|
ACMEError, "Unexpected error occurred while getting body bytes", exc
|
||||||
|
)
|
||||||
|
except Exception as exc: # this is required for nim 1.6
|
||||||
|
raise newException(
|
||||||
|
ACMEError, "Unexpected error occurred while getting body bytes", exc
|
||||||
|
)
|
||||||
|
|
||||||
|
proc createCSR*(
|
||||||
|
domain: string, certKeyPair: KeyPair
|
||||||
|
): string {.raises: [ACMEError].} =
|
||||||
|
var certKey: cert_key_t
|
||||||
|
var certCtx: cert_context_t
|
||||||
|
var derCSR: ptr cert_buffer = nil
|
||||||
|
|
||||||
|
# convert KeyPair to cert_key_t
|
||||||
|
let rawSeckey: seq[byte] = certKeyPair.seckey.getRawBytes.valueOr:
|
||||||
|
raise newException(ACMEError, "Failed to get seckey raw bytes (DER)")
|
||||||
|
let seckeyBuffer = rawSeckey.toCertBuffer()
|
||||||
|
if cert_new_key_t(seckeyBuffer.unsafeAddr, certKey.addr) != CERT_SUCCESS:
|
||||||
|
raise newException(ACMEError, "Failed to convert key pair to cert_key_t")
|
||||||
|
|
||||||
|
# create CSR
|
||||||
|
if cert_signing_req(domain.cstring, certKey, derCSR.addr) != CERT_SUCCESS:
|
||||||
|
raise newException(ACMEError, "Failed to create CSR")
|
||||||
|
|
||||||
|
base64.encode(derCSR.toSeq, safe = true)
|
||||||
33
libp2p/autotls/mockservice.nim
Normal file
33
libp2p/autotls/mockservice.nim
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
when defined(libp2p_autotls_support):
|
||||||
|
import ./service, ./acme/client, ../peeridauth/client
|
||||||
|
|
||||||
|
import ../crypto/crypto, ../crypto/rsa, websock/websock
|
||||||
|
|
||||||
|
type MockAutotlsService* = ref object of AutotlsService
|
||||||
|
mockedCert*: TLSCertificate
|
||||||
|
mockedKey*: TLSPrivateKey
|
||||||
|
|
||||||
|
proc new*(
|
||||||
|
T: typedesc[MockAutotlsService],
|
||||||
|
rng: ref HmacDrbgContext = newRng(),
|
||||||
|
config: AutotlsConfig = AutotlsConfig.new(),
|
||||||
|
): T =
|
||||||
|
T(
|
||||||
|
acmeClient:
|
||||||
|
ACMEClient.new(api = ACMEApi.new(acmeServerURL = config.acmeServerURL)),
|
||||||
|
brokerClient: PeerIDAuthClient.new(),
|
||||||
|
bearer: Opt.none(BearerToken),
|
||||||
|
cert: Opt.none(AutotlsCert),
|
||||||
|
certReady: newAsyncEvent(),
|
||||||
|
running: newAsyncEvent(),
|
||||||
|
config: config,
|
||||||
|
rng: rng,
|
||||||
|
)
|
||||||
|
|
||||||
|
method getCertWhenReady*(
|
||||||
|
self: MockAutotlsService
|
||||||
|
): Future[AutotlsCert] {.async: (raises: [AutoTLSError, CancelledError]).} =
|
||||||
|
AutotlsCert.new(self.mockedCert, self.mockedKey, Moment.now)
|
||||||
|
|
||||||
|
method setup*(self: MockAutotlsService) {.base, async.} =
|
||||||
|
self.running.fire()
|
||||||
295
libp2p/autotls/service.nim
Normal file
295
libp2p/autotls/service.nim
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
# Nim-Libp2p
|
||||||
|
# Copyright (c) 2025 Status Research & Development GmbH
|
||||||
|
# Licensed under either of
|
||||||
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
# at your option.
|
||||||
|
# This file may not be copied, modified, or distributed except according to
|
||||||
|
# those terms.
|
||||||
|
|
||||||
|
{.push raises: [].}
|
||||||
|
{.push public.}
|
||||||
|
|
||||||
|
import chronos, chronicles, net, results
|
||||||
|
import chronos/apps/http/httpclient, bearssl/rand
|
||||||
|
|
||||||
|
import
|
||||||
|
./acme/client,
|
||||||
|
./utils,
|
||||||
|
../crypto/crypto,
|
||||||
|
../nameresolving/nameresolver,
|
||||||
|
../peeridauth/client,
|
||||||
|
../switch,
|
||||||
|
../peerinfo,
|
||||||
|
../wire
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "libp2p autotls"
|
||||||
|
|
||||||
|
export LetsEncryptURL, AutoTLSError
|
||||||
|
|
||||||
|
const
|
||||||
|
DefaultDnsServers* =
|
||||||
|
@[
|
||||||
|
initTAddress("1.1.1.1:53"),
|
||||||
|
initTAddress("1.0.0.1:53"),
|
||||||
|
initTAddress("[2606:4700:4700::1111]:53"),
|
||||||
|
]
|
||||||
|
DefaultRenewCheckTime* = 1.hours
|
||||||
|
DefaultRenewBufferTime = 1.hours
|
||||||
|
|
||||||
|
DefaultIssueRetries = 3
|
||||||
|
DefaultIssueRetryTime = 1.seconds
|
||||||
|
|
||||||
|
AutoTLSBroker* = "registration.libp2p.direct"
|
||||||
|
AutoTLSDNSServer* = "libp2p.direct"
|
||||||
|
HttpOk* = 200
|
||||||
|
HttpCreated* = 201
|
||||||
|
# NoneIp is needed because nim 1.6.16 can't do proper generic inference
|
||||||
|
NoneIp = Opt.none(IpAddress)
|
||||||
|
|
||||||
|
type SigParam = object
|
||||||
|
k: string
|
||||||
|
v: seq[byte]
|
||||||
|
|
||||||
|
type AutotlsCert* = ref object
|
||||||
|
cert*: TLSCertificate
|
||||||
|
privkey*: TLSPrivateKey
|
||||||
|
expiry*: Moment
|
||||||
|
|
||||||
|
type AutotlsConfig* = ref object
|
||||||
|
acmeServerURL*: Uri
|
||||||
|
nameResolver*: NameResolver
|
||||||
|
ipAddress: Opt[IpAddress]
|
||||||
|
renewCheckTime*: Duration
|
||||||
|
renewBufferTime*: Duration
|
||||||
|
issueRetries*: int
|
||||||
|
issueRetryTime*: Duration
|
||||||
|
|
||||||
|
type AutotlsService* = ref object of Service
|
||||||
|
acmeClient*: ACMEClient
|
||||||
|
brokerClient*: PeerIDAuthClient
|
||||||
|
bearer*: Opt[BearerToken]
|
||||||
|
cert*: Opt[AutotlsCert]
|
||||||
|
certReady*: AsyncEvent
|
||||||
|
running*: AsyncEvent
|
||||||
|
config*: AutotlsConfig
|
||||||
|
managerFut: Future[void]
|
||||||
|
peerInfo: PeerInfo
|
||||||
|
rng*: ref HmacDrbgContext
|
||||||
|
|
||||||
|
when defined(libp2p_autotls_support):
|
||||||
|
import json, sequtils, bearssl/pem
|
||||||
|
|
||||||
|
import
|
||||||
|
../crypto/rsa,
|
||||||
|
../utils/heartbeat,
|
||||||
|
../transports/transport,
|
||||||
|
../utils/ipaddr,
|
||||||
|
../transports/tcptransport,
|
||||||
|
../nameresolving/dnsresolver
|
||||||
|
|
||||||
|
proc new*(
|
||||||
|
T: typedesc[AutotlsCert],
|
||||||
|
cert: TLSCertificate,
|
||||||
|
privkey: TLSPrivateKey,
|
||||||
|
expiry: Moment,
|
||||||
|
): T =
|
||||||
|
T(cert: cert, privkey: privkey, expiry: expiry)
|
||||||
|
|
||||||
|
method getCertWhenReady*(
|
||||||
|
self: AutotlsService
|
||||||
|
): Future[AutotlsCert] {.base, async: (raises: [AutoTLSError, CancelledError]).} =
|
||||||
|
await self.certReady.wait()
|
||||||
|
return self.cert.get
|
||||||
|
|
||||||
|
proc new*(
|
||||||
|
T: typedesc[AutotlsConfig],
|
||||||
|
ipAddress: Opt[IpAddress] = NoneIp,
|
||||||
|
nameServers: seq[TransportAddress] = DefaultDnsServers,
|
||||||
|
acmeServerURL: Uri = parseUri(LetsEncryptURL),
|
||||||
|
renewCheckTime: Duration = DefaultRenewCheckTime,
|
||||||
|
renewBufferTime: Duration = DefaultRenewBufferTime,
|
||||||
|
issueRetries: int = DefaultIssueRetries,
|
||||||
|
issueRetryTime: Duration = DefaultIssueRetryTime,
|
||||||
|
): T =
|
||||||
|
T(
|
||||||
|
nameResolver: DnsResolver.new(nameServers),
|
||||||
|
acmeServerURL: acmeServerURL,
|
||||||
|
ipAddress: ipAddress,
|
||||||
|
renewCheckTime: renewCheckTime,
|
||||||
|
renewBufferTime: renewBufferTime,
|
||||||
|
issueRetries: issueRetries,
|
||||||
|
issueRetryTime: issueRetryTime,
|
||||||
|
)
|
||||||
|
|
||||||
|
proc new*(
|
||||||
|
T: typedesc[AutotlsService],
|
||||||
|
rng: ref HmacDrbgContext = newRng(),
|
||||||
|
config: AutotlsConfig = AutotlsConfig.new(),
|
||||||
|
): T =
|
||||||
|
T(
|
||||||
|
acmeClient:
|
||||||
|
ACMEClient.new(api = ACMEApi.new(acmeServerURL = config.acmeServerURL)),
|
||||||
|
brokerClient: PeerIDAuthClient.new(),
|
||||||
|
bearer: Opt.none(BearerToken),
|
||||||
|
cert: Opt.none(AutotlsCert),
|
||||||
|
certReady: newAsyncEvent(),
|
||||||
|
running: newAsyncEvent(),
|
||||||
|
config: config,
|
||||||
|
managerFut: nil,
|
||||||
|
peerInfo: nil,
|
||||||
|
rng: rng,
|
||||||
|
)
|
||||||
|
|
||||||
|
method setup*(
|
||||||
|
self: AutotlsService, switch: Switch
|
||||||
|
): Future[bool] {.async: (raises: [CancelledError]).} =
|
||||||
|
trace "Setting up AutotlsService"
|
||||||
|
let hasBeenSetup = await procCall Service(self).setup(switch)
|
||||||
|
if hasBeenSetup:
|
||||||
|
if self.config.ipAddress.isNone():
|
||||||
|
try:
|
||||||
|
self.config.ipAddress = Opt.some(getPublicIPAddress())
|
||||||
|
except ValueError as exc:
|
||||||
|
error "Failed to get public IP address", err = exc.msg
|
||||||
|
return false
|
||||||
|
except OSError as exc:
|
||||||
|
error "Failed to get public IP address", err = exc.msg
|
||||||
|
return false
|
||||||
|
self.managerFut = self.run(switch)
|
||||||
|
return hasBeenSetup
|
||||||
|
|
||||||
|
method issueCertificate(
|
||||||
|
self: AutotlsService
|
||||||
|
): Future[bool] {.
|
||||||
|
base, async: (raises: [AutoTLSError, ACMEError, PeerIDAuthError, CancelledError])
|
||||||
|
.} =
|
||||||
|
trace "Issuing certificate"
|
||||||
|
|
||||||
|
if self.peerInfo.isNil():
|
||||||
|
error "Cannot issue new certificate: peerInfo not set"
|
||||||
|
return false
|
||||||
|
|
||||||
|
# generate autotls domain string: "*.{peerID}.libp2p.direct"
|
||||||
|
let baseDomain =
|
||||||
|
api.Domain(encodePeerId(self.peerInfo.peerId) & "." & AutoTLSDNSServer)
|
||||||
|
let domain = api.Domain("*." & baseDomain)
|
||||||
|
|
||||||
|
let acmeClient = self.acmeClient
|
||||||
|
|
||||||
|
trace "Requesting ACME challenge"
|
||||||
|
let dns01Challenge = await acmeClient.getChallenge(@[domain])
|
||||||
|
trace "Generating key authorization"
|
||||||
|
let keyAuth = acmeClient.genKeyAuthorization(dns01Challenge.dns01.token)
|
||||||
|
|
||||||
|
let addrs = await self.peerInfo.expandAddrs()
|
||||||
|
if addrs.len == 0:
|
||||||
|
error "Unable to authenticate with broker: no addresses"
|
||||||
|
return false
|
||||||
|
|
||||||
|
let strMultiaddresses: seq[string] = addrs.mapIt($it)
|
||||||
|
let payload = %*{"value": keyAuth, "addresses": strMultiaddresses}
|
||||||
|
let registrationURL = parseUri("https://" & AutoTLSBroker & "/v1/_acme-challenge")
|
||||||
|
|
||||||
|
trace "Sending challenge to AutoTLS broker"
|
||||||
|
let (bearer, response) =
|
||||||
|
await self.brokerClient.send(registrationURL, self.peerInfo, payload, self.bearer)
|
||||||
|
if self.bearer.isNone():
|
||||||
|
# save bearer token for future
|
||||||
|
self.bearer = Opt.some(bearer)
|
||||||
|
if response.status != HttpOk:
|
||||||
|
error "Failed to authenticate with AutoTLS Broker at " & AutoTLSBroker
|
||||||
|
debug "Broker message",
|
||||||
|
body = bytesToString(response.body), peerinfo = self.peerInfo
|
||||||
|
return false
|
||||||
|
|
||||||
|
let dashedIpAddr = ($self.config.ipAddress.get()).replace(".", "-")
|
||||||
|
let acmeChalDomain = api.Domain("_acme-challenge." & baseDomain)
|
||||||
|
let ip4Domain = api.Domain(dashedIpAddr & "." & baseDomain)
|
||||||
|
debug "Waiting for DNS record to be set", ip = ip4Domain, acme = acmeChalDomain
|
||||||
|
let dnsSet = await checkDNSRecords(
|
||||||
|
self.config.nameResolver, self.config.ipAddress.get(), baseDomain, keyAuth
|
||||||
|
)
|
||||||
|
if not dnsSet:
|
||||||
|
error "DNS records not set"
|
||||||
|
return false
|
||||||
|
|
||||||
|
trace "Notifying challenge completion to ACME and downloading cert"
|
||||||
|
let certKeyPair = KeyPair.random(PKScheme.RSA, self.rng[]).get()
|
||||||
|
|
||||||
|
let certificate =
|
||||||
|
await acmeClient.getCertificate(domain, certKeyPair, dns01Challenge)
|
||||||
|
|
||||||
|
let derPrivKey = certKeyPair.seckey.rsakey.getBytes.valueOr:
|
||||||
|
raise newException(AutoTLSError, "Unable to get TLS private key")
|
||||||
|
let pemPrivKey: string = derPrivKey.pemEncode("PRIVATE KEY")
|
||||||
|
debug "autotls cert", pemPrivKey = pemPrivKey, cert = certificate.rawCertificate
|
||||||
|
|
||||||
|
trace "Installing certificate"
|
||||||
|
let newCert =
|
||||||
|
try:
|
||||||
|
AutotlsCert.new(
|
||||||
|
TLSCertificate.init(certificate.rawCertificate),
|
||||||
|
TLSPrivateKey.init(pemPrivKey),
|
||||||
|
asMoment(certificate.certificateExpiry),
|
||||||
|
)
|
||||||
|
except TLSStreamProtocolError:
|
||||||
|
error "Could not parse downloaded certificates"
|
||||||
|
return false
|
||||||
|
self.cert = Opt.some(newCert)
|
||||||
|
self.certReady.fire()
|
||||||
|
trace "Certificate installed"
|
||||||
|
true
|
||||||
|
|
||||||
|
proc hasTcpStarted(switch: Switch): bool =
|
||||||
|
switch.transports.filterIt(it of TcpTransport and it.running).len == 0
|
||||||
|
|
||||||
|
proc tryIssueCertificate(self: AutotlsService) {.async: (raises: [CancelledError]).} =
|
||||||
|
for _ in 0 ..< self.config.issueRetries:
|
||||||
|
try:
|
||||||
|
if await self.issueCertificate():
|
||||||
|
return
|
||||||
|
except CancelledError as exc:
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "Failed to issue certificate", err = exc.msg
|
||||||
|
await sleepAsync(self.config.issueRetryTime)
|
||||||
|
error "Failed to issue certificate"
|
||||||
|
|
||||||
|
method run*(
|
||||||
|
self: AutotlsService, switch: Switch
|
||||||
|
) {.async: (raises: [CancelledError]).} =
|
||||||
|
trace "Starting Autotls management"
|
||||||
|
self.running.fire()
|
||||||
|
self.peerInfo = switch.peerInfo
|
||||||
|
|
||||||
|
# ensure that there's at least one TcpTransport running
|
||||||
|
# for communicating with autotls broker
|
||||||
|
if switch.hasTcpStarted():
|
||||||
|
error "Could not find a running TcpTransport in switch"
|
||||||
|
return
|
||||||
|
|
||||||
|
heartbeat "Certificate Management", self.config.renewCheckTime:
|
||||||
|
if self.cert.isNone():
|
||||||
|
await self.tryIssueCertificate()
|
||||||
|
|
||||||
|
# AutotlsService will renew the cert 1h before it expires
|
||||||
|
let cert = self.cert.get
|
||||||
|
let waitTime = cert.expiry - Moment.now - self.config.renewBufferTime
|
||||||
|
if waitTime <= self.config.renewBufferTime:
|
||||||
|
await self.tryIssueCertificate()
|
||||||
|
|
||||||
|
method stop*(
|
||||||
|
self: AutotlsService, switch: Switch
|
||||||
|
): Future[bool] {.async: (raises: [CancelledError]).} =
|
||||||
|
let hasBeenStopped = await procCall Service(self).stop(switch)
|
||||||
|
if hasBeenStopped:
|
||||||
|
if not self.acmeClient.isNil():
|
||||||
|
await self.acmeClient.close()
|
||||||
|
if not self.brokerClient.isNil():
|
||||||
|
await self.brokerClient.close()
|
||||||
|
if not self.managerFut.isNil():
|
||||||
|
await self.managerFut.cancelAndWait()
|
||||||
|
self.managerFut = nil
|
||||||
|
return hasBeenStopped
|
||||||
82
libp2p/autotls/utils.nim
Normal file
82
libp2p/autotls/utils.nim
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# Nim-Libp2p
|
||||||
|
# Copyright (c) 2025 Status Research & Development GmbH
|
||||||
|
# Licensed under either of
|
||||||
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
|
# at your option.
|
||||||
|
# This file may not be copied, modified, or distributed except according to
|
||||||
|
# those terms.
|
||||||
|
{.push raises: [].}
|
||||||
|
{.push public.}
|
||||||
|
|
||||||
|
import chronos, chronicles
|
||||||
|
import ../errors
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "libp2p utils"
|
||||||
|
|
||||||
|
const
|
||||||
|
DefaultDnsRetries = 3
|
||||||
|
DefaultDnsRetryTime = 1.seconds
|
||||||
|
|
||||||
|
type AutoTLSError* = object of LPError
|
||||||
|
|
||||||
|
when defined(libp2p_autotls_support):
|
||||||
|
import strutils
|
||||||
|
from times import DateTime, toTime, toUnix
|
||||||
|
import stew/base36
|
||||||
|
import
|
||||||
|
../peerid,
|
||||||
|
../multihash,
|
||||||
|
../cid,
|
||||||
|
../multicodec,
|
||||||
|
../nameresolving/nameresolver,
|
||||||
|
./acme/client
|
||||||
|
|
||||||
|
proc asMoment*(dt: DateTime): Moment =
|
||||||
|
let unixTime: int64 = dt.toTime.toUnix
|
||||||
|
return Moment.init(unixTime, Second)
|
||||||
|
|
||||||
|
proc encodePeerId*(peerId: PeerId): string {.raises: [AutoTLSError].} =
|
||||||
|
var mh: MultiHash
|
||||||
|
let decodeResult = MultiHash.decode(peerId.data, mh)
|
||||||
|
if decodeResult.isErr() or decodeResult.get() == -1:
|
||||||
|
raise
|
||||||
|
newException(AutoTLSError, "Failed to decode PeerId: invalid multihash format")
|
||||||
|
|
||||||
|
let cidResult = Cid.init(CIDv1, multiCodec("libp2p-key"), mh)
|
||||||
|
if cidResult.isErr():
|
||||||
|
raise newException(AutoTLSError, "Failed to initialize CID from multihash")
|
||||||
|
|
||||||
|
return Base36.encode(cidResult.get().data.buffer)
|
||||||
|
|
||||||
|
proc checkDNSRecords*(
|
||||||
|
nameResolver: NameResolver,
|
||||||
|
ipAddress: IpAddress,
|
||||||
|
baseDomain: api.Domain,
|
||||||
|
keyAuth: KeyAuthorization,
|
||||||
|
retries: int = DefaultDnsRetries,
|
||||||
|
): Future[bool] {.async: (raises: [AutoTLSError, CancelledError]).} =
|
||||||
|
# if my ip address is 100.10.10.3 then the ip4Domain will be:
|
||||||
|
# 100-10-10-3.{peerIdBase36}.libp2p.direct
|
||||||
|
# and acme challenge TXT domain will be:
|
||||||
|
# _acme-challenge.{peerIdBase36}.libp2p.direct
|
||||||
|
let dashedIpAddr = ($ipAddress).replace(".", "-")
|
||||||
|
let acmeChalDomain = api.Domain("_acme-challenge." & baseDomain)
|
||||||
|
let ip4Domain = api.Domain(dashedIpAddr & "." & baseDomain)
|
||||||
|
|
||||||
|
var txt: seq[string]
|
||||||
|
var ip4: seq[TransportAddress]
|
||||||
|
for _ in 0 .. retries:
|
||||||
|
txt = await nameResolver.resolveTxt(acmeChalDomain)
|
||||||
|
try:
|
||||||
|
ip4 = await nameResolver.resolveIp(ip4Domain, 0.Port)
|
||||||
|
except CancelledError as exc:
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
error "Failed to resolve IP", description = exc.msg # retry
|
||||||
|
if txt.len > 0 and txt[0] == keyAuth and ip4.len > 0:
|
||||||
|
return true
|
||||||
|
await sleepAsync(DefaultDnsRetryTime)
|
||||||
|
|
||||||
|
return false
|
||||||
@@ -23,22 +23,44 @@ import
|
|||||||
stream/connection,
|
stream/connection,
|
||||||
multiaddress,
|
multiaddress,
|
||||||
crypto/crypto,
|
crypto/crypto,
|
||||||
transports/[transport, tcptransport],
|
transports/[transport, tcptransport, wstransport, memorytransport],
|
||||||
muxers/[muxer, mplex/mplex, yamux/yamux],
|
muxers/[muxer, mplex/mplex, yamux/yamux],
|
||||||
protocols/[identify, secure/secure, secure/noise, rendezvous],
|
protocols/[identify, secure/secure, secure/noise, rendezvous],
|
||||||
protocols/connectivity/[autonat/server, relay/relay, relay/client, relay/rtransport],
|
protocols/connectivity/[
|
||||||
|
autonat/server,
|
||||||
|
autonatv2/server,
|
||||||
|
autonatv2/service,
|
||||||
|
autonatv2/client,
|
||||||
|
relay/relay,
|
||||||
|
relay/client,
|
||||||
|
relay/rtransport,
|
||||||
|
],
|
||||||
connmanager,
|
connmanager,
|
||||||
upgrademngrs/muxedupgrade,
|
upgrademngrs/muxedupgrade,
|
||||||
observedaddrmanager,
|
observedaddrmanager,
|
||||||
|
autotls/service,
|
||||||
nameresolving/nameresolver,
|
nameresolving/nameresolver,
|
||||||
errors,
|
errors,
|
||||||
utility
|
utility
|
||||||
import services/wildcardresolverservice
|
import services/wildcardresolverservice
|
||||||
|
|
||||||
export switch, peerid, peerinfo, connection, multiaddress, crypto, errors
|
export
|
||||||
|
switch, peerid, peerinfo, connection, multiaddress, crypto, errors, TLSPrivateKey,
|
||||||
|
TLSCertificate, TLSFlags, ServerFlags
|
||||||
|
|
||||||
|
const MemoryAutoAddress* = memorytransport.MemoryAutoAddress
|
||||||
|
|
||||||
type
|
type
|
||||||
TransportProvider* {.public.} = proc(upgr: Upgrade): Transport {.gcsafe, raises: [].}
|
TransportProvider* {.deprecated: "Use TransportBuilder instead".} =
|
||||||
|
proc(upgr: Upgrade, privateKey: PrivateKey): Transport {.gcsafe, raises: [].}
|
||||||
|
|
||||||
|
TransportBuilder* {.public.} =
|
||||||
|
proc(config: TransportConfig): Transport {.gcsafe, raises: [].}
|
||||||
|
|
||||||
|
TransportConfig* = ref object
|
||||||
|
upgr*: Upgrade
|
||||||
|
privateKey*: PrivateKey
|
||||||
|
autotls*: AutotlsService
|
||||||
|
|
||||||
SecureProtocol* {.pure.} = enum
|
SecureProtocol* {.pure.} = enum
|
||||||
Noise
|
Noise
|
||||||
@@ -48,7 +70,7 @@ type
|
|||||||
addresses: seq[MultiAddress]
|
addresses: seq[MultiAddress]
|
||||||
secureManagers: seq[SecureProtocol]
|
secureManagers: seq[SecureProtocol]
|
||||||
muxers: seq[MuxerProvider]
|
muxers: seq[MuxerProvider]
|
||||||
transports: seq[TransportProvider]
|
transports: seq[TransportBuilder]
|
||||||
rng: ref HmacDrbgContext
|
rng: ref HmacDrbgContext
|
||||||
maxConnections: int
|
maxConnections: int
|
||||||
maxIn: int
|
maxIn: int
|
||||||
@@ -60,6 +82,10 @@ type
|
|||||||
nameResolver: NameResolver
|
nameResolver: NameResolver
|
||||||
peerStoreCapacity: Opt[int]
|
peerStoreCapacity: Opt[int]
|
||||||
autonat: bool
|
autonat: bool
|
||||||
|
autonatV2ServerConfig: Opt[AutonatV2Config]
|
||||||
|
autonatV2Client: AutonatV2Client
|
||||||
|
autonatV2ServiceConfig: AutonatV2ServiceConfig
|
||||||
|
autotls: AutotlsService
|
||||||
circuitRelay: Relay
|
circuitRelay: Relay
|
||||||
rdv: RendezVous
|
rdv: RendezVous
|
||||||
services: seq[Service]
|
services: seq[Service]
|
||||||
@@ -144,26 +170,71 @@ proc withNoise*(b: SwitchBuilder): SwitchBuilder {.public.} =
|
|||||||
b
|
b
|
||||||
|
|
||||||
proc withTransport*(
|
proc withTransport*(
|
||||||
b: SwitchBuilder, prov: TransportProvider
|
b: SwitchBuilder, prov: TransportBuilder
|
||||||
): SwitchBuilder {.public.} =
|
): SwitchBuilder {.public.} =
|
||||||
## Use a custom transport
|
## Use a custom transport
|
||||||
runnableExamples:
|
runnableExamples:
|
||||||
let switch = SwitchBuilder
|
let switch = SwitchBuilder
|
||||||
.new()
|
.new()
|
||||||
.withTransport(
|
.withTransport(
|
||||||
proc(upgr: Upgrade): Transport =
|
proc(config: TransportConfig): Transport =
|
||||||
TcpTransport.new(flags, upgr)
|
TcpTransport.new(flags, config.upgr)
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
b.transports.add(prov)
|
b.transports.add(prov)
|
||||||
b
|
b
|
||||||
|
|
||||||
|
proc withTransport*(
|
||||||
|
b: SwitchBuilder, prov: TransportProvider
|
||||||
|
): SwitchBuilder {.deprecated: "Use TransportBuilder instead".} =
|
||||||
|
## Use a custom transport
|
||||||
|
runnableExamples:
|
||||||
|
let switch = SwitchBuilder
|
||||||
|
.new()
|
||||||
|
.withTransport(
|
||||||
|
proc(upgr: Upgrade, privateKey: PrivateKey): Transport =
|
||||||
|
TcpTransport.new(flags, upgr)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
let tBuilder: TransportBuilder = proc(config: TransportConfig): Transport =
|
||||||
|
prov(config.upgr, config.privateKey)
|
||||||
|
b.withTransport(tBuilder)
|
||||||
|
|
||||||
proc withTcpTransport*(
|
proc withTcpTransport*(
|
||||||
b: SwitchBuilder, flags: set[ServerFlags] = {}
|
b: SwitchBuilder, flags: set[ServerFlags] = {}
|
||||||
): SwitchBuilder {.public.} =
|
): SwitchBuilder {.public.} =
|
||||||
b.withTransport(
|
b.withTransport(
|
||||||
proc(upgr: Upgrade): Transport =
|
proc(config: TransportConfig): Transport =
|
||||||
TcpTransport.new(flags, upgr)
|
TcpTransport.new(flags, config.upgr)
|
||||||
|
)
|
||||||
|
|
||||||
|
proc withWsTransport*(
|
||||||
|
b: SwitchBuilder,
|
||||||
|
tlsPrivateKey: TLSPrivateKey = nil,
|
||||||
|
tlsCertificate: TLSCertificate = nil,
|
||||||
|
tlsFlags: set[TLSFlags] = {},
|
||||||
|
flags: set[ServerFlags] = {},
|
||||||
|
): SwitchBuilder =
|
||||||
|
b.withTransport(
|
||||||
|
proc(config: TransportConfig): Transport =
|
||||||
|
WsTransport.new(
|
||||||
|
config.upgr, tlsPrivateKey, tlsCertificate, config.autotls, tlsFlags, flags
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
when defined(libp2p_quic_support):
|
||||||
|
import transports/quictransport
|
||||||
|
|
||||||
|
proc withQuicTransport*(b: SwitchBuilder): SwitchBuilder {.public.} =
|
||||||
|
b.withTransport(
|
||||||
|
proc(config: TransportConfig): Transport =
|
||||||
|
QuicTransport.new(config.upgr, config.privateKey)
|
||||||
|
)
|
||||||
|
|
||||||
|
proc withMemoryTransport*(b: SwitchBuilder): SwitchBuilder {.public.} =
|
||||||
|
b.withTransport(
|
||||||
|
proc(config: TransportConfig): Transport =
|
||||||
|
MemoryTransport.new(config.upgr)
|
||||||
)
|
)
|
||||||
|
|
||||||
proc withRng*(b: SwitchBuilder, rng: ref HmacDrbgContext): SwitchBuilder {.public.} =
|
proc withRng*(b: SwitchBuilder, rng: ref HmacDrbgContext): SwitchBuilder {.public.} =
|
||||||
@@ -220,6 +291,26 @@ proc withAutonat*(b: SwitchBuilder): SwitchBuilder =
|
|||||||
b.autonat = true
|
b.autonat = true
|
||||||
b
|
b
|
||||||
|
|
||||||
|
proc withAutonatV2Server*(
|
||||||
|
b: SwitchBuilder, config: AutonatV2Config = AutonatV2Config.new()
|
||||||
|
): SwitchBuilder =
|
||||||
|
b.autonatV2ServerConfig = Opt.some(config)
|
||||||
|
b
|
||||||
|
|
||||||
|
proc withAutonatV2*(
|
||||||
|
b: SwitchBuilder, serviceConfig = AutonatV2ServiceConfig.new()
|
||||||
|
): SwitchBuilder =
|
||||||
|
b.autonatV2Client = AutonatV2Client.new(b.rng)
|
||||||
|
b.autonatV2ServiceConfig = serviceConfig
|
||||||
|
b
|
||||||
|
|
||||||
|
when defined(libp2p_autotls_support):
|
||||||
|
proc withAutotls*(
|
||||||
|
b: SwitchBuilder, config: AutotlsConfig = AutotlsConfig.new()
|
||||||
|
): SwitchBuilder {.public.} =
|
||||||
|
b.autotls = AutotlsService.new(config = config)
|
||||||
|
b
|
||||||
|
|
||||||
proc withCircuitRelay*(b: SwitchBuilder, r: Relay = Relay.new()): SwitchBuilder =
|
proc withCircuitRelay*(b: SwitchBuilder, r: Relay = Relay.new()): SwitchBuilder =
|
||||||
b.circuitRelay = r
|
b.circuitRelay = r
|
||||||
b
|
b
|
||||||
@@ -247,6 +338,10 @@ proc build*(b: SwitchBuilder): Switch {.raises: [LPError], public.} =
|
|||||||
let pkRes = PrivateKey.random(b.rng[])
|
let pkRes = PrivateKey.random(b.rng[])
|
||||||
let seckey = b.privKey.get(otherwise = pkRes.expect("Expected default Private Key"))
|
let seckey = b.privKey.get(otherwise = pkRes.expect("Expected default Private Key"))
|
||||||
|
|
||||||
|
if b.secureManagers.len == 0:
|
||||||
|
debug "no secure managers defined. Adding noise by default"
|
||||||
|
b.secureManagers.add(SecureProtocol.Noise)
|
||||||
|
|
||||||
var secureManagerInstances: seq[Secure]
|
var secureManagerInstances: seq[Secure]
|
||||||
if SecureProtocol.Noise in b.secureManagers:
|
if SecureProtocol.Noise in b.secureManagers:
|
||||||
secureManagerInstances.add(Noise.new(b.rng, seckey).Secure)
|
secureManagerInstances.add(Noise.new(b.rng, seckey).Secure)
|
||||||
@@ -267,10 +362,17 @@ proc build*(b: SwitchBuilder): Switch {.raises: [LPError], public.} =
|
|||||||
ms = MultistreamSelect.new()
|
ms = MultistreamSelect.new()
|
||||||
muxedUpgrade = MuxedUpgrade.new(b.muxers, secureManagerInstances, ms)
|
muxedUpgrade = MuxedUpgrade.new(b.muxers, secureManagerInstances, ms)
|
||||||
|
|
||||||
|
if not b.autotls.isNil():
|
||||||
|
b.services.insert(b.autotls, 0)
|
||||||
|
|
||||||
let transports = block:
|
let transports = block:
|
||||||
var transports: seq[Transport]
|
var transports: seq[Transport]
|
||||||
for tProvider in b.transports:
|
for tProvider in b.transports:
|
||||||
transports.add(tProvider(muxedUpgrade))
|
transports.add(
|
||||||
|
tProvider(
|
||||||
|
TransportConfig(upgr: muxedUpgrade, privateKey: seckey, autotls: b.autotls)
|
||||||
|
)
|
||||||
|
)
|
||||||
transports
|
transports
|
||||||
|
|
||||||
if b.secureManagers.len == 0:
|
if b.secureManagers.len == 0:
|
||||||
@@ -288,6 +390,13 @@ proc build*(b: SwitchBuilder): Switch {.raises: [LPError], public.} =
|
|||||||
if b.enableWildcardResolver:
|
if b.enableWildcardResolver:
|
||||||
b.services.insert(WildcardAddressResolverService.new(), 0)
|
b.services.insert(WildcardAddressResolverService.new(), 0)
|
||||||
|
|
||||||
|
if not isNil(b.autonatV2Client):
|
||||||
|
b.services.add(
|
||||||
|
AutonatV2Service.new(
|
||||||
|
b.rng, client = b.autonatV2Client, config = b.autonatV2ServiceConfig
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
let switch = newSwitch(
|
let switch = newSwitch(
|
||||||
peerInfo = peerInfo,
|
peerInfo = peerInfo,
|
||||||
transports = transports,
|
transports = transports,
|
||||||
@@ -301,9 +410,15 @@ proc build*(b: SwitchBuilder): Switch {.raises: [LPError], public.} =
|
|||||||
|
|
||||||
switch.mount(identify)
|
switch.mount(identify)
|
||||||
|
|
||||||
|
if not isNil(b.autonatV2Client):
|
||||||
|
b.autonatV2Client.setup(switch)
|
||||||
|
switch.mount(b.autonatV2Client)
|
||||||
|
|
||||||
|
b.autonatV2ServerConfig.withValue(config):
|
||||||
|
switch.mount(AutonatV2.new(switch, config = config))
|
||||||
|
|
||||||
if b.autonat:
|
if b.autonat:
|
||||||
let autonat = Autonat.new(switch)
|
switch.mount(Autonat.new(switch))
|
||||||
switch.mount(autonat)
|
|
||||||
|
|
||||||
if not isNil(b.circuitRelay):
|
if not isNil(b.circuitRelay):
|
||||||
if b.circuitRelay of RelayClient:
|
if b.circuitRelay of RelayClient:
|
||||||
@@ -317,13 +432,78 @@ proc build*(b: SwitchBuilder): Switch {.raises: [LPError], public.} =
|
|||||||
|
|
||||||
return switch
|
return switch
|
||||||
|
|
||||||
proc newStandardSwitch*(
|
type TransportType* {.pure.} = enum
|
||||||
|
QUIC
|
||||||
|
TCP
|
||||||
|
Memory
|
||||||
|
|
||||||
|
proc newStandardSwitchBuilder*(
|
||||||
privKey = none(PrivateKey),
|
privKey = none(PrivateKey),
|
||||||
addrs: MultiAddress | seq[MultiAddress] =
|
addrs: MultiAddress | seq[MultiAddress] = newSeq[MultiAddress](),
|
||||||
MultiAddress.init("/ip4/127.0.0.1/tcp/0").expect("valid address"),
|
transport: TransportType = TransportType.TCP,
|
||||||
secureManagers: openArray[SecureProtocol] = [SecureProtocol.Noise],
|
|
||||||
transportFlags: set[ServerFlags] = {},
|
transportFlags: set[ServerFlags] = {},
|
||||||
rng = newRng(),
|
rng = newRng(),
|
||||||
|
secureManagers: openArray[SecureProtocol] = [SecureProtocol.Noise],
|
||||||
|
inTimeout: Duration = 5.minutes,
|
||||||
|
outTimeout: Duration = 5.minutes,
|
||||||
|
maxConnections = MaxConnections,
|
||||||
|
maxIn = -1,
|
||||||
|
maxOut = -1,
|
||||||
|
maxConnsPerPeer = MaxConnectionsPerPeer,
|
||||||
|
nameResolver: NameResolver = nil,
|
||||||
|
sendSignedPeerRecord = false,
|
||||||
|
peerStoreCapacity = 1000,
|
||||||
|
): SwitchBuilder {.raises: [LPError], public.} =
|
||||||
|
## Helper for common switch configurations.
|
||||||
|
var b = SwitchBuilder
|
||||||
|
.new()
|
||||||
|
.withRng(rng)
|
||||||
|
.withSignedPeerRecord(sendSignedPeerRecord)
|
||||||
|
.withMaxConnections(maxConnections)
|
||||||
|
.withMaxIn(maxIn)
|
||||||
|
.withMaxOut(maxOut)
|
||||||
|
.withMaxConnsPerPeer(maxConnsPerPeer)
|
||||||
|
.withPeerStore(capacity = peerStoreCapacity)
|
||||||
|
.withNameResolver(nameResolver)
|
||||||
|
.withNoise()
|
||||||
|
|
||||||
|
var addrs =
|
||||||
|
when addrs is MultiAddress:
|
||||||
|
@[addrs]
|
||||||
|
else:
|
||||||
|
addrs
|
||||||
|
|
||||||
|
case transport
|
||||||
|
of TransportType.QUIC:
|
||||||
|
when defined(libp2p_quic_support):
|
||||||
|
if addrs.len == 0:
|
||||||
|
addrs = @[MultiAddress.init("/ip4/0.0.0.0/udp/0/quic-v1").tryGet()]
|
||||||
|
b = b.withQuicTransport().withAddresses(addrs)
|
||||||
|
else:
|
||||||
|
raiseAssert "QUIC not supported in this build"
|
||||||
|
of TransportType.TCP:
|
||||||
|
if addrs.len == 0:
|
||||||
|
addrs = @[MultiAddress.init("/ip4/127.0.0.1/tcp/0").tryGet()]
|
||||||
|
b = b.withTcpTransport(transportFlags).withAddresses(addrs).withMplex(
|
||||||
|
inTimeout, outTimeout
|
||||||
|
)
|
||||||
|
of TransportType.Memory:
|
||||||
|
if addrs.len == 0:
|
||||||
|
addrs = @[MultiAddress.init(MemoryAutoAddress).tryGet()]
|
||||||
|
b = b.withMemoryTransport().withAddresses(addrs).withMplex(inTimeout, outTimeout)
|
||||||
|
|
||||||
|
privKey.withValue(pkey):
|
||||||
|
b = b.withPrivateKey(pkey)
|
||||||
|
|
||||||
|
b
|
||||||
|
|
||||||
|
proc newStandardSwitch*(
|
||||||
|
privKey = none(PrivateKey),
|
||||||
|
addrs: MultiAddress | seq[MultiAddress] = newSeq[MultiAddress](),
|
||||||
|
transport: TransportType = TransportType.TCP,
|
||||||
|
transportFlags: set[ServerFlags] = {},
|
||||||
|
rng = newRng(),
|
||||||
|
secureManagers: openArray[SecureProtocol] = [SecureProtocol.Noise],
|
||||||
inTimeout: Duration = 5.minutes,
|
inTimeout: Duration = 5.minutes,
|
||||||
outTimeout: Duration = 5.minutes,
|
outTimeout: Duration = 5.minutes,
|
||||||
maxConnections = MaxConnections,
|
maxConnections = MaxConnections,
|
||||||
@@ -334,28 +514,21 @@ proc newStandardSwitch*(
|
|||||||
sendSignedPeerRecord = false,
|
sendSignedPeerRecord = false,
|
||||||
peerStoreCapacity = 1000,
|
peerStoreCapacity = 1000,
|
||||||
): Switch {.raises: [LPError], public.} =
|
): Switch {.raises: [LPError], public.} =
|
||||||
## Helper for common switch configurations.
|
newStandardSwitchBuilder(
|
||||||
let addrs =
|
privKey = privKey,
|
||||||
when addrs is MultiAddress:
|
addrs = addrs,
|
||||||
@[addrs]
|
transport = transport,
|
||||||
else:
|
transportFlags = transportFlags,
|
||||||
addrs
|
rng = rng,
|
||||||
var b = SwitchBuilder
|
secureManagers = secureManagers,
|
||||||
.new()
|
inTimeout = inTimeout,
|
||||||
.withAddresses(addrs)
|
outTimeout = outTimeout,
|
||||||
.withRng(rng)
|
maxConnections = maxConnections,
|
||||||
.withSignedPeerRecord(sendSignedPeerRecord)
|
maxIn = maxIn,
|
||||||
.withMaxConnections(maxConnections)
|
maxOut = maxOut,
|
||||||
.withMaxIn(maxIn)
|
maxConnsPerPeer = maxConnsPerPeer,
|
||||||
.withMaxOut(maxOut)
|
nameResolver = nameResolver,
|
||||||
.withMaxConnsPerPeer(maxConnsPerPeer)
|
sendSignedPeerRecord = sendSignedPeerRecord,
|
||||||
.withPeerStore(capacity = peerStoreCapacity)
|
peerStoreCapacity = peerStoreCapacity,
|
||||||
.withMplex(inTimeout, outTimeout)
|
)
|
||||||
.withTcpTransport(transportFlags)
|
.build()
|
||||||
.withNameResolver(nameResolver)
|
|
||||||
.withNoise()
|
|
||||||
|
|
||||||
privKey.withValue(pkey):
|
|
||||||
b = b.withPrivateKey(pkey)
|
|
||||||
|
|
||||||
b.build()
|
|
||||||
|
|||||||
@@ -10,10 +10,12 @@
|
|||||||
## This module implementes CID (Content IDentifier).
|
## This module implementes CID (Content IDentifier).
|
||||||
|
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
{.used.}
|
||||||
|
|
||||||
import tables, hashes
|
import tables, hashes
|
||||||
import multibase, multicodec, multihash, vbuffer, varint
|
import multibase, multicodec, multihash, vbuffer, varint, results
|
||||||
import stew/[base58, results]
|
import stew/base58
|
||||||
|
import ./utils/sequninit
|
||||||
|
|
||||||
export results
|
export results
|
||||||
|
|
||||||
@@ -41,6 +43,7 @@ const ContentIdsList = [
|
|||||||
multiCodec("dag-pb"),
|
multiCodec("dag-pb"),
|
||||||
multiCodec("dag-cbor"),
|
multiCodec("dag-cbor"),
|
||||||
multiCodec("dag-json"),
|
multiCodec("dag-json"),
|
||||||
|
multiCodec("libp2p-key"),
|
||||||
multiCodec("git-raw"),
|
multiCodec("git-raw"),
|
||||||
multiCodec("eth-block"),
|
multiCodec("eth-block"),
|
||||||
multiCodec("eth-block-list"),
|
multiCodec("eth-block-list"),
|
||||||
@@ -121,7 +124,7 @@ proc decode(data: openArray[char]): Result[Cid, CidError] =
|
|||||||
return err(CidError.Incorrect)
|
return err(CidError.Incorrect)
|
||||||
if len(data) == 46:
|
if len(data) == 46:
|
||||||
if data[0] == 'Q' and data[1] == 'm':
|
if data[0] == 'Q' and data[1] == 'm':
|
||||||
buffer = newSeq[byte](BTCBase58.decodedLength(len(data)))
|
buffer = newSeqUninit[byte](BTCBase58.decodedLength(len(data)))
|
||||||
if BTCBase58.decode(data, buffer, plen) != Base58Status.Success:
|
if BTCBase58.decode(data, buffer, plen) != Base58Status.Success:
|
||||||
return err(CidError.Incorrect)
|
return err(CidError.Incorrect)
|
||||||
buffer.setLen(plen)
|
buffer.setLen(plen)
|
||||||
@@ -129,7 +132,7 @@ proc decode(data: openArray[char]): Result[Cid, CidError] =
|
|||||||
let length = MultiBase.decodedLength(data[0], len(data))
|
let length = MultiBase.decodedLength(data[0], len(data))
|
||||||
if length == -1:
|
if length == -1:
|
||||||
return err(CidError.Incorrect)
|
return err(CidError.Incorrect)
|
||||||
buffer = newSeq[byte](length)
|
buffer = newSeqUninit[byte](length)
|
||||||
if MultiBase.decode(data, buffer, plen) != MultiBaseStatus.Success:
|
if MultiBase.decode(data, buffer, plen) != MultiBaseStatus.Success:
|
||||||
return err(CidError.Incorrect)
|
return err(CidError.Incorrect)
|
||||||
buffer.setLen(plen)
|
buffer.setLen(plen)
|
||||||
|
|||||||
@@ -42,8 +42,9 @@ type
|
|||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
ConnEventHandler* =
|
ConnEventHandler* = proc(peerId: PeerId, event: ConnEvent): Future[void] {.
|
||||||
proc(peerId: PeerId, event: ConnEvent): Future[void] {.gcsafe, raises: [].}
|
gcsafe, async: (raises: [CancelledError])
|
||||||
|
.}
|
||||||
|
|
||||||
PeerEventKind* {.pure.} = enum
|
PeerEventKind* {.pure.} = enum
|
||||||
Left
|
Left
|
||||||
@@ -57,8 +58,9 @@ type
|
|||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
|
|
||||||
PeerEventHandler* =
|
PeerEventHandler* = proc(peerId: PeerId, event: PeerEvent): Future[void] {.
|
||||||
proc(peerId: PeerId, event: PeerEvent): Future[void] {.gcsafe, raises: [].}
|
gcsafe, async: (raises: [CancelledError])
|
||||||
|
.}
|
||||||
|
|
||||||
ConnManager* = ref object of RootObj
|
ConnManager* = ref object of RootObj
|
||||||
maxConnsPerPeer: int
|
maxConnsPerPeer: int
|
||||||
@@ -123,7 +125,9 @@ proc removeConnEventHandler*(
|
|||||||
) =
|
) =
|
||||||
c.connEvents[kind].excl(handler)
|
c.connEvents[kind].excl(handler)
|
||||||
|
|
||||||
proc triggerConnEvent*(c: ConnManager, peerId: PeerId, event: ConnEvent) {.async.} =
|
proc triggerConnEvent*(
|
||||||
|
c: ConnManager, peerId: PeerId, event: ConnEvent
|
||||||
|
) {.async: (raises: [CancelledError]).} =
|
||||||
try:
|
try:
|
||||||
trace "About to trigger connection events", peer = peerId
|
trace "About to trigger connection events", peer = peerId
|
||||||
if c.connEvents[event.kind].len() > 0:
|
if c.connEvents[event.kind].len() > 0:
|
||||||
@@ -136,7 +140,7 @@ proc triggerConnEvent*(c: ConnManager, peerId: PeerId, event: ConnEvent) {.async
|
|||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
raise exc
|
raise exc
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
warn "Exception in triggerConnEvents",
|
warn "Exception in triggerConnEvent",
|
||||||
description = exc.msg, peer = peerId, event = $event
|
description = exc.msg, peer = peerId, event = $event
|
||||||
|
|
||||||
proc addPeerEventHandler*(
|
proc addPeerEventHandler*(
|
||||||
@@ -154,7 +158,9 @@ proc removePeerEventHandler*(
|
|||||||
) =
|
) =
|
||||||
c.peerEvents[kind].excl(handler)
|
c.peerEvents[kind].excl(handler)
|
||||||
|
|
||||||
proc triggerPeerEvents*(c: ConnManager, peerId: PeerId, event: PeerEvent) {.async.} =
|
proc triggerPeerEvents*(
|
||||||
|
c: ConnManager, peerId: PeerId, event: PeerEvent
|
||||||
|
) {.async: (raises: [CancelledError]).} =
|
||||||
trace "About to trigger peer events", peer = peerId
|
trace "About to trigger peer events", peer = peerId
|
||||||
if c.peerEvents[event.kind].len == 0:
|
if c.peerEvents[event.kind].len == 0:
|
||||||
return
|
return
|
||||||
@@ -174,16 +180,16 @@ proc triggerPeerEvents*(c: ConnManager, peerId: PeerId, event: PeerEvent) {.asyn
|
|||||||
|
|
||||||
proc expectConnection*(
|
proc expectConnection*(
|
||||||
c: ConnManager, p: PeerId, dir: Direction
|
c: ConnManager, p: PeerId, dir: Direction
|
||||||
): Future[Muxer] {.async.} =
|
): Future[Muxer] {.async: (raises: [AlreadyExpectingConnectionError, CancelledError]).} =
|
||||||
## Wait for a peer to connect to us. This will bypass the `MaxConnectionsPerPeer`
|
## Wait for a peer to connect to us. This will bypass the `MaxConnectionsPerPeer`
|
||||||
let key = (p, dir)
|
let key = (p, dir)
|
||||||
if key in c.expectedConnectionsOverLimit:
|
if key in c.expectedConnectionsOverLimit:
|
||||||
raise newException(
|
raise newException(
|
||||||
AlreadyExpectingConnectionError,
|
AlreadyExpectingConnectionError,
|
||||||
"Already expecting an incoming connection from that peer",
|
"Already expecting an incoming connection from that peer: " & shortLog(p),
|
||||||
)
|
)
|
||||||
|
|
||||||
let future = newFuture[Muxer]()
|
let future = Future[Muxer].Raising([CancelledError]).init()
|
||||||
c.expectedConnectionsOverLimit[key] = future
|
c.expectedConnectionsOverLimit[key] = future
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -205,18 +211,18 @@ proc contains*(c: ConnManager, muxer: Muxer): bool =
|
|||||||
let conn = muxer.connection
|
let conn = muxer.connection
|
||||||
return muxer in c.muxed.getOrDefault(conn.peerId)
|
return muxer in c.muxed.getOrDefault(conn.peerId)
|
||||||
|
|
||||||
proc closeMuxer(muxer: Muxer) {.async.} =
|
proc closeMuxer(muxer: Muxer) {.async: (raises: [CancelledError]).} =
|
||||||
trace "Cleaning up muxer", m = muxer
|
trace "Cleaning up muxer", m = muxer
|
||||||
|
|
||||||
await muxer.close()
|
await muxer.close()
|
||||||
if not (isNil(muxer.handler)):
|
if not (isNil(muxer.handler)):
|
||||||
try:
|
try:
|
||||||
await muxer.handler # TODO noraises?
|
await muxer.handler
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
trace "Exception in close muxer handler", description = exc.msg
|
trace "Exception in close muxer handler", description = exc.msg
|
||||||
trace "Cleaned up muxer", m = muxer
|
trace "Cleaned up muxer", m = muxer
|
||||||
|
|
||||||
proc muxCleanup(c: ConnManager, mux: Muxer) {.async.} =
|
proc muxCleanup(c: ConnManager, mux: Muxer) {.async: (raises: []).} =
|
||||||
try:
|
try:
|
||||||
trace "Triggering disconnect events", mux
|
trace "Triggering disconnect events", mux
|
||||||
let peerId = mux.connection.peerId
|
let peerId = mux.connection.peerId
|
||||||
@@ -238,7 +244,7 @@ proc muxCleanup(c: ConnManager, mux: Muxer) {.async.} =
|
|||||||
# do not need to propagate CancelledError and should handle other errors
|
# do not need to propagate CancelledError and should handle other errors
|
||||||
warn "Unexpected exception peer cleanup handler", mux, description = exc.msg
|
warn "Unexpected exception peer cleanup handler", mux, description = exc.msg
|
||||||
|
|
||||||
proc onClose(c: ConnManager, mux: Muxer) {.async.} =
|
proc onClose(c: ConnManager, mux: Muxer) {.async: (raises: []).} =
|
||||||
## connection close even handler
|
## connection close even handler
|
||||||
##
|
##
|
||||||
## triggers the connections resource cleanup
|
## triggers the connections resource cleanup
|
||||||
@@ -272,7 +278,7 @@ proc selectMuxer*(c: ConnManager, peerId: PeerId): Muxer =
|
|||||||
trace "connection not found", peerId
|
trace "connection not found", peerId
|
||||||
return mux
|
return mux
|
||||||
|
|
||||||
proc storeMuxer*(c: ConnManager, muxer: Muxer) {.raises: [CatchableError].} =
|
proc storeMuxer*(c: ConnManager, muxer: Muxer) {.raises: [LPError].} =
|
||||||
## store the connection and muxer
|
## store the connection and muxer
|
||||||
##
|
##
|
||||||
|
|
||||||
@@ -324,7 +330,9 @@ proc storeMuxer*(c: ConnManager, muxer: Muxer) {.raises: [CatchableError].} =
|
|||||||
|
|
||||||
trace "Stored muxer", muxer, direction = $muxer.connection.dir, peers = c.muxed.len
|
trace "Stored muxer", muxer, direction = $muxer.connection.dir, peers = c.muxed.len
|
||||||
|
|
||||||
proc getIncomingSlot*(c: ConnManager): Future[ConnectionSlot] {.async.} =
|
proc getIncomingSlot*(
|
||||||
|
c: ConnManager
|
||||||
|
): Future[ConnectionSlot] {.async: (raises: [CancelledError]).} =
|
||||||
await c.inSema.acquire()
|
await c.inSema.acquire()
|
||||||
return ConnectionSlot(connManager: c, direction: In)
|
return ConnectionSlot(connManager: c, direction: In)
|
||||||
|
|
||||||
@@ -339,25 +347,21 @@ proc getOutgoingSlot*(
|
|||||||
raise newTooManyConnectionsError()
|
raise newTooManyConnectionsError()
|
||||||
return ConnectionSlot(connManager: c, direction: Out)
|
return ConnectionSlot(connManager: c, direction: Out)
|
||||||
|
|
||||||
|
func semaphore(c: ConnManager, dir: Direction): AsyncSemaphore {.inline.} =
|
||||||
|
return if dir == In: c.inSema else: c.outSema
|
||||||
|
|
||||||
proc slotsAvailable*(c: ConnManager, dir: Direction): int =
|
proc slotsAvailable*(c: ConnManager, dir: Direction): int =
|
||||||
case dir
|
return semaphore(c, dir).count
|
||||||
of Direction.In:
|
|
||||||
return c.inSema.count
|
|
||||||
of Direction.Out:
|
|
||||||
return c.outSema.count
|
|
||||||
|
|
||||||
proc release*(cs: ConnectionSlot) =
|
proc release*(cs: ConnectionSlot) =
|
||||||
if cs.direction == In:
|
semaphore(cs.connManager, cs.direction).release()
|
||||||
cs.connManager.inSema.release()
|
|
||||||
else:
|
|
||||||
cs.connManager.outSema.release()
|
|
||||||
|
|
||||||
proc trackConnection*(cs: ConnectionSlot, conn: Connection) =
|
proc trackConnection*(cs: ConnectionSlot, conn: Connection) =
|
||||||
if isNil(conn):
|
if isNil(conn):
|
||||||
cs.release()
|
cs.release()
|
||||||
return
|
return
|
||||||
|
|
||||||
proc semaphoreMonitor() {.async.} =
|
proc semaphoreMonitor() {.async: (raises: [CancelledError]).} =
|
||||||
try:
|
try:
|
||||||
await conn.join()
|
await conn.join()
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
@@ -373,14 +377,18 @@ proc trackMuxer*(cs: ConnectionSlot, mux: Muxer) =
|
|||||||
return
|
return
|
||||||
cs.trackConnection(mux.connection)
|
cs.trackConnection(mux.connection)
|
||||||
|
|
||||||
proc getStream*(c: ConnManager, muxer: Muxer): Future[Connection] {.async.} =
|
proc getStream*(
|
||||||
|
c: ConnManager, muxer: Muxer
|
||||||
|
): Future[Connection] {.async: (raises: [LPStreamError, MuxerError, CancelledError]).} =
|
||||||
## get a muxed stream for the passed muxer
|
## get a muxed stream for the passed muxer
|
||||||
##
|
##
|
||||||
|
|
||||||
if not (isNil(muxer)):
|
if not (isNil(muxer)):
|
||||||
return await muxer.newStream()
|
return await muxer.newStream()
|
||||||
|
|
||||||
proc getStream*(c: ConnManager, peerId: PeerId): Future[Connection] {.async.} =
|
proc getStream*(
|
||||||
|
c: ConnManager, peerId: PeerId
|
||||||
|
): Future[Connection] {.async: (raises: [LPStreamError, MuxerError, CancelledError]).} =
|
||||||
## get a muxed stream for the passed peer from any connection
|
## get a muxed stream for the passed peer from any connection
|
||||||
##
|
##
|
||||||
|
|
||||||
@@ -388,13 +396,13 @@ proc getStream*(c: ConnManager, peerId: PeerId): Future[Connection] {.async.} =
|
|||||||
|
|
||||||
proc getStream*(
|
proc getStream*(
|
||||||
c: ConnManager, peerId: PeerId, dir: Direction
|
c: ConnManager, peerId: PeerId, dir: Direction
|
||||||
): Future[Connection] {.async.} =
|
): Future[Connection] {.async: (raises: [LPStreamError, MuxerError, CancelledError]).} =
|
||||||
## get a muxed stream for the passed peer from a connection with `dir`
|
## get a muxed stream for the passed peer from a connection with `dir`
|
||||||
##
|
##
|
||||||
|
|
||||||
return await c.getStream(c.selectMuxer(peerId, dir))
|
return await c.getStream(c.selectMuxer(peerId, dir))
|
||||||
|
|
||||||
proc dropPeer*(c: ConnManager, peerId: PeerId) {.async.} =
|
proc dropPeer*(c: ConnManager, peerId: PeerId) {.async: (raises: [CancelledError]).} =
|
||||||
## drop connections and cleanup resources for peer
|
## drop connections and cleanup resources for peer
|
||||||
##
|
##
|
||||||
trace "Dropping peer", peerId
|
trace "Dropping peer", peerId
|
||||||
@@ -405,7 +413,7 @@ proc dropPeer*(c: ConnManager, peerId: PeerId) {.async.} =
|
|||||||
|
|
||||||
trace "Peer dropped", peerId
|
trace "Peer dropped", peerId
|
||||||
|
|
||||||
proc close*(c: ConnManager) {.async.} =
|
proc close*(c: ConnManager) {.async: (raises: [CancelledError]).} =
|
||||||
## cleanup resources for the connection
|
## cleanup resources for the connection
|
||||||
## manager
|
## manager
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
from strutils import split, strip, cmpIgnoreCase
|
from strutils import split, strip, cmpIgnoreCase
|
||||||
|
import ../utils/sequninit
|
||||||
|
|
||||||
const libp2p_pki_schemes* {.strdefine.} = "rsa,ed25519,secp256k1,ecnist"
|
const libp2p_pki_schemes* {.strdefine.} = "rsa,ed25519,secp256k1,ecnist"
|
||||||
|
|
||||||
@@ -76,7 +77,7 @@ import nimcrypto/[rijndael, twofish, sha2, hash, hmac]
|
|||||||
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
||||||
import nimcrypto/utils as ncrutils
|
import nimcrypto/utils as ncrutils
|
||||||
import ../utility
|
import ../utility
|
||||||
import stew/results
|
import results
|
||||||
export results, utility
|
export results, utility
|
||||||
|
|
||||||
# This is workaround for Nim's `import` bug
|
# This is workaround for Nim's `import` bug
|
||||||
@@ -176,7 +177,7 @@ proc shuffle*[T](rng: ref HmacDrbgContext, x: var openArray[T]) =
|
|||||||
if x.len == 0:
|
if x.len == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
var randValues = newSeqUninitialized[byte](len(x) * 2)
|
var randValues = newSeqUninit[byte](len(x) * 2)
|
||||||
hmacDrbgGenerate(rng[], randValues)
|
hmacDrbgGenerate(rng[], randValues)
|
||||||
|
|
||||||
for i in countdown(x.high, 1):
|
for i in countdown(x.high, 1):
|
||||||
@@ -873,7 +874,7 @@ proc stretchKeys*(
|
|||||||
var seed = "key expansion"
|
var seed = "key expansion"
|
||||||
result.macsize = 20
|
result.macsize = 20
|
||||||
let length = result.ivsize + result.keysize + result.macsize
|
let length = result.ivsize + result.keysize + result.macsize
|
||||||
result.data = newSeq[byte](2 * length)
|
result.data = newSeqUninit[byte](2 * length)
|
||||||
|
|
||||||
if hashType == "SHA256":
|
if hashType == "SHA256":
|
||||||
makeSecret(result.data, HMAC[sha256], sharedSecret, seed)
|
makeSecret(result.data, HMAC[sha256], sharedSecret, seed)
|
||||||
@@ -904,7 +905,7 @@ template macOpenArray*(secret: Secret, id: int): untyped =
|
|||||||
|
|
||||||
proc iv*(secret: Secret, id: int): seq[byte] {.inline.} =
|
proc iv*(secret: Secret, id: int): seq[byte] {.inline.} =
|
||||||
## Get array of bytes with with initial vector.
|
## Get array of bytes with with initial vector.
|
||||||
result = newSeq[byte](secret.ivsize)
|
result = newSeqUninit[byte](secret.ivsize)
|
||||||
var offset =
|
var offset =
|
||||||
if id == 0:
|
if id == 0:
|
||||||
0
|
0
|
||||||
@@ -913,7 +914,7 @@ proc iv*(secret: Secret, id: int): seq[byte] {.inline.} =
|
|||||||
copyMem(addr result[0], unsafeAddr secret.data[offset], secret.ivsize)
|
copyMem(addr result[0], unsafeAddr secret.data[offset], secret.ivsize)
|
||||||
|
|
||||||
proc key*(secret: Secret, id: int): seq[byte] {.inline.} =
|
proc key*(secret: Secret, id: int): seq[byte] {.inline.} =
|
||||||
result = newSeq[byte](secret.keysize)
|
result = newSeqUninit[byte](secret.keysize)
|
||||||
var offset =
|
var offset =
|
||||||
if id == 0:
|
if id == 0:
|
||||||
0
|
0
|
||||||
@@ -923,7 +924,7 @@ proc key*(secret: Secret, id: int): seq[byte] {.inline.} =
|
|||||||
copyMem(addr result[0], unsafeAddr secret.data[offset], secret.keysize)
|
copyMem(addr result[0], unsafeAddr secret.data[offset], secret.keysize)
|
||||||
|
|
||||||
proc mac*(secret: Secret, id: int): seq[byte] {.inline.} =
|
proc mac*(secret: Secret, id: int): seq[byte] {.inline.} =
|
||||||
result = newSeq[byte](secret.macsize)
|
result = newSeqUninit[byte](secret.macsize)
|
||||||
var offset =
|
var offset =
|
||||||
if id == 0:
|
if id == 0:
|
||||||
0
|
0
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import bearssl/[ec, rand]
|
import bearssl/[ec, rand]
|
||||||
import stew/results
|
import results
|
||||||
from stew/assign2 import assign
|
from stew/assign2 import assign
|
||||||
export results
|
export results
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ import bearssl/[ec, rand, hash]
|
|||||||
import nimcrypto/utils as ncrutils
|
import nimcrypto/utils as ncrutils
|
||||||
import minasn1
|
import minasn1
|
||||||
export minasn1.Asn1Error
|
export minasn1.Asn1Error
|
||||||
import stew/[results, ctops]
|
import stew/ctops
|
||||||
|
import results
|
||||||
|
import ../utils/sequninit
|
||||||
|
|
||||||
import ../utility
|
import ../utility
|
||||||
|
|
||||||
@@ -457,7 +459,7 @@ proc getBytes*(seckey: EcPrivateKey): EcResult[seq[byte]] =
|
|||||||
if isNil(seckey):
|
if isNil(seckey):
|
||||||
return err(EcKeyIncorrectError)
|
return err(EcKeyIncorrectError)
|
||||||
if seckey.key.curve in EcSupportedCurvesCint:
|
if seckey.key.curve in EcSupportedCurvesCint:
|
||||||
var res = newSeq[byte]()
|
var res = newSeqUninit[byte](0)
|
||||||
let length = ?seckey.toBytes(res)
|
let length = ?seckey.toBytes(res)
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
discard ?seckey.toBytes(res)
|
discard ?seckey.toBytes(res)
|
||||||
@@ -470,7 +472,7 @@ proc getBytes*(pubkey: EcPublicKey): EcResult[seq[byte]] =
|
|||||||
if isNil(pubkey):
|
if isNil(pubkey):
|
||||||
return err(EcKeyIncorrectError)
|
return err(EcKeyIncorrectError)
|
||||||
if pubkey.key.curve in EcSupportedCurvesCint:
|
if pubkey.key.curve in EcSupportedCurvesCint:
|
||||||
var res = newSeq[byte]()
|
var res = newSeqUninit[byte](0)
|
||||||
let length = ?pubkey.toBytes(res)
|
let length = ?pubkey.toBytes(res)
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
discard ?pubkey.toBytes(res)
|
discard ?pubkey.toBytes(res)
|
||||||
@@ -482,7 +484,7 @@ proc getBytes*(sig: EcSignature): EcResult[seq[byte]] =
|
|||||||
## Serialize EC signature ``sig`` to ASN.1 DER binary form and return it.
|
## Serialize EC signature ``sig`` to ASN.1 DER binary form and return it.
|
||||||
if isNil(sig):
|
if isNil(sig):
|
||||||
return err(EcSignatureError)
|
return err(EcSignatureError)
|
||||||
var res = newSeq[byte]()
|
var res = newSeqUninit[byte](0)
|
||||||
let length = ?sig.toBytes(res)
|
let length = ?sig.toBytes(res)
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
discard ?sig.toBytes(res)
|
discard ?sig.toBytes(res)
|
||||||
@@ -493,7 +495,7 @@ proc getRawBytes*(seckey: EcPrivateKey): EcResult[seq[byte]] =
|
|||||||
if isNil(seckey):
|
if isNil(seckey):
|
||||||
return err(EcKeyIncorrectError)
|
return err(EcKeyIncorrectError)
|
||||||
if seckey.key.curve in EcSupportedCurvesCint:
|
if seckey.key.curve in EcSupportedCurvesCint:
|
||||||
var res = newSeq[byte]()
|
var res = newSeqUninit[byte](0)
|
||||||
let length = ?seckey.toRawBytes(res)
|
let length = ?seckey.toRawBytes(res)
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
discard ?seckey.toRawBytes(res)
|
discard ?seckey.toRawBytes(res)
|
||||||
@@ -506,7 +508,7 @@ proc getRawBytes*(pubkey: EcPublicKey): EcResult[seq[byte]] =
|
|||||||
if isNil(pubkey):
|
if isNil(pubkey):
|
||||||
return err(EcKeyIncorrectError)
|
return err(EcKeyIncorrectError)
|
||||||
if pubkey.key.curve in EcSupportedCurvesCint:
|
if pubkey.key.curve in EcSupportedCurvesCint:
|
||||||
var res = newSeq[byte]()
|
var res = newSeqUninit[byte](0)
|
||||||
let length = ?pubkey.toRawBytes(res)
|
let length = ?pubkey.toRawBytes(res)
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
discard ?pubkey.toRawBytes(res)
|
discard ?pubkey.toRawBytes(res)
|
||||||
@@ -518,7 +520,7 @@ proc getRawBytes*(sig: EcSignature): EcResult[seq[byte]] =
|
|||||||
## Serialize EC signature ``sig`` to raw binary form and return it.
|
## Serialize EC signature ``sig`` to raw binary form and return it.
|
||||||
if isNil(sig):
|
if isNil(sig):
|
||||||
return err(EcSignatureError)
|
return err(EcSignatureError)
|
||||||
var res = newSeq[byte]()
|
var res = newSeqUninit[byte](0)
|
||||||
let length = ?sig.toBytes(res)
|
let length = ?sig.toBytes(res)
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
discard ?sig.toBytes(res)
|
discard ?sig.toBytes(res)
|
||||||
@@ -928,7 +930,7 @@ proc getSecret*(pubkey: EcPublicKey, seckey: EcPrivateKey): seq[byte] =
|
|||||||
var data: array[Secret521Length, byte]
|
var data: array[Secret521Length, byte]
|
||||||
let res = toSecret(pubkey, seckey, data)
|
let res = toSecret(pubkey, seckey, data)
|
||||||
if res > 0:
|
if res > 0:
|
||||||
result = newSeq[byte](res)
|
result = newSeqUninit[byte](res)
|
||||||
copyMem(addr result[0], addr data[0], res)
|
copyMem(addr result[0], addr data[0], res)
|
||||||
|
|
||||||
proc sign*[T: byte | char](
|
proc sign*[T: byte | char](
|
||||||
@@ -942,7 +944,7 @@ proc sign*[T: byte | char](
|
|||||||
var impl = ecGetDefault()
|
var impl = ecGetDefault()
|
||||||
if seckey.key.curve in EcSupportedCurvesCint:
|
if seckey.key.curve in EcSupportedCurvesCint:
|
||||||
var sig = new EcSignature
|
var sig = new EcSignature
|
||||||
sig.buffer = newSeq[byte](256)
|
sig.buffer = newSeqUninit[byte](256)
|
||||||
var kv = addr sha256Vtable
|
var kv = addr sha256Vtable
|
||||||
kv.init(addr hc.vtable)
|
kv.init(addr hc.vtable)
|
||||||
if len(message) > 0:
|
if len(message) > 0:
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ import constants
|
|||||||
import nimcrypto/[hash, sha2]
|
import nimcrypto/[hash, sha2]
|
||||||
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
||||||
import nimcrypto/utils as ncrutils
|
import nimcrypto/utils as ncrutils
|
||||||
import stew/[results, ctops]
|
import results
|
||||||
|
import stew/ctops
|
||||||
|
|
||||||
import ../../utility
|
import ../../utility
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ proc hkdf*[T: sha256, len: static int](
|
|||||||
if salt.len > 0:
|
if salt.len > 0:
|
||||||
unsafeAddr salt[0]
|
unsafeAddr salt[0]
|
||||||
else:
|
else:
|
||||||
nil
|
nil,
|
||||||
,
|
|
||||||
csize_t(salt.len),
|
csize_t(salt.len),
|
||||||
)
|
)
|
||||||
hkdfInject(
|
hkdfInject(
|
||||||
@@ -37,8 +36,7 @@ proc hkdf*[T: sha256, len: static int](
|
|||||||
if ikm.len > 0:
|
if ikm.len > 0:
|
||||||
unsafeAddr ikm[0]
|
unsafeAddr ikm[0]
|
||||||
else:
|
else:
|
||||||
nil
|
nil,
|
||||||
,
|
|
||||||
csize_t(ikm.len),
|
csize_t(ikm.len),
|
||||||
)
|
)
|
||||||
hkdfFlip(ctx)
|
hkdfFlip(ctx)
|
||||||
@@ -48,8 +46,7 @@ proc hkdf*[T: sha256, len: static int](
|
|||||||
if info.len > 0:
|
if info.len > 0:
|
||||||
unsafeAddr info[0]
|
unsafeAddr info[0]
|
||||||
else:
|
else:
|
||||||
nil
|
nil,
|
||||||
,
|
|
||||||
csize_t(info.len),
|
csize_t(info.len),
|
||||||
addr outputs[i][0],
|
addr outputs[i][0],
|
||||||
csize_t(outputs[i].len),
|
csize_t(outputs[i].len),
|
||||||
|
|||||||
@@ -11,11 +11,13 @@
|
|||||||
|
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import stew/[endians2, results, ctops]
|
import stew/[endians2, ctops]
|
||||||
|
import results
|
||||||
export results
|
export results
|
||||||
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
||||||
import nimcrypto/utils as ncrutils
|
import nimcrypto/utils as ncrutils
|
||||||
import ../utility
|
import ../utility
|
||||||
|
import ../utils/sequninit
|
||||||
|
|
||||||
type
|
type
|
||||||
Asn1Error* {.pure.} = enum
|
Asn1Error* {.pure.} = enum
|
||||||
@@ -291,28 +293,6 @@ proc asn1EncodeBitString*(
|
|||||||
dest[2 + lenlen + bytelen - 1] = lastbyte and mask
|
dest[2 + lenlen + bytelen - 1] = lastbyte and mask
|
||||||
res
|
res
|
||||||
|
|
||||||
proc asn1EncodeTag[T: SomeUnsignedInt](dest: var openArray[byte], value: T): int =
|
|
||||||
var v = value
|
|
||||||
if value <= cast[T](0x7F):
|
|
||||||
if len(dest) >= 1:
|
|
||||||
dest[0] = cast[byte](value)
|
|
||||||
1
|
|
||||||
else:
|
|
||||||
var s = 0
|
|
||||||
var res = 0
|
|
||||||
while v != 0:
|
|
||||||
v = v shr 7
|
|
||||||
s += 7
|
|
||||||
inc(res)
|
|
||||||
if len(dest) >= res:
|
|
||||||
var k = 0
|
|
||||||
while s != 0:
|
|
||||||
s -= 7
|
|
||||||
dest[k] = cast[byte](((value shr s) and cast[T](0x7F)) or cast[T](0x80))
|
|
||||||
inc(k)
|
|
||||||
dest[k - 1] = dest[k - 1] and 0x7F'u8
|
|
||||||
res
|
|
||||||
|
|
||||||
proc asn1EncodeOid*(dest: var openArray[byte], value: openArray[byte]): int =
|
proc asn1EncodeOid*(dest: var openArray[byte], value: openArray[byte]): int =
|
||||||
## Encode array of bytes ``value`` as ASN.1 DER `OBJECT IDENTIFIER` and return
|
## Encode array of bytes ``value`` as ASN.1 DER `OBJECT IDENTIFIER` and return
|
||||||
## number of bytes (octets) used.
|
## number of bytes (octets) used.
|
||||||
@@ -665,9 +645,6 @@ proc read*(ab: var Asn1Buffer): Asn1Result[Asn1Field] =
|
|||||||
return ok(field)
|
return ok(field)
|
||||||
else:
|
else:
|
||||||
return err(Asn1Error.NoSupport)
|
return err(Asn1Error.NoSupport)
|
||||||
|
|
||||||
inclass = false
|
|
||||||
ttag = 0
|
|
||||||
else:
|
else:
|
||||||
return err(Asn1Error.NoSupport)
|
return err(Asn1Error.NoSupport)
|
||||||
|
|
||||||
@@ -703,15 +680,15 @@ proc init*(t: typedesc[Asn1Buffer], data: string): Asn1Buffer =
|
|||||||
|
|
||||||
proc init*(t: typedesc[Asn1Buffer]): Asn1Buffer =
|
proc init*(t: typedesc[Asn1Buffer]): Asn1Buffer =
|
||||||
## Initialize empty ``Asn1Buffer``.
|
## Initialize empty ``Asn1Buffer``.
|
||||||
Asn1Buffer(buffer: newSeq[byte]())
|
Asn1Buffer(buffer: newSeqUninit[byte](0))
|
||||||
|
|
||||||
proc init*(t: typedesc[Asn1Composite], tag: Asn1Tag): Asn1Composite =
|
proc init*(t: typedesc[Asn1Composite], tag: Asn1Tag): Asn1Composite =
|
||||||
## Initialize ``Asn1Composite`` with tag ``tag``.
|
## Initialize ``Asn1Composite`` with tag ``tag``.
|
||||||
Asn1Composite(tag: tag, buffer: newSeq[byte]())
|
Asn1Composite(tag: tag, buffer: newSeqUninit[byte](0))
|
||||||
|
|
||||||
proc init*(t: typedesc[Asn1Composite], idx: int): Asn1Composite =
|
proc init*(t: typedesc[Asn1Composite], idx: int): Asn1Composite =
|
||||||
## Initialize ``Asn1Composite`` with tag context-specific id ``id``.
|
## Initialize ``Asn1Composite`` with tag context-specific id ``id``.
|
||||||
Asn1Composite(tag: Asn1Tag.Context, idx: idx, buffer: newSeq[byte]())
|
Asn1Composite(tag: Asn1Tag.Context, idx: idx, buffer: newSeqUninit[byte](0))
|
||||||
|
|
||||||
proc `$`*(buffer: Asn1Buffer): string =
|
proc `$`*(buffer: Asn1Buffer): string =
|
||||||
## Return string representation of ``buffer``.
|
## Return string representation of ``buffer``.
|
||||||
|
|||||||
@@ -17,9 +17,11 @@
|
|||||||
|
|
||||||
import bearssl/[rsa, rand, hash]
|
import bearssl/[rsa, rand, hash]
|
||||||
import minasn1
|
import minasn1
|
||||||
import stew/[results, ctops]
|
import results
|
||||||
|
import stew/ctops
|
||||||
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
# We use `ncrutils` for constant-time hexadecimal encoding/decoding procedures.
|
||||||
import nimcrypto/utils as ncrutils
|
import nimcrypto/utils as ncrutils
|
||||||
|
import ../utils/sequninit
|
||||||
|
|
||||||
export Asn1Error, results
|
export Asn1Error, results
|
||||||
|
|
||||||
@@ -123,7 +125,7 @@ proc random*[T: RsaKP](
|
|||||||
length = eko + ((bits + 7) shr 3)
|
length = eko + ((bits + 7) shr 3)
|
||||||
|
|
||||||
let res = new T
|
let res = new T
|
||||||
res.buffer = newSeq[byte](length)
|
res.buffer = newSeqUninit[byte](length)
|
||||||
|
|
||||||
var keygen = rsaKeygenGetDefault()
|
var keygen = rsaKeygenGetDefault()
|
||||||
|
|
||||||
@@ -168,7 +170,7 @@ proc copy*[T: RsaPKI](key: T): T =
|
|||||||
key.seck.dqlen.uint + key.seck.iqlen.uint + key.pubk.nlen.uint +
|
key.seck.dqlen.uint + key.seck.iqlen.uint + key.pubk.nlen.uint +
|
||||||
key.pubk.elen.uint + key.pexplen.uint
|
key.pubk.elen.uint + key.pexplen.uint
|
||||||
result = new RsaPrivateKey
|
result = new RsaPrivateKey
|
||||||
result.buffer = newSeq[byte](length)
|
result.buffer = newSeqUninit[byte](length)
|
||||||
let po: uint = 0
|
let po: uint = 0
|
||||||
let qo = po + key.seck.plen
|
let qo = po + key.seck.plen
|
||||||
let dpo = qo + key.seck.qlen
|
let dpo = qo + key.seck.qlen
|
||||||
@@ -206,7 +208,7 @@ proc copy*[T: RsaPKI](key: T): T =
|
|||||||
if len(key.buffer) > 0:
|
if len(key.buffer) > 0:
|
||||||
let length = key.key.nlen + key.key.elen
|
let length = key.key.nlen + key.key.elen
|
||||||
result = new RsaPublicKey
|
result = new RsaPublicKey
|
||||||
result.buffer = newSeq[byte](length)
|
result.buffer = newSeqUninit[byte](length)
|
||||||
let no = 0
|
let no = 0
|
||||||
let eo = no + key.key.nlen
|
let eo = no + key.key.nlen
|
||||||
copyMem(addr result.buffer[no], key.key.n, key.key.nlen)
|
copyMem(addr result.buffer[no], key.key.n, key.key.nlen)
|
||||||
@@ -225,7 +227,7 @@ proc getPublicKey*(key: RsaPrivateKey): RsaPublicKey =
|
|||||||
doAssert(not isNil(key))
|
doAssert(not isNil(key))
|
||||||
let length = key.pubk.nlen + key.pubk.elen
|
let length = key.pubk.nlen + key.pubk.elen
|
||||||
result = new RsaPublicKey
|
result = new RsaPublicKey
|
||||||
result.buffer = newSeq[byte](length)
|
result.buffer = newSeqUninit[byte](length)
|
||||||
result.key.n = addr result.buffer[0]
|
result.key.n = addr result.buffer[0]
|
||||||
result.key.e = addr result.buffer[key.pubk.nlen]
|
result.key.e = addr result.buffer[key.pubk.nlen]
|
||||||
copyMem(addr result.buffer[0], cast[pointer](key.pubk.n), key.pubk.nlen)
|
copyMem(addr result.buffer[0], cast[pointer](key.pubk.n), key.pubk.nlen)
|
||||||
@@ -356,7 +358,7 @@ proc getBytes*(key: RsaPrivateKey): RsaResult[seq[byte]] =
|
|||||||
## return it.
|
## return it.
|
||||||
if isNil(key):
|
if isNil(key):
|
||||||
return err(RsaKeyIncorrectError)
|
return err(RsaKeyIncorrectError)
|
||||||
var res = newSeq[byte](4096)
|
var res = newSeqUninit[byte](4096)
|
||||||
let length = ?key.toBytes(res)
|
let length = ?key.toBytes(res)
|
||||||
if length > 0:
|
if length > 0:
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
@@ -369,7 +371,7 @@ proc getBytes*(key: RsaPublicKey): RsaResult[seq[byte]] =
|
|||||||
## return it.
|
## return it.
|
||||||
if isNil(key):
|
if isNil(key):
|
||||||
return err(RsaKeyIncorrectError)
|
return err(RsaKeyIncorrectError)
|
||||||
var res = newSeq[byte](4096)
|
var res = newSeqUninit[byte](4096)
|
||||||
let length = ?key.toBytes(res)
|
let length = ?key.toBytes(res)
|
||||||
if length > 0:
|
if length > 0:
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
@@ -381,7 +383,7 @@ proc getBytes*(sig: RsaSignature): RsaResult[seq[byte]] =
|
|||||||
## Serialize RSA signature ``sig`` to raw binary form and return it.
|
## Serialize RSA signature ``sig`` to raw binary form and return it.
|
||||||
if isNil(sig):
|
if isNil(sig):
|
||||||
return err(RsaSignatureError)
|
return err(RsaSignatureError)
|
||||||
var res = newSeq[byte](4096)
|
var res = newSeqUninit[byte](4096)
|
||||||
let length = ?sig.toBytes(res)
|
let length = ?sig.toBytes(res)
|
||||||
if length > 0:
|
if length > 0:
|
||||||
res.setLen(length)
|
res.setLen(length)
|
||||||
@@ -752,7 +754,7 @@ proc sign*[T: byte | char](
|
|||||||
var hash: array[32, byte]
|
var hash: array[32, byte]
|
||||||
let impl = rsaPkcs1SignGetDefault()
|
let impl = rsaPkcs1SignGetDefault()
|
||||||
var res = new RsaSignature
|
var res = new RsaSignature
|
||||||
res.buffer = newSeq[byte]((key.seck.nBitlen + 7) shr 3)
|
res.buffer = newSeqUninit[byte]((key.seck.nBitlen + 7) shr 3)
|
||||||
var kv = addr sha256Vtable
|
var kv = addr sha256Vtable
|
||||||
kv.init(addr hc.vtable)
|
kv.init(addr hc.vtable)
|
||||||
if len(message) > 0:
|
if len(message) > 0:
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import bearssl/rand
|
import bearssl/rand
|
||||||
import secp256k1, stew/[byteutils, results], nimcrypto/[hash, sha2]
|
import secp256k1, results, stew/byteutils, nimcrypto/[hash, sha2]
|
||||||
|
import ../utils/sequninit
|
||||||
|
|
||||||
export sha2, results, rand
|
export sha2, results, rand
|
||||||
|
|
||||||
@@ -85,8 +86,9 @@ proc init*(sig: var SkSignature, data: string): SkResult[void] =
|
|||||||
var buffer: seq[byte]
|
var buffer: seq[byte]
|
||||||
try:
|
try:
|
||||||
buffer = hexToSeqByte(data)
|
buffer = hexToSeqByte(data)
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
return err("secp: Hex to bytes failed")
|
let errMsg = "secp: Hex to bytes failed: " & e.msg
|
||||||
|
return err(errMsg.cstring)
|
||||||
init(sig, buffer)
|
init(sig, buffer)
|
||||||
|
|
||||||
proc init*(t: typedesc[SkPrivateKey], data: openArray[byte]): SkResult[SkPrivateKey] =
|
proc init*(t: typedesc[SkPrivateKey], data: openArray[byte]): SkResult[SkPrivateKey] =
|
||||||
@@ -181,7 +183,7 @@ proc getBytes*(key: SkPublicKey): seq[byte] {.inline.} =
|
|||||||
|
|
||||||
proc getBytes*(sig: SkSignature): seq[byte] {.inline.} =
|
proc getBytes*(sig: SkSignature): seq[byte] {.inline.} =
|
||||||
## Serialize Secp256k1 `signature` and return it.
|
## Serialize Secp256k1 `signature` and return it.
|
||||||
result = newSeq[byte](72)
|
result = newSeqUninit[byte](72)
|
||||||
let length = toBytes(sig, result)
|
let length = toBytes(sig, result)
|
||||||
result.setLen(length)
|
result.setLen(length)
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,148 +0,0 @@
|
|||||||
# Nim-Libp2p
|
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
|
||||||
# Licensed under either of
|
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
|
||||||
# at your option.
|
|
||||||
# This file may not be copied, modified, or distributed except according to
|
|
||||||
# those terms.
|
|
||||||
|
|
||||||
{.push raises: [].}
|
|
||||||
|
|
||||||
## This module implements Pool of StreamTransport.
|
|
||||||
import chronos
|
|
||||||
|
|
||||||
const DefaultPoolSize* = 8 ## Default pool size
|
|
||||||
|
|
||||||
type
|
|
||||||
ConnectionFlags = enum
|
|
||||||
None
|
|
||||||
Busy
|
|
||||||
|
|
||||||
PoolItem = object
|
|
||||||
transp*: StreamTransport
|
|
||||||
flags*: set[ConnectionFlags]
|
|
||||||
|
|
||||||
PoolState = enum
|
|
||||||
Connecting
|
|
||||||
Connected
|
|
||||||
Closing
|
|
||||||
Closed
|
|
||||||
|
|
||||||
TransportPool* = ref object ## Transports pool object
|
|
||||||
transports: seq[PoolItem]
|
|
||||||
busyCount: int
|
|
||||||
state: PoolState
|
|
||||||
bufferSize: int
|
|
||||||
event: AsyncEvent
|
|
||||||
|
|
||||||
TransportPoolError* = object of AsyncError
|
|
||||||
|
|
||||||
proc waitAll[T](futs: seq[Future[T]]): Future[void] =
|
|
||||||
## Performs waiting for all Future[T].
|
|
||||||
var counter = len(futs)
|
|
||||||
var retFuture = newFuture[void]("connpool.waitAllConnections")
|
|
||||||
proc cb(udata: pointer) =
|
|
||||||
dec(counter)
|
|
||||||
if counter == 0:
|
|
||||||
retFuture.complete()
|
|
||||||
|
|
||||||
for fut in futs:
|
|
||||||
fut.addCallback(cb)
|
|
||||||
return retFuture
|
|
||||||
|
|
||||||
proc newPool*(
|
|
||||||
address: TransportAddress,
|
|
||||||
poolsize: int = DefaultPoolSize,
|
|
||||||
bufferSize = DefaultStreamBufferSize,
|
|
||||||
): Future[TransportPool] {.async.} =
|
|
||||||
## Establish pool of connections to address ``address`` with size
|
|
||||||
## ``poolsize``.
|
|
||||||
var pool = new TransportPool
|
|
||||||
pool.bufferSize = bufferSize
|
|
||||||
pool.transports = newSeq[PoolItem](poolsize)
|
|
||||||
var conns = newSeq[Future[StreamTransport]](poolsize)
|
|
||||||
pool.state = Connecting
|
|
||||||
for i in 0 ..< poolsize:
|
|
||||||
conns[i] = connect(address, bufferSize)
|
|
||||||
# Waiting for all connections to be established.
|
|
||||||
await waitAll(conns)
|
|
||||||
# Checking connections and preparing pool.
|
|
||||||
for i in 0 ..< poolsize:
|
|
||||||
if conns[i].failed:
|
|
||||||
raise conns[i].error
|
|
||||||
else:
|
|
||||||
let transp = conns[i].read()
|
|
||||||
let item = PoolItem(transp: transp)
|
|
||||||
pool.transports[i] = item
|
|
||||||
# Setup available connections event
|
|
||||||
pool.event = newAsyncEvent()
|
|
||||||
pool.state = Connected
|
|
||||||
result = pool
|
|
||||||
|
|
||||||
proc acquire*(pool: TransportPool): Future[StreamTransport] {.async.} =
|
|
||||||
## Acquire non-busy connection from pool ``pool``.
|
|
||||||
var transp: StreamTransport
|
|
||||||
if pool.state in {Connected}:
|
|
||||||
while true:
|
|
||||||
if pool.busyCount < len(pool.transports):
|
|
||||||
for conn in pool.transports.mitems():
|
|
||||||
if Busy notin conn.flags:
|
|
||||||
conn.flags.incl(Busy)
|
|
||||||
inc(pool.busyCount)
|
|
||||||
transp = conn.transp
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
await pool.event.wait()
|
|
||||||
pool.event.clear()
|
|
||||||
|
|
||||||
if not isNil(transp):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise newException(TransportPoolError, "Pool is not ready!")
|
|
||||||
result = transp
|
|
||||||
|
|
||||||
proc release*(pool: TransportPool, transp: StreamTransport) =
|
|
||||||
## Release connection ``transp`` back to pool ``pool``.
|
|
||||||
if pool.state in {Connected, Closing}:
|
|
||||||
var found = false
|
|
||||||
for conn in pool.transports.mitems():
|
|
||||||
if conn.transp == transp:
|
|
||||||
conn.flags.excl(Busy)
|
|
||||||
dec(pool.busyCount)
|
|
||||||
pool.event.fire()
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
raise newException(TransportPoolError, "Transport not bound to pool!")
|
|
||||||
else:
|
|
||||||
raise newException(TransportPoolError, "Pool is not ready!")
|
|
||||||
|
|
||||||
proc join*(pool: TransportPool) {.async.} =
|
|
||||||
## Waiting for all connection to become available.
|
|
||||||
if pool.state in {Connected, Closing}:
|
|
||||||
while true:
|
|
||||||
if pool.busyCount == 0:
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
await pool.event.wait()
|
|
||||||
pool.event.clear()
|
|
||||||
elif pool.state == Connecting:
|
|
||||||
raise newException(TransportPoolError, "Pool is not ready!")
|
|
||||||
|
|
||||||
proc close*(pool: TransportPool) {.async.} =
|
|
||||||
## Closes transports pool ``pool`` and release all resources.
|
|
||||||
if pool.state == Connected:
|
|
||||||
pool.state = Closing
|
|
||||||
# Waiting for all transports to become available.
|
|
||||||
await pool.join()
|
|
||||||
# Closing all transports
|
|
||||||
var pending = newSeq[Future[void]](len(pool.transports))
|
|
||||||
for i in 0 ..< len(pool.transports):
|
|
||||||
let transp = pool.transports[i].transp
|
|
||||||
transp.close()
|
|
||||||
pending[i] = transp.join()
|
|
||||||
# Waiting for all transports to be closed
|
|
||||||
await waitAll(pending)
|
|
||||||
# Mark pool as `Closed`.
|
|
||||||
pool.state = Closed
|
|
||||||
@@ -10,12 +10,14 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import chronos
|
import chronos
|
||||||
import stew/results
|
import results
|
||||||
import peerid, stream/connection, transports/transport
|
import peerid, stream/connection, transports/transport, muxers/muxer
|
||||||
|
|
||||||
export results
|
export results
|
||||||
|
|
||||||
type Dial* = ref object of RootObj
|
type
|
||||||
|
Dial* = ref object of RootObj
|
||||||
|
DialFailedError* = object of LPError
|
||||||
|
|
||||||
method connect*(
|
method connect*(
|
||||||
self: Dial,
|
self: Dial,
|
||||||
@@ -24,28 +26,44 @@ method connect*(
|
|||||||
forceDial = false,
|
forceDial = false,
|
||||||
reuseConnection = true,
|
reuseConnection = true,
|
||||||
dir = Direction.Out,
|
dir = Direction.Out,
|
||||||
) {.async, base.} =
|
) {.base, async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## connect remote peer without negotiating
|
## connect remote peer without negotiating
|
||||||
## a protocol
|
## a protocol
|
||||||
##
|
##
|
||||||
|
|
||||||
doAssert(false, "Not implemented!")
|
doAssert(false, "[Dial.connect] abstract method not implemented!")
|
||||||
|
|
||||||
method connect*(
|
method connect*(
|
||||||
self: Dial, address: MultiAddress, allowUnknownPeerId = false
|
self: Dial, address: MultiAddress, allowUnknownPeerId = false
|
||||||
): Future[PeerId] {.async, base.} =
|
): Future[PeerId] {.base, async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## Connects to a peer and retrieve its PeerId
|
## Connects to a peer and retrieve its PeerId
|
||||||
|
|
||||||
doAssert(false, "Not implemented!")
|
doAssert(false, "[Dial.connect] abstract method not implemented!")
|
||||||
|
|
||||||
method dial*(
|
method dial*(
|
||||||
self: Dial, peerId: PeerId, protos: seq[string]
|
self: Dial, peerId: PeerId, protos: seq[string]
|
||||||
): Future[Connection] {.async, base.} =
|
): Future[Connection] {.base, async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## create a protocol stream over an
|
## create a protocol stream over an
|
||||||
## existing connection
|
## existing connection
|
||||||
##
|
##
|
||||||
|
|
||||||
doAssert(false, "Not implemented!")
|
doAssert(false, "[Dial.dial] abstract method not implemented!")
|
||||||
|
|
||||||
|
method dialAndUpgrade*(
|
||||||
|
self: Dial,
|
||||||
|
peerId: Opt[PeerId],
|
||||||
|
hostname: string,
|
||||||
|
addrs: MultiAddress,
|
||||||
|
dir = Direction.Out,
|
||||||
|
): Future[Muxer] {.base, async: (raises: [CancelledError]).} =
|
||||||
|
doAssert(false, "[Dial.dialAndUpgrade] abstract method not implemented!")
|
||||||
|
|
||||||
|
method dialAndUpgrade*(
|
||||||
|
self: Dial, peerId: Opt[PeerId], addrs: seq[MultiAddress], dir = Direction.Out
|
||||||
|
): Future[Muxer] {.
|
||||||
|
base, async: (raises: [CancelledError, MaError, TransportAddressError, LPError])
|
||||||
|
.} =
|
||||||
|
doAssert(false, "[Dial.dialAndUpgrade] abstract method not implemented!")
|
||||||
|
|
||||||
method dial*(
|
method dial*(
|
||||||
self: Dial,
|
self: Dial,
|
||||||
@@ -53,17 +71,24 @@ method dial*(
|
|||||||
addrs: seq[MultiAddress],
|
addrs: seq[MultiAddress],
|
||||||
protos: seq[string],
|
protos: seq[string],
|
||||||
forceDial = false,
|
forceDial = false,
|
||||||
): Future[Connection] {.async, base.} =
|
): Future[Connection] {.base, async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## create a protocol stream and establish
|
## create a protocol stream and establish
|
||||||
## a connection if one doesn't exist already
|
## a connection if one doesn't exist already
|
||||||
##
|
##
|
||||||
|
|
||||||
doAssert(false, "Not implemented!")
|
doAssert(false, "[Dial.dial] abstract method not implemented!")
|
||||||
|
|
||||||
method addTransport*(self: Dial, transport: Transport) {.base.} =
|
method addTransport*(self: Dial, transport: Transport) {.base.} =
|
||||||
doAssert(false, "Not implemented!")
|
doAssert(false, "[Dial.addTransport] abstract method not implemented!")
|
||||||
|
|
||||||
|
method negotiateStream*(
|
||||||
|
self: Dial, conn: Connection, protos: seq[string]
|
||||||
|
): Future[Connection] {.base, async: (raises: [CatchableError]).} =
|
||||||
|
doAssert(false, "[Dial.negotiateStream] abstract method not implemented!")
|
||||||
|
|
||||||
method tryDial*(
|
method tryDial*(
|
||||||
self: Dial, peerId: PeerId, addrs: seq[MultiAddress]
|
self: Dial, peerId: PeerId, addrs: seq[MultiAddress]
|
||||||
): Future[Opt[MultiAddress]] {.async, base.} =
|
): Future[Opt[MultiAddress]] {.
|
||||||
doAssert(false, "Not implemented!")
|
base, async: (raises: [DialFailedError, CancelledError])
|
||||||
|
.} =
|
||||||
|
doAssert(false, "[Dial.tryDial] abstract method not implemented!")
|
||||||
|
|||||||
@@ -9,8 +9,7 @@
|
|||||||
|
|
||||||
import std/tables
|
import std/tables
|
||||||
|
|
||||||
import stew/results
|
import pkg/[chronos, chronicles, metrics, results]
|
||||||
import pkg/[chronos, chronicles, metrics]
|
|
||||||
|
|
||||||
import
|
import
|
||||||
dial,
|
dial,
|
||||||
@@ -36,31 +35,28 @@ declareCounter(libp2p_total_dial_attempts, "total attempted dials")
|
|||||||
declareCounter(libp2p_successful_dials, "dialed successful peers")
|
declareCounter(libp2p_successful_dials, "dialed successful peers")
|
||||||
declareCounter(libp2p_failed_dials, "failed dials")
|
declareCounter(libp2p_failed_dials, "failed dials")
|
||||||
|
|
||||||
type
|
type Dialer* = ref object of Dial
|
||||||
DialFailedError* = object of LPError
|
localPeerId*: PeerId
|
||||||
|
connManager: ConnManager
|
||||||
|
dialLock: Table[PeerId, AsyncLock]
|
||||||
|
transports: seq[Transport]
|
||||||
|
peerStore: PeerStore
|
||||||
|
nameResolver: NameResolver
|
||||||
|
|
||||||
Dialer* = ref object of Dial
|
method dialAndUpgrade*(
|
||||||
localPeerId*: PeerId
|
|
||||||
connManager: ConnManager
|
|
||||||
dialLock: Table[PeerId, AsyncLock]
|
|
||||||
transports: seq[Transport]
|
|
||||||
peerStore: PeerStore
|
|
||||||
nameResolver: NameResolver
|
|
||||||
|
|
||||||
proc dialAndUpgrade(
|
|
||||||
self: Dialer,
|
self: Dialer,
|
||||||
peerId: Opt[PeerId],
|
peerId: Opt[PeerId],
|
||||||
hostname: string,
|
hostname: string,
|
||||||
address: MultiAddress,
|
addrs: MultiAddress,
|
||||||
dir = Direction.Out,
|
dir = Direction.Out,
|
||||||
): Future[Muxer] {.async.} =
|
): Future[Muxer] {.async: (raises: [CancelledError]).} =
|
||||||
for transport in self.transports: # for each transport
|
for transport in self.transports: # for each transport
|
||||||
if transport.handles(address): # check if it can dial it
|
if transport.handles(addrs): # check if it can dial it
|
||||||
trace "Dialing address", address, peerId = peerId.get(default(PeerId)), hostname
|
trace "Dialing address", addrs, peerId = peerId.get(default(PeerId)), hostname
|
||||||
let dialed =
|
let dialed =
|
||||||
try:
|
try:
|
||||||
libp2p_total_dial_attempts.inc()
|
libp2p_total_dial_attempts.inc()
|
||||||
await transport.dial(hostname, address, peerId)
|
await transport.dial(hostname, addrs, peerId)
|
||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
trace "Dialing canceled",
|
trace "Dialing canceled",
|
||||||
description = exc.msg, peerId = peerId.get(default(PeerId))
|
description = exc.msg, peerId = peerId.get(default(PeerId))
|
||||||
@@ -105,40 +101,54 @@ proc dialAndUpgrade(
|
|||||||
|
|
||||||
proc expandDnsAddr(
|
proc expandDnsAddr(
|
||||||
self: Dialer, peerId: Opt[PeerId], address: MultiAddress
|
self: Dialer, peerId: Opt[PeerId], address: MultiAddress
|
||||||
): Future[seq[(MultiAddress, Opt[PeerId])]] {.async.} =
|
): Future[seq[(MultiAddress, Opt[PeerId])]] {.
|
||||||
if not DNSADDR.matchPartial(address):
|
async: (raises: [CancelledError, MaError, TransportAddressError, LPError])
|
||||||
|
.} =
|
||||||
|
if not DNS.matchPartial(address):
|
||||||
return @[(address, peerId)]
|
return @[(address, peerId)]
|
||||||
if isNil(self.nameResolver):
|
if isNil(self.nameResolver):
|
||||||
info "Can't resolve DNSADDR without NameResolver", ma = address
|
info "Can't resolve DNSADDR without NameResolver", ma = address
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
|
trace "Start trying to resolve addresses"
|
||||||
let
|
let
|
||||||
toResolve =
|
toResolve =
|
||||||
if peerId.isSome:
|
if peerId.isSome:
|
||||||
address & MultiAddress.init(multiCodec("p2p"), peerId.tryGet()).tryGet()
|
try:
|
||||||
|
address & MultiAddress.init(multiCodec("p2p"), peerId.tryGet()).tryGet()
|
||||||
|
except ResultError[void]:
|
||||||
|
raiseAssert "checked with if"
|
||||||
else:
|
else:
|
||||||
address
|
address
|
||||||
resolved = await self.nameResolver.resolveDnsAddr(toResolve)
|
resolved = await self.nameResolver.resolveDnsAddr(toResolve)
|
||||||
|
|
||||||
|
debug "resolved addresses",
|
||||||
|
originalAddresses = toResolve, resolvedAddresses = resolved
|
||||||
|
|
||||||
for resolvedAddress in resolved:
|
for resolvedAddress in resolved:
|
||||||
let lastPart = resolvedAddress[^1].tryGet()
|
let lastPart = resolvedAddress[^1].tryGet()
|
||||||
if lastPart.protoCode == Result[MultiCodec, string].ok(multiCodec("p2p")):
|
if lastPart.protoCode == Result[MultiCodec, string].ok(multiCodec("p2p")):
|
||||||
let
|
var peerIdBytes: seq[byte]
|
||||||
|
try:
|
||||||
peerIdBytes = lastPart.protoArgument().tryGet()
|
peerIdBytes = lastPart.protoArgument().tryGet()
|
||||||
addrPeerId = PeerId.init(peerIdBytes).tryGet()
|
except ResultError[string] as e:
|
||||||
|
raiseAssert "expandDnsAddr failed in expandDnsAddr protoArgument: " & e.msg
|
||||||
|
|
||||||
|
let addrPeerId = PeerId.init(peerIdBytes).tryGet()
|
||||||
result.add((resolvedAddress[0 ..^ 2].tryGet(), Opt.some(addrPeerId)))
|
result.add((resolvedAddress[0 ..^ 2].tryGet(), Opt.some(addrPeerId)))
|
||||||
else:
|
else:
|
||||||
result.add((resolvedAddress, peerId))
|
result.add((resolvedAddress, peerId))
|
||||||
|
|
||||||
proc dialAndUpgrade(
|
method dialAndUpgrade*(
|
||||||
self: Dialer, peerId: Opt[PeerId], addrs: seq[MultiAddress], dir = Direction.Out
|
self: Dialer, peerId: Opt[PeerId], addrs: seq[MultiAddress], dir = Direction.Out
|
||||||
): Future[Muxer] {.async.} =
|
): Future[Muxer] {.
|
||||||
|
async: (raises: [CancelledError, MaError, TransportAddressError, LPError])
|
||||||
|
.} =
|
||||||
debug "Dialing peer", peerId = peerId.get(default(PeerId)), addrs
|
debug "Dialing peer", peerId = peerId.get(default(PeerId)), addrs
|
||||||
|
|
||||||
for rawAddress in addrs:
|
for rawAddress in addrs:
|
||||||
# resolve potential dnsaddr
|
# resolve potential dnsaddr
|
||||||
let addresses = await self.expandDnsAddr(peerId, rawAddress)
|
let addresses = await self.expandDnsAddr(peerId, rawAddress)
|
||||||
|
|
||||||
for (expandedAddress, addrPeerId) in addresses:
|
for (expandedAddress, addrPeerId) in addresses:
|
||||||
# DNS resolution
|
# DNS resolution
|
||||||
let
|
let
|
||||||
@@ -149,6 +159,11 @@ proc dialAndUpgrade(
|
|||||||
else:
|
else:
|
||||||
await self.nameResolver.resolveMAddress(expandedAddress)
|
await self.nameResolver.resolveMAddress(expandedAddress)
|
||||||
|
|
||||||
|
debug "Expanded address and hostname",
|
||||||
|
expandedAddress = expandedAddress,
|
||||||
|
hostname = hostname,
|
||||||
|
resolvedAddresses = resolvedAddresses
|
||||||
|
|
||||||
for resolvedAddress in resolvedAddresses:
|
for resolvedAddress in resolvedAddresses:
|
||||||
result = await self.dialAndUpgrade(addrPeerId, hostname, resolvedAddress, dir)
|
result = await self.dialAndUpgrade(addrPeerId, hostname, resolvedAddress, dir)
|
||||||
if not isNil(result):
|
if not isNil(result):
|
||||||
@@ -169,47 +184,69 @@ proc internalConnect(
|
|||||||
forceDial: bool,
|
forceDial: bool,
|
||||||
reuseConnection = true,
|
reuseConnection = true,
|
||||||
dir = Direction.Out,
|
dir = Direction.Out,
|
||||||
): Future[Muxer] {.async.} =
|
): Future[Muxer] {.async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
if Opt.some(self.localPeerId) == peerId:
|
if Opt.some(self.localPeerId) == peerId:
|
||||||
raise newException(CatchableError, "can't dial self!")
|
raise newException(DialFailedError, "internalConnect can't dial self!")
|
||||||
|
|
||||||
# Ensure there's only one in-flight attempt per peer
|
# Ensure there's only one in-flight attempt per peer
|
||||||
let lock = self.dialLock.mgetOrPut(peerId.get(default(PeerId)), newAsyncLock())
|
let lock = self.dialLock.mgetOrPut(peerId.get(default(PeerId)), newAsyncLock())
|
||||||
try:
|
await lock.acquire()
|
||||||
await lock.acquire()
|
defer:
|
||||||
|
|
||||||
if reuseConnection:
|
|
||||||
peerId.withValue(peerId):
|
|
||||||
self.tryReusingConnection(peerId).withValue(mux):
|
|
||||||
return mux
|
|
||||||
|
|
||||||
let slot = self.connManager.getOutgoingSlot(forceDial)
|
|
||||||
let muxed =
|
|
||||||
try:
|
|
||||||
await self.dialAndUpgrade(peerId, addrs, dir)
|
|
||||||
except CatchableError as exc:
|
|
||||||
slot.release()
|
|
||||||
raise exc
|
|
||||||
slot.trackMuxer(muxed)
|
|
||||||
if isNil(muxed): # None of the addresses connected
|
|
||||||
raise newException(DialFailedError, "Unable to establish outgoing link")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.connManager.storeMuxer(muxed)
|
|
||||||
await self.peerStore.identify(muxed)
|
|
||||||
await self.connManager.triggerPeerEvents(
|
|
||||||
muxed.connection.peerId,
|
|
||||||
PeerEvent(kind: PeerEventKind.Identified, initiator: true),
|
|
||||||
)
|
|
||||||
except CatchableError as exc:
|
|
||||||
trace "Failed to finish outgoung upgrade", description = exc.msg
|
|
||||||
await muxed.close()
|
|
||||||
raise exc
|
|
||||||
|
|
||||||
return muxed
|
|
||||||
finally:
|
|
||||||
if lock.locked():
|
|
||||||
lock.release()
|
lock.release()
|
||||||
|
except AsyncLockError as e:
|
||||||
|
raiseAssert "lock must have been acquired in line above: " & e.msg
|
||||||
|
|
||||||
|
if reuseConnection:
|
||||||
|
peerId.withValue(peerId):
|
||||||
|
self.tryReusingConnection(peerId).withValue(mux):
|
||||||
|
return mux
|
||||||
|
|
||||||
|
let slot =
|
||||||
|
try:
|
||||||
|
self.connManager.getOutgoingSlot(forceDial)
|
||||||
|
except TooManyConnectionsError as exc:
|
||||||
|
raise newException(
|
||||||
|
DialFailedError, "failed getOutgoingSlot in internalConnect: " & exc.msg, exc
|
||||||
|
)
|
||||||
|
|
||||||
|
let muxed =
|
||||||
|
try:
|
||||||
|
await self.dialAndUpgrade(peerId, addrs, dir)
|
||||||
|
except CancelledError as exc:
|
||||||
|
slot.release()
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
slot.release()
|
||||||
|
raise newException(
|
||||||
|
DialFailedError, "failed dialAndUpgrade in internalConnect: " & exc.msg, exc
|
||||||
|
)
|
||||||
|
|
||||||
|
slot.trackMuxer(muxed)
|
||||||
|
if isNil(muxed): # None of the addresses connected
|
||||||
|
raise newException(
|
||||||
|
DialFailedError, "Unable to establish outgoing link in internalConnect"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.connManager.storeMuxer(muxed)
|
||||||
|
await self.peerStore.identify(muxed)
|
||||||
|
await self.connManager.triggerPeerEvents(
|
||||||
|
muxed.connection.peerId,
|
||||||
|
PeerEvent(kind: PeerEventKind.Identified, initiator: true),
|
||||||
|
)
|
||||||
|
return muxed
|
||||||
|
except CancelledError as exc:
|
||||||
|
await muxed.close()
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
trace "Failed to finish outgoing upgrade", description = exc.msg
|
||||||
|
await muxed.close()
|
||||||
|
raise newException(
|
||||||
|
DialFailedError,
|
||||||
|
"Failed to finish outgoing upgrade in internalConnect: " & exc.msg,
|
||||||
|
exc,
|
||||||
|
)
|
||||||
|
|
||||||
method connect*(
|
method connect*(
|
||||||
self: Dialer,
|
self: Dialer,
|
||||||
@@ -218,7 +255,7 @@ method connect*(
|
|||||||
forceDial = false,
|
forceDial = false,
|
||||||
reuseConnection = true,
|
reuseConnection = true,
|
||||||
dir = Direction.Out,
|
dir = Direction.Out,
|
||||||
) {.async.} =
|
) {.async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## connect remote peer without negotiating
|
## connect remote peer without negotiating
|
||||||
## a protocol
|
## a protocol
|
||||||
##
|
##
|
||||||
@@ -231,7 +268,7 @@ method connect*(
|
|||||||
|
|
||||||
method connect*(
|
method connect*(
|
||||||
self: Dialer, address: MultiAddress, allowUnknownPeerId = false
|
self: Dialer, address: MultiAddress, allowUnknownPeerId = false
|
||||||
): Future[PeerId] {.async.} =
|
): Future[PeerId] {.async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## Connects to a peer and retrieve its PeerId
|
## Connects to a peer and retrieve its PeerId
|
||||||
|
|
||||||
parseFullAddress(address).toOpt().withValue(fullAddress):
|
parseFullAddress(address).toOpt().withValue(fullAddress):
|
||||||
@@ -241,26 +278,25 @@ method connect*(
|
|||||||
|
|
||||||
if allowUnknownPeerId == false:
|
if allowUnknownPeerId == false:
|
||||||
raise newException(
|
raise newException(
|
||||||
DialFailedError, "Address without PeerID and unknown peer id disabled!"
|
DialFailedError, "Address without PeerID and unknown peer id disabled in connect"
|
||||||
)
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
(await self.internalConnect(Opt.none(PeerId), @[address], false)).connection.peerId
|
(await self.internalConnect(Opt.none(PeerId), @[address], false)).connection.peerId
|
||||||
|
|
||||||
proc negotiateStream(
|
method negotiateStream*(
|
||||||
self: Dialer, conn: Connection, protos: seq[string]
|
self: Dialer, conn: Connection, protos: seq[string]
|
||||||
): Future[Connection] {.async.} =
|
): Future[Connection] {.async: (raises: [CatchableError]).} =
|
||||||
trace "Negotiating stream", conn, protos
|
trace "Negotiating stream", conn, protos
|
||||||
let selected = await MultistreamSelect.select(conn, protos)
|
let selected = await MultistreamSelect.select(conn, protos)
|
||||||
if not protos.contains(selected):
|
if not protos.contains(selected):
|
||||||
await conn.closeWithEOF()
|
await conn.closeWithEOF()
|
||||||
raise newException(DialFailedError, "Unable to select sub-protocol " & $protos)
|
raise newException(DialFailedError, "Unable to select sub-protocol: " & $protos)
|
||||||
|
|
||||||
return conn
|
return conn
|
||||||
|
|
||||||
method tryDial*(
|
method tryDial*(
|
||||||
self: Dialer, peerId: PeerId, addrs: seq[MultiAddress]
|
self: Dialer, peerId: PeerId, addrs: seq[MultiAddress]
|
||||||
): Future[Opt[MultiAddress]] {.async.} =
|
): Future[Opt[MultiAddress]] {.async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## Create a protocol stream in order to check
|
## Create a protocol stream in order to check
|
||||||
## if a connection is possible.
|
## if a connection is possible.
|
||||||
## Doesn't use the Connection Manager to save it.
|
## Doesn't use the Connection Manager to save it.
|
||||||
@@ -270,27 +306,37 @@ method tryDial*(
|
|||||||
try:
|
try:
|
||||||
let mux = await self.dialAndUpgrade(Opt.some(peerId), addrs)
|
let mux = await self.dialAndUpgrade(Opt.some(peerId), addrs)
|
||||||
if mux.isNil():
|
if mux.isNil():
|
||||||
raise newException(DialFailedError, "No valid multiaddress")
|
raise newException(DialFailedError, "No valid multiaddress in tryDial")
|
||||||
await mux.close()
|
await mux.close()
|
||||||
return mux.connection.observedAddr
|
return mux.connection.observedAddr
|
||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
raise exc
|
raise exc
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
raise newException(DialFailedError, exc.msg)
|
raise newException(DialFailedError, "tryDial failed: " & exc.msg, exc)
|
||||||
|
|
||||||
method dial*(
|
method dial*(
|
||||||
self: Dialer, peerId: PeerId, protos: seq[string]
|
self: Dialer, peerId: PeerId, protos: seq[string]
|
||||||
): Future[Connection] {.async.} =
|
): Future[Connection] {.async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## create a protocol stream over an
|
## create a protocol stream over an
|
||||||
## existing connection
|
## existing connection
|
||||||
##
|
##
|
||||||
|
|
||||||
trace "Dialing (existing)", peerId, protos
|
trace "Dialing (existing)", peerId, protos
|
||||||
let stream = await self.connManager.getStream(peerId)
|
|
||||||
if stream.isNil:
|
|
||||||
raise newException(DialFailedError, "Couldn't get muxed stream")
|
|
||||||
|
|
||||||
return await self.negotiateStream(stream, protos)
|
try:
|
||||||
|
let stream = await self.connManager.getStream(peerId)
|
||||||
|
if stream.isNil:
|
||||||
|
raise newException(
|
||||||
|
DialFailedError,
|
||||||
|
"Couldn't get muxed stream in dial for peer_id: " & shortLog(peerId),
|
||||||
|
)
|
||||||
|
return await self.negotiateStream(stream, protos)
|
||||||
|
except CancelledError as exc:
|
||||||
|
trace "Dial canceled", description = exc.msg
|
||||||
|
raise exc
|
||||||
|
except CatchableError as exc:
|
||||||
|
trace "Error dialing", description = exc.msg
|
||||||
|
raise newException(DialFailedError, "failed dial existing: " & exc.msg)
|
||||||
|
|
||||||
method dial*(
|
method dial*(
|
||||||
self: Dialer,
|
self: Dialer,
|
||||||
@@ -298,7 +344,7 @@ method dial*(
|
|||||||
addrs: seq[MultiAddress],
|
addrs: seq[MultiAddress],
|
||||||
protos: seq[string],
|
protos: seq[string],
|
||||||
forceDial = false,
|
forceDial = false,
|
||||||
): Future[Connection] {.async.} =
|
): Future[Connection] {.async: (raises: [DialFailedError, CancelledError]).} =
|
||||||
## create a protocol stream and establish
|
## create a protocol stream and establish
|
||||||
## a connection if one doesn't exist already
|
## a connection if one doesn't exist already
|
||||||
##
|
##
|
||||||
@@ -307,7 +353,7 @@ method dial*(
|
|||||||
conn: Muxer
|
conn: Muxer
|
||||||
stream: Connection
|
stream: Connection
|
||||||
|
|
||||||
proc cleanup() {.async.} =
|
proc cleanup() {.async: (raises: []).} =
|
||||||
if not (isNil(stream)):
|
if not (isNil(stream)):
|
||||||
await stream.closeWithEOF()
|
await stream.closeWithEOF()
|
||||||
|
|
||||||
@@ -321,17 +367,20 @@ method dial*(
|
|||||||
stream = await self.connManager.getStream(conn)
|
stream = await self.connManager.getStream(conn)
|
||||||
|
|
||||||
if isNil(stream):
|
if isNil(stream):
|
||||||
raise newException(DialFailedError, "Couldn't get muxed stream")
|
raise newException(
|
||||||
|
DialFailedError,
|
||||||
|
"Couldn't get muxed stream in new dial for remote_peer_id: " & shortLog(peerId),
|
||||||
|
)
|
||||||
|
|
||||||
return await self.negotiateStream(stream, protos)
|
return await self.negotiateStream(stream, protos)
|
||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
trace "Dial canceled", conn
|
trace "Dial canceled", conn, description = exc.msg
|
||||||
await cleanup()
|
await cleanup()
|
||||||
raise exc
|
raise exc
|
||||||
except CatchableError as exc:
|
except CatchableError as exc:
|
||||||
debug "Error dialing", conn, description = exc.msg
|
debug "Error dialing", conn, description = exc.msg
|
||||||
await cleanup()
|
await cleanup()
|
||||||
raise exc
|
raise newException(DialFailedError, "failed new dial: " & exc.msg, exc)
|
||||||
|
|
||||||
method addTransport*(self: Dialer, t: Transport) =
|
method addTransport*(self: Dialer, t: Transport) =
|
||||||
self.transports &= t
|
self.transports &= t
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import std/sequtils
|
import std/sequtils
|
||||||
import chronos, chronicles, stew/results
|
import chronos, chronicles, results
|
||||||
import ../errors
|
import ../errors
|
||||||
|
|
||||||
type
|
type
|
||||||
@@ -38,8 +38,7 @@ proc add*[T](pa: var PeerAttributes, value: T) =
|
|||||||
Attribute[T](
|
Attribute[T](
|
||||||
value: value,
|
value: value,
|
||||||
comparator: proc(f: BaseAttr, c: BaseAttr): bool =
|
comparator: proc(f: BaseAttr, c: BaseAttr): bool =
|
||||||
f.ofType(T) and c.ofType(T) and f.to(T) == c.to(T)
|
f.ofType(T) and c.ofType(T) and f.to(T) == c.to(T),
|
||||||
,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,7 +59,7 @@ proc `{}`*[T](pa: PeerAttributes, t: typedesc[T]): Opt[T] =
|
|||||||
|
|
||||||
proc `[]`*[T](pa: PeerAttributes, t: typedesc[T]): T {.raises: [KeyError].} =
|
proc `[]`*[T](pa: PeerAttributes, t: typedesc[T]): T {.raises: [KeyError].} =
|
||||||
pa{T}.valueOr:
|
pa{T}.valueOr:
|
||||||
raise newException(KeyError, "Attritute not found")
|
raise newException(KeyError, "Attribute not found")
|
||||||
|
|
||||||
proc match*(pa, candidate: PeerAttributes): bool =
|
proc match*(pa, candidate: PeerAttributes): bool =
|
||||||
for f in pa.attributes:
|
for f in pa.attributes:
|
||||||
@@ -80,16 +79,21 @@ type
|
|||||||
advertisementUpdated*: AsyncEvent
|
advertisementUpdated*: AsyncEvent
|
||||||
advertiseLoop*: Future[void]
|
advertiseLoop*: Future[void]
|
||||||
|
|
||||||
method request*(self: DiscoveryInterface, pa: PeerAttributes) {.async, base.} =
|
|
||||||
doAssert(false, "Not implemented!")
|
|
||||||
|
|
||||||
method advertise*(self: DiscoveryInterface) {.async, base.} =
|
|
||||||
doAssert(false, "Not implemented!")
|
|
||||||
|
|
||||||
type
|
|
||||||
DiscoveryError* = object of LPError
|
DiscoveryError* = object of LPError
|
||||||
DiscoveryFinished* = object of LPError
|
DiscoveryFinished* = object of LPError
|
||||||
|
AdvertiseError* = object of DiscoveryError
|
||||||
|
|
||||||
|
method request*(
|
||||||
|
self: DiscoveryInterface, pa: PeerAttributes
|
||||||
|
) {.base, async: (raises: [DiscoveryError, CancelledError]).} =
|
||||||
|
doAssert(false, "[DiscoveryInterface.request] abstract method not implemented!")
|
||||||
|
|
||||||
|
method advertise*(
|
||||||
|
self: DiscoveryInterface
|
||||||
|
) {.base, async: (raises: [CancelledError, AdvertiseError]).} =
|
||||||
|
doAssert(false, "[DiscoveryInterface.advertise] abstract method not implemented!")
|
||||||
|
|
||||||
|
type
|
||||||
DiscoveryQuery* = ref object
|
DiscoveryQuery* = ref object
|
||||||
attr: PeerAttributes
|
attr: PeerAttributes
|
||||||
peers: AsyncQueue[PeerAttributes]
|
peers: AsyncQueue[PeerAttributes]
|
||||||
@@ -109,7 +113,7 @@ proc add*(dm: DiscoveryManager, di: DiscoveryInterface) =
|
|||||||
try:
|
try:
|
||||||
query.peers.putNoWait(pa)
|
query.peers.putNoWait(pa)
|
||||||
except AsyncQueueFullError as exc:
|
except AsyncQueueFullError as exc:
|
||||||
debug "Cannot push discovered peer to queue"
|
debug "Cannot push discovered peer to queue", description = exc.msg
|
||||||
|
|
||||||
proc request*(dm: DiscoveryManager, pa: PeerAttributes): DiscoveryQuery =
|
proc request*(dm: DiscoveryManager, pa: PeerAttributes): DiscoveryQuery =
|
||||||
var query = DiscoveryQuery(attr: pa, peers: newAsyncQueue[PeerAttributes]())
|
var query = DiscoveryQuery(attr: pa, peers: newAsyncQueue[PeerAttributes]())
|
||||||
@@ -138,7 +142,9 @@ template forEach*(query: DiscoveryQuery, code: untyped) =
|
|||||||
## peer attritubtes are available through the variable
|
## peer attritubtes are available through the variable
|
||||||
## `peer`
|
## `peer`
|
||||||
|
|
||||||
proc forEachInternal(q: DiscoveryQuery) {.async.} =
|
proc forEachInternal(
|
||||||
|
q: DiscoveryQuery
|
||||||
|
) {.async: (raises: [CancelledError, DiscoveryError]).} =
|
||||||
while true:
|
while true:
|
||||||
let peer {.inject.} =
|
let peer {.inject.} =
|
||||||
try:
|
try:
|
||||||
@@ -153,7 +159,7 @@ proc stop*(query: DiscoveryQuery) =
|
|||||||
query.finished = true
|
query.finished = true
|
||||||
for r in query.futs:
|
for r in query.futs:
|
||||||
if not r.finished():
|
if not r.finished():
|
||||||
r.cancel()
|
r.cancelSoon()
|
||||||
|
|
||||||
proc stop*(dm: DiscoveryManager) =
|
proc stop*(dm: DiscoveryManager) =
|
||||||
for q in dm.queries:
|
for q in dm.queries:
|
||||||
@@ -161,15 +167,19 @@ proc stop*(dm: DiscoveryManager) =
|
|||||||
for i in dm.interfaces:
|
for i in dm.interfaces:
|
||||||
if isNil(i.advertiseLoop):
|
if isNil(i.advertiseLoop):
|
||||||
continue
|
continue
|
||||||
i.advertiseLoop.cancel()
|
i.advertiseLoop.cancelSoon()
|
||||||
|
|
||||||
proc getPeer*(query: DiscoveryQuery): Future[PeerAttributes] {.async.} =
|
proc getPeer*(
|
||||||
|
query: DiscoveryQuery
|
||||||
|
): Future[PeerAttributes] {.
|
||||||
|
async: (raises: [CancelledError, DiscoveryError, DiscoveryFinished])
|
||||||
|
.} =
|
||||||
let getter = query.peers.popFirst()
|
let getter = query.peers.popFirst()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await getter or allFinished(query.futs)
|
await getter or allFinished(query.futs)
|
||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
getter.cancel()
|
getter.cancelSoon()
|
||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
if not finished(getter):
|
if not finished(getter):
|
||||||
|
|||||||
@@ -23,15 +23,17 @@ type
|
|||||||
|
|
||||||
proc `==`*(a, b: RdvNamespace): bool {.borrow.}
|
proc `==`*(a, b: RdvNamespace): bool {.borrow.}
|
||||||
|
|
||||||
method request*(self: RendezVousInterface, pa: PeerAttributes) {.async.} =
|
method request*(
|
||||||
var namespace = ""
|
self: RendezVousInterface, pa: PeerAttributes
|
||||||
|
) {.async: (raises: [DiscoveryError, CancelledError]).} =
|
||||||
|
var namespace = Opt.none(string)
|
||||||
for attr in pa:
|
for attr in pa:
|
||||||
if attr.ofType(RdvNamespace):
|
if attr.ofType(RdvNamespace):
|
||||||
namespace = string attr.to(RdvNamespace)
|
namespace = Opt.some(string attr.to(RdvNamespace))
|
||||||
elif attr.ofType(DiscoveryService):
|
elif attr.ofType(DiscoveryService):
|
||||||
namespace = string attr.to(DiscoveryService)
|
namespace = Opt.some(string attr.to(DiscoveryService))
|
||||||
elif attr.ofType(PeerId):
|
elif attr.ofType(PeerId):
|
||||||
namespace = $attr.to(PeerId)
|
namespace = Opt.some($attr.to(PeerId))
|
||||||
else:
|
else:
|
||||||
# unhandled type
|
# unhandled type
|
||||||
return
|
return
|
||||||
@@ -42,13 +44,15 @@ method request*(self: RendezVousInterface, pa: PeerAttributes) {.async.} =
|
|||||||
for address in pr.addresses:
|
for address in pr.addresses:
|
||||||
peer.add(address.address)
|
peer.add(address.address)
|
||||||
|
|
||||||
peer.add(DiscoveryService(namespace))
|
peer.add(DiscoveryService(namespace.get()))
|
||||||
peer.add(RdvNamespace(namespace))
|
peer.add(RdvNamespace(namespace.get()))
|
||||||
self.onPeerFound(peer)
|
self.onPeerFound(peer)
|
||||||
|
|
||||||
await sleepAsync(self.timeToRequest)
|
await sleepAsync(self.timeToRequest)
|
||||||
|
|
||||||
method advertise*(self: RendezVousInterface) {.async.} =
|
method advertise*(
|
||||||
|
self: RendezVousInterface
|
||||||
|
) {.async: (raises: [CancelledError, AdvertiseError]).} =
|
||||||
while true:
|
while true:
|
||||||
var toAdvertise: seq[string]
|
var toAdvertise: seq[string]
|
||||||
for attr in self.toAdvertise:
|
for attr in self.toAdvertise:
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ macro checkFutures*[F](futs: seq[F], exclude: untyped = []): untyped =
|
|||||||
quote:
|
quote:
|
||||||
for res in `futs`:
|
for res in `futs`:
|
||||||
if res.failed:
|
if res.failed:
|
||||||
let exc = res.readError()
|
let exc = res.error
|
||||||
# We still don't abort but warn
|
# We still don't abort but warn
|
||||||
debug "A future has failed, enable trace logging for details",
|
debug "A future has failed, enable trace logging for details",
|
||||||
error = exc.name
|
error = exc.name
|
||||||
@@ -37,7 +37,7 @@ macro checkFutures*[F](futs: seq[F], exclude: untyped = []): untyped =
|
|||||||
for res in `futs`:
|
for res in `futs`:
|
||||||
block check:
|
block check:
|
||||||
if res.failed:
|
if res.failed:
|
||||||
let exc = res.readError()
|
let exc = res.error
|
||||||
for i in 0 ..< `nexclude`:
|
for i in 0 ..< `nexclude`:
|
||||||
if exc of `exclude`[i]:
|
if exc of `exclude`[i]:
|
||||||
trace "A future has failed", error = exc.name, description = exc.msg
|
trace "A future has failed", error = exc.name, description = exc.msg
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ import
|
|||||||
errors,
|
errors,
|
||||||
utility
|
utility
|
||||||
import stew/[base58, base32, endians2]
|
import stew/[base58, base32, endians2]
|
||||||
export results, minprotobuf, vbuffer, errors, utility
|
export results, vbuffer, errors, utility
|
||||||
|
import ./utils/sequninit
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "libp2p multiaddress"
|
topics = "libp2p multiaddress"
|
||||||
@@ -171,6 +172,18 @@ proc ip6zoneVB(vb: var VBuffer): bool =
|
|||||||
## IPv6 validateBuffer() implementation.
|
## IPv6 validateBuffer() implementation.
|
||||||
pathValidateBufferNoSlash(vb)
|
pathValidateBufferNoSlash(vb)
|
||||||
|
|
||||||
|
proc memoryStB(s: string, vb: var VBuffer): bool =
|
||||||
|
## Memory stringToBuffer() implementation.
|
||||||
|
pathStringToBuffer(s, vb)
|
||||||
|
|
||||||
|
proc memoryBtS(vb: var VBuffer, s: var string): bool =
|
||||||
|
## Memory bufferToString() implementation.
|
||||||
|
pathBufferToString(vb, s)
|
||||||
|
|
||||||
|
proc memoryVB(vb: var VBuffer): bool =
|
||||||
|
## Memory validateBuffer() implementation.
|
||||||
|
pathValidateBuffer(vb)
|
||||||
|
|
||||||
proc portStB(s: string, vb: var VBuffer): bool =
|
proc portStB(s: string, vb: var VBuffer): bool =
|
||||||
## Port number stringToBuffer() implementation.
|
## Port number stringToBuffer() implementation.
|
||||||
var port: array[2, byte]
|
var port: array[2, byte]
|
||||||
@@ -211,7 +224,7 @@ proc p2pStB(s: string, vb: var VBuffer): bool =
|
|||||||
|
|
||||||
proc p2pBtS(vb: var VBuffer, s: var string): bool =
|
proc p2pBtS(vb: var VBuffer, s: var string): bool =
|
||||||
## P2P address bufferToString() implementation.
|
## P2P address bufferToString() implementation.
|
||||||
var address = newSeq[byte]()
|
var address = newSeqUninit[byte](0)
|
||||||
if vb.readSeq(address) > 0:
|
if vb.readSeq(address) > 0:
|
||||||
var mh: MultiHash
|
var mh: MultiHash
|
||||||
if MultiHash.decode(address, mh).isOk:
|
if MultiHash.decode(address, mh).isOk:
|
||||||
@@ -220,7 +233,7 @@ proc p2pBtS(vb: var VBuffer, s: var string): bool =
|
|||||||
|
|
||||||
proc p2pVB(vb: var VBuffer): bool =
|
proc p2pVB(vb: var VBuffer): bool =
|
||||||
## P2P address validateBuffer() implementation.
|
## P2P address validateBuffer() implementation.
|
||||||
var address = newSeq[byte]()
|
var address = newSeqUninit[byte](0)
|
||||||
if vb.readSeq(address) > 0:
|
if vb.readSeq(address) > 0:
|
||||||
var mh: MultiHash
|
var mh: MultiHash
|
||||||
if MultiHash.decode(address, mh).isOk:
|
if MultiHash.decode(address, mh).isOk:
|
||||||
@@ -355,6 +368,10 @@ const
|
|||||||
)
|
)
|
||||||
TranscoderDNS* =
|
TranscoderDNS* =
|
||||||
Transcoder(stringToBuffer: dnsStB, bufferToString: dnsBtS, validateBuffer: dnsVB)
|
Transcoder(stringToBuffer: dnsStB, bufferToString: dnsBtS, validateBuffer: dnsVB)
|
||||||
|
TranscoderMemory* = Transcoder(
|
||||||
|
stringToBuffer: memoryStB, bufferToString: memoryBtS, validateBuffer: memoryVB
|
||||||
|
)
|
||||||
|
|
||||||
ProtocolsList = [
|
ProtocolsList = [
|
||||||
MAProtocol(mcodec: multiCodec("ip4"), kind: Fixed, size: 4, coder: TranscoderIP4),
|
MAProtocol(mcodec: multiCodec("ip4"), kind: Fixed, size: 4, coder: TranscoderIP4),
|
||||||
MAProtocol(mcodec: multiCodec("tcp"), kind: Fixed, size: 2, coder: TranscoderPort),
|
MAProtocol(mcodec: multiCodec("tcp"), kind: Fixed, size: 2, coder: TranscoderPort),
|
||||||
@@ -393,6 +410,9 @@ const
|
|||||||
MAProtocol(mcodec: multiCodec("p2p-websocket-star"), kind: Marker, size: 0),
|
MAProtocol(mcodec: multiCodec("p2p-websocket-star"), kind: Marker, size: 0),
|
||||||
MAProtocol(mcodec: multiCodec("p2p-webrtc-star"), kind: Marker, size: 0),
|
MAProtocol(mcodec: multiCodec("p2p-webrtc-star"), kind: Marker, size: 0),
|
||||||
MAProtocol(mcodec: multiCodec("p2p-webrtc-direct"), kind: Marker, size: 0),
|
MAProtocol(mcodec: multiCodec("p2p-webrtc-direct"), kind: Marker, size: 0),
|
||||||
|
MAProtocol(
|
||||||
|
mcodec: multiCodec("memory"), kind: Path, size: 0, coder: TranscoderMemory
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
DNSANY* = mapEq("dns")
|
DNSANY* = mapEq("dns")
|
||||||
@@ -453,6 +473,8 @@ const
|
|||||||
|
|
||||||
CircuitRelay* = mapEq("p2p-circuit")
|
CircuitRelay* = mapEq("p2p-circuit")
|
||||||
|
|
||||||
|
Memory* = mapEq("memory")
|
||||||
|
|
||||||
proc initMultiAddressCodeTable(): Table[MultiCodec, MAProtocol] {.compileTime.} =
|
proc initMultiAddressCodeTable(): Table[MultiCodec, MAProtocol] {.compileTime.} =
|
||||||
for item in ProtocolsList:
|
for item in ProtocolsList:
|
||||||
result[item.mcodec] = item
|
result[item.mcodec] = item
|
||||||
@@ -534,7 +556,7 @@ proc protoAddress*(ma: MultiAddress): MaResult[seq[byte]] =
|
|||||||
##
|
##
|
||||||
## If current MultiAddress do not have argument value, then result array will
|
## If current MultiAddress do not have argument value, then result array will
|
||||||
## be empty.
|
## be empty.
|
||||||
var buffer = newSeq[byte](len(ma.data.buffer))
|
var buffer = newSeqUninit[byte](len(ma.data.buffer))
|
||||||
let res = ?protoArgument(ma, buffer)
|
let res = ?protoArgument(ma, buffer)
|
||||||
buffer.setLen(res)
|
buffer.setLen(res)
|
||||||
ok(buffer)
|
ok(buffer)
|
||||||
@@ -548,7 +570,7 @@ proc protoArgument*(ma: MultiAddress): MaResult[seq[byte]] =
|
|||||||
|
|
||||||
proc getPart(ma: MultiAddress, index: int): MaResult[MultiAddress] =
|
proc getPart(ma: MultiAddress, index: int): MaResult[MultiAddress] =
|
||||||
var header: uint64
|
var header: uint64
|
||||||
var data = newSeq[byte]()
|
var data = newSeqUninit[byte](0)
|
||||||
var offset = 0
|
var offset = 0
|
||||||
var vb = ma
|
var vb = ma
|
||||||
var res: MultiAddress
|
var res: MultiAddress
|
||||||
@@ -622,7 +644,7 @@ proc `[]`*(ma: MultiAddress, slice: HSlice): MaResult[MultiAddress] {.inline.} =
|
|||||||
iterator items*(ma: MultiAddress): MaResult[MultiAddress] =
|
iterator items*(ma: MultiAddress): MaResult[MultiAddress] =
|
||||||
## Iterates over all addresses inside of MultiAddress ``ma``.
|
## Iterates over all addresses inside of MultiAddress ``ma``.
|
||||||
var header: uint64
|
var header: uint64
|
||||||
var data = newSeq[byte]()
|
var data = newSeqUninit[byte](0)
|
||||||
var vb = ma
|
var vb = ma
|
||||||
while true:
|
while true:
|
||||||
if vb.data.isEmpty():
|
if vb.data.isEmpty():
|
||||||
@@ -821,6 +843,14 @@ proc init*(
|
|||||||
res.data.finish()
|
res.data.finish()
|
||||||
ok(res)
|
ok(res)
|
||||||
|
|
||||||
|
proc getPart*(ma: MultiAddress, codec: MultiCodec): MaResult[MultiAddress] =
|
||||||
|
## Returns the first multiaddress in ``value`` with codec ``codec``
|
||||||
|
for part in ma:
|
||||||
|
let part = ?part
|
||||||
|
if codec == ?part.protoCode:
|
||||||
|
return ok(part)
|
||||||
|
err("no such codec in multiaddress")
|
||||||
|
|
||||||
proc getProtocol(name: string): MAProtocol {.inline.} =
|
proc getProtocol(name: string): MAProtocol {.inline.} =
|
||||||
let mc = MultiCodec.codec(name)
|
let mc = MultiCodec.codec(name)
|
||||||
if mc != InvalidMultiCodec:
|
if mc != InvalidMultiCodec:
|
||||||
@@ -1097,3 +1127,32 @@ proc getRepeatedField*(
|
|||||||
err(ProtoError.IncorrectBlob)
|
err(ProtoError.IncorrectBlob)
|
||||||
else:
|
else:
|
||||||
ok(true)
|
ok(true)
|
||||||
|
|
||||||
|
proc areAddrsConsistent*(a, b: MultiAddress): bool =
|
||||||
|
## Checks if two multiaddresses have the same protocol stack.
|
||||||
|
let protosA = a.protocols().get()
|
||||||
|
let protosB = b.protocols().get()
|
||||||
|
if protosA.len != protosB.len:
|
||||||
|
return false
|
||||||
|
|
||||||
|
for idx in 0 ..< protosA.len:
|
||||||
|
let protoA = protosA[idx]
|
||||||
|
let protoB = protosB[idx]
|
||||||
|
|
||||||
|
if protoA != protoB:
|
||||||
|
if idx == 0:
|
||||||
|
# allow DNS ↔ IP at the first component
|
||||||
|
if protoB == multiCodec("dns") or protoB == multiCodec("dnsaddr"):
|
||||||
|
if not (protoA == multiCodec("ip4") or protoA == multiCodec("ip6")):
|
||||||
|
return false
|
||||||
|
elif protoB == multiCodec("dns4"):
|
||||||
|
if protoA != multiCodec("ip4"):
|
||||||
|
return false
|
||||||
|
elif protoB == multiCodec("dns6"):
|
||||||
|
if protoA != multiCodec("ip6"):
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
else:
|
||||||
|
return false
|
||||||
|
true
|
||||||
|
|||||||
@@ -16,7 +16,9 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import tables
|
import tables
|
||||||
import stew/[base32, base58, base64, results]
|
import results
|
||||||
|
import stew/[base32, base58, base64]
|
||||||
|
import ./utils/sequninit
|
||||||
|
|
||||||
type
|
type
|
||||||
MultiBaseStatus* {.pure.} = enum
|
MultiBaseStatus* {.pure.} = enum
|
||||||
@@ -532,7 +534,7 @@ proc decode*(
|
|||||||
let empty: seq[byte] = @[]
|
let empty: seq[byte] = @[]
|
||||||
ok(empty) # empty
|
ok(empty) # empty
|
||||||
else:
|
else:
|
||||||
var buffer = newSeq[byte](mb.decl(length - 1))
|
var buffer = newSeqUninit[byte](mb.decl(length - 1))
|
||||||
var outlen = 0
|
var outlen = 0
|
||||||
let res = mb.decr(inbytes.toOpenArray(1, length - 1), buffer, outlen)
|
let res = mb.decr(inbytes.toOpenArray(1, length - 1), buffer, outlen)
|
||||||
if res != MultiBaseStatus.Success:
|
if res != MultiBaseStatus.Success:
|
||||||
|
|||||||
@@ -10,10 +10,11 @@
|
|||||||
## This module implements MultiCodec.
|
## This module implements MultiCodec.
|
||||||
|
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
{.used.}
|
||||||
|
|
||||||
import tables, hashes
|
import tables, hashes
|
||||||
import vbuffer
|
import vbuffer
|
||||||
import stew/results
|
import results
|
||||||
export results
|
export results
|
||||||
|
|
||||||
## List of officially supported codecs can BE found here
|
## List of officially supported codecs can BE found here
|
||||||
@@ -396,6 +397,7 @@ const MultiCodecList = [
|
|||||||
("onion3", 0x01BD),
|
("onion3", 0x01BD),
|
||||||
("p2p-circuit", 0x0122),
|
("p2p-circuit", 0x0122),
|
||||||
("libp2p-peer-record", 0x0301),
|
("libp2p-peer-record", 0x0301),
|
||||||
|
("memory", 0x0309),
|
||||||
("dns", 0x35),
|
("dns", 0x35),
|
||||||
("dns4", 0x36),
|
("dns4", 0x36),
|
||||||
("dns6", 0x37),
|
("dns6", 0x37),
|
||||||
@@ -403,6 +405,7 @@ const MultiCodecList = [
|
|||||||
# IPLD formats
|
# IPLD formats
|
||||||
("dag-pb", 0x70),
|
("dag-pb", 0x70),
|
||||||
("dag-cbor", 0x71),
|
("dag-cbor", 0x71),
|
||||||
|
("libp2p-key", 0x72),
|
||||||
("dag-json", 0x129),
|
("dag-json", 0x129),
|
||||||
("git-raw", 0x78),
|
("git-raw", 0x78),
|
||||||
("eth-block", 0x90),
|
("eth-block", 0x90),
|
||||||
|
|||||||
@@ -22,12 +22,13 @@
|
|||||||
## 2. MURMUR
|
## 2. MURMUR
|
||||||
|
|
||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
{.used.}
|
||||||
|
|
||||||
import tables
|
import tables
|
||||||
import nimcrypto/[sha, sha2, keccak, blake2, hash, utils]
|
import nimcrypto/[sha, sha2, keccak, blake2, hash, utils]
|
||||||
import varint, vbuffer, multicodec, multibase
|
import varint, vbuffer, multicodec, multibase
|
||||||
import stew/base58
|
import stew/base58
|
||||||
import stew/results
|
import results
|
||||||
export results
|
export results
|
||||||
# This is workaround for Nim `import` bug.
|
# This is workaround for Nim `import` bug.
|
||||||
export sha, sha2, keccak, blake2, hash, utils
|
export sha, sha2, keccak, blake2, hash, utils
|
||||||
@@ -566,7 +567,7 @@ proc init*(mhtype: typedesc[MultiHash], data: string): MhResult[MultiHash] {.inl
|
|||||||
proc init58*(mhtype: typedesc[MultiHash], data: string): MultiHash {.inline.} =
|
proc init58*(mhtype: typedesc[MultiHash], data: string): MultiHash {.inline.} =
|
||||||
## Create MultiHash from BASE58 encoded string representation ``data``.
|
## Create MultiHash from BASE58 encoded string representation ``data``.
|
||||||
if MultiHash.decode(Base58.decode(data), result) == -1:
|
if MultiHash.decode(Base58.decode(data), result) == -1:
|
||||||
raise newException(MultihashError, "Incorrect MultiHash binary format")
|
raise newException(MultihashError, "Incorrect MultiHash binary format in init58")
|
||||||
|
|
||||||
proc cmp(a: openArray[byte], b: openArray[byte]): bool {.inline.} =
|
proc cmp(a: openArray[byte], b: openArray[byte]): bool {.inline.} =
|
||||||
if len(a) != len(b):
|
if len(a) != len(b):
|
||||||
|
|||||||
@@ -249,11 +249,7 @@ proc addHandler*[E](
|
|||||||
m.handlers.add(HandlerHolder(protos: @[codec], protocol: protocol, match: matcher))
|
m.handlers.add(HandlerHolder(protos: @[codec], protocol: protocol, match: matcher))
|
||||||
|
|
||||||
proc start*(m: MultistreamSelect) {.async: (raises: [CancelledError]).} =
|
proc start*(m: MultistreamSelect) {.async: (raises: [CancelledError]).} =
|
||||||
# Nim 1.6.18: Using `mapIt` results in a seq of `.Raising([])`
|
let futs = m.handlers.mapIt(it.protocol.start())
|
||||||
# TODO https://github.com/nim-lang/Nim/issues/23445
|
|
||||||
var futs = newSeqOfCap[Future[void].Raising([CancelledError])](m.handlers.len)
|
|
||||||
for it in m.handlers:
|
|
||||||
futs.add it.protocol.start()
|
|
||||||
try:
|
try:
|
||||||
await allFutures(futs)
|
await allFutures(futs)
|
||||||
for fut in futs:
|
for fut in futs:
|
||||||
@@ -273,10 +269,7 @@ proc start*(m: MultistreamSelect) {.async: (raises: [CancelledError]).} =
|
|||||||
raise exc
|
raise exc
|
||||||
|
|
||||||
proc stop*(m: MultistreamSelect) {.async: (raises: []).} =
|
proc stop*(m: MultistreamSelect) {.async: (raises: []).} =
|
||||||
# Nim 1.6.18: Using `mapIt` results in a seq of `.Raising([CancelledError])`
|
let futs = m.handlers.mapIt(it.protocol.stop())
|
||||||
var futs = newSeqOfCap[Future[void].Raising([])](m.handlers.len)
|
|
||||||
for it in m.handlers:
|
|
||||||
futs.add it.protocol.stop()
|
|
||||||
await noCancel allFutures(futs)
|
await noCancel allFutures(futs)
|
||||||
for fut in futs:
|
for fut in futs:
|
||||||
await fut
|
await fut
|
||||||
|
|||||||
@@ -11,8 +11,7 @@
|
|||||||
|
|
||||||
import std/[oids, strformat]
|
import std/[oids, strformat]
|
||||||
import pkg/[chronos, chronicles, metrics]
|
import pkg/[chronos, chronicles, metrics]
|
||||||
import
|
import ./coder, ../muxer, ../../stream/[bufferstream, connection], ../../peerinfo
|
||||||
./coder, ../muxer, ../../stream/[bufferstream, connection, streamseq], ../../peerinfo
|
|
||||||
|
|
||||||
export connection
|
export connection
|
||||||
|
|
||||||
@@ -87,7 +86,7 @@ proc open*(s: LPChannel) {.async: (raises: [CancelledError, LPStreamError]).} =
|
|||||||
raise exc
|
raise exc
|
||||||
except LPStreamError as exc:
|
except LPStreamError as exc:
|
||||||
await s.conn.close()
|
await s.conn.close()
|
||||||
raise exc
|
raise newException(LPStreamError, "Opening LPChannel failed: " & exc.msg, exc)
|
||||||
|
|
||||||
method closed*(s: LPChannel): bool =
|
method closed*(s: LPChannel): bool =
|
||||||
s.closedLocal
|
s.closedLocal
|
||||||
@@ -151,6 +150,10 @@ method close*(s: LPChannel) {.async: (raises: []).} =
|
|||||||
|
|
||||||
trace "Closed channel", s, len = s.len
|
trace "Closed channel", s, len = s.len
|
||||||
|
|
||||||
|
method closeWrite*(s: LPChannel) {.async: (raises: []).} =
|
||||||
|
## For mplex, closeWrite is the same as close - it implements half-close
|
||||||
|
await s.close()
|
||||||
|
|
||||||
method initStream*(s: LPChannel) =
|
method initStream*(s: LPChannel) =
|
||||||
if s.objName.len == 0:
|
if s.objName.len == 0:
|
||||||
s.objName = LPChannelTrackerName
|
s.objName = LPChannelTrackerName
|
||||||
@@ -169,6 +172,7 @@ method readOnce*(
|
|||||||
## channel must not be done from within a callback / read handler of another
|
## channel must not be done from within a callback / read handler of another
|
||||||
## or the reads will lock each other.
|
## or the reads will lock each other.
|
||||||
if s.remoteReset:
|
if s.remoteReset:
|
||||||
|
trace "reset stream in readOnce", s
|
||||||
raise newLPStreamResetError()
|
raise newLPStreamResetError()
|
||||||
if s.localReset:
|
if s.localReset:
|
||||||
raise newLPStreamClosedError()
|
raise newLPStreamClosedError()
|
||||||
@@ -201,6 +205,7 @@ proc prepareWrite(
|
|||||||
# prepareWrite is the slow path of writing a message - see conditions in
|
# prepareWrite is the slow path of writing a message - see conditions in
|
||||||
# write
|
# write
|
||||||
if s.remoteReset:
|
if s.remoteReset:
|
||||||
|
trace "stream is reset when prepareWrite", s
|
||||||
raise newLPStreamResetError()
|
raise newLPStreamResetError()
|
||||||
if s.closedLocal:
|
if s.closedLocal:
|
||||||
raise newLPStreamClosedError()
|
raise newLPStreamClosedError()
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ proc newStreamInternal*(
|
|||||||
|
|
||||||
result.peerId = m.connection.peerId
|
result.peerId = m.connection.peerId
|
||||||
result.observedAddr = m.connection.observedAddr
|
result.observedAddr = m.connection.observedAddr
|
||||||
|
result.localAddr = m.connection.localAddr
|
||||||
result.transportDir = m.connection.transportDir
|
result.transportDir = m.connection.transportDir
|
||||||
when defined(libp2p_agents_metrics):
|
when defined(libp2p_agents_metrics):
|
||||||
result.shortAgent = m.connection.shortAgent
|
result.shortAgent = m.connection.shortAgent
|
||||||
@@ -247,7 +248,7 @@ method close*(m: Mplex) {.async: (raises: []).} =
|
|||||||
|
|
||||||
trace "Closed mplex", m
|
trace "Closed mplex", m
|
||||||
|
|
||||||
method getStreams*(m: Mplex): seq[Connection] =
|
method getStreams*(m: Mplex): seq[Connection] {.gcsafe.} =
|
||||||
for c in m.channels[false].values:
|
for c in m.channels[false].values:
|
||||||
result.add(c)
|
result.add(c)
|
||||||
for c in m.channels[true].values:
|
for c in m.channels[true].values:
|
||||||
|
|||||||
@@ -52,7 +52,11 @@ method newStream*(
|
|||||||
): Future[Connection] {.
|
): Future[Connection] {.
|
||||||
base, async: (raises: [CancelledError, LPStreamError, MuxerError], raw: true)
|
base, async: (raises: [CancelledError, LPStreamError, MuxerError], raw: true)
|
||||||
.} =
|
.} =
|
||||||
raiseAssert("Not implemented!")
|
raiseAssert("[Muxer.newStream] abstract method not implemented!")
|
||||||
|
|
||||||
|
when defined(libp2p_agents_metrics):
|
||||||
|
method setShortAgent*(m: Muxer, shortAgent: string) {.base, gcsafe.} =
|
||||||
|
m.connection.shortAgent = shortAgent
|
||||||
|
|
||||||
method close*(m: Muxer) {.base, async: (raises: []).} =
|
method close*(m: Muxer) {.base, async: (raises: []).} =
|
||||||
if m.connection != nil:
|
if m.connection != nil:
|
||||||
@@ -67,5 +71,5 @@ proc new*(
|
|||||||
let muxerProvider = T(newMuxer: creator, codec: codec)
|
let muxerProvider = T(newMuxer: creator, codec: codec)
|
||||||
muxerProvider
|
muxerProvider
|
||||||
|
|
||||||
method getStreams*(m: Muxer): seq[Connection] {.base.} =
|
method getStreams*(m: Muxer): seq[Connection] {.base, gcsafe.} =
|
||||||
raiseAssert("Not implemented!")
|
raiseAssert("[Muxer.getStreams] abstract method not implemented!")
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
import sequtils, std/[tables]
|
import sequtils, std/[tables]
|
||||||
import chronos, chronicles, metrics, stew/[endians2, byteutils, objects]
|
import chronos, chronicles, metrics, stew/[endians2, byteutils, objects]
|
||||||
import ../muxer, ../../stream/connection
|
import ../muxer, ../../stream/connection
|
||||||
|
import ../../utils/[zeroqueue, sequninit]
|
||||||
|
|
||||||
export muxer
|
export muxer
|
||||||
|
|
||||||
@@ -82,8 +83,7 @@ proc `$`(header: YamuxHeader): string =
|
|||||||
if a != "":
|
if a != "":
|
||||||
a & ", " & $b
|
a & ", " & $b
|
||||||
else:
|
else:
|
||||||
$b
|
$b,
|
||||||
,
|
|
||||||
"",
|
"",
|
||||||
) & "}, " & "streamId: " & $header.streamId & ", " & "length: " & $header.length &
|
) & "}, " & "streamId: " & $header.streamId & ", " & "length: " & $header.length &
|
||||||
"}"
|
"}"
|
||||||
@@ -135,12 +135,11 @@ proc windowUpdate(
|
|||||||
)
|
)
|
||||||
|
|
||||||
type
|
type
|
||||||
ToSend =
|
ToSend = ref object
|
||||||
tuple[
|
data: seq[byte]
|
||||||
data: seq[byte],
|
sent: int
|
||||||
sent: int,
|
fut: Future[void].Raising([CancelledError, LPStreamError])
|
||||||
fut: Future[void].Raising([CancelledError, LPStreamError]),
|
|
||||||
]
|
|
||||||
YamuxChannel* = ref object of Connection
|
YamuxChannel* = ref object of Connection
|
||||||
id: uint32
|
id: uint32
|
||||||
recvWindow: int
|
recvWindow: int
|
||||||
@@ -152,7 +151,7 @@ type
|
|||||||
opened: bool
|
opened: bool
|
||||||
isSending: bool
|
isSending: bool
|
||||||
sendQueue: seq[ToSend]
|
sendQueue: seq[ToSend]
|
||||||
recvQueue: seq[byte]
|
recvQueue: ZeroQueue
|
||||||
isReset: bool
|
isReset: bool
|
||||||
remoteReset: bool
|
remoteReset: bool
|
||||||
closedRemotely: AsyncEvent
|
closedRemotely: AsyncEvent
|
||||||
@@ -176,8 +175,7 @@ proc `$`(channel: YamuxChannel): string =
|
|||||||
if a != "":
|
if a != "":
|
||||||
a & ", " & b
|
a & ", " & b
|
||||||
else:
|
else:
|
||||||
b
|
b,
|
||||||
,
|
|
||||||
"",
|
"",
|
||||||
) & "}"
|
) & "}"
|
||||||
|
|
||||||
@@ -205,6 +203,7 @@ proc remoteClosed(channel: YamuxChannel) {.async: (raises: []).} =
|
|||||||
if not channel.closedRemotely.isSet():
|
if not channel.closedRemotely.isSet():
|
||||||
channel.closedRemotely.fire()
|
channel.closedRemotely.fire()
|
||||||
await channel.actuallyClose()
|
await channel.actuallyClose()
|
||||||
|
channel.isClosedRemotely = true
|
||||||
|
|
||||||
method closeImpl*(channel: YamuxChannel) {.async: (raises: []).} =
|
method closeImpl*(channel: YamuxChannel) {.async: (raises: []).} =
|
||||||
if not channel.closedLocally:
|
if not channel.closedLocally:
|
||||||
@@ -218,6 +217,19 @@ method closeImpl*(channel: YamuxChannel) {.async: (raises: []).} =
|
|||||||
discard
|
discard
|
||||||
await channel.actuallyClose()
|
await channel.actuallyClose()
|
||||||
|
|
||||||
|
method closeWrite*(channel: YamuxChannel) {.async: (raises: []).} =
|
||||||
|
## For yamux, closeWrite is the same as close - it implements half-close
|
||||||
|
await channel.close()
|
||||||
|
|
||||||
|
proc clearQueues(channel: YamuxChannel, error: ref LPStreamEOFError = nil) =
|
||||||
|
for toSend in channel.sendQueue:
|
||||||
|
if error.isNil():
|
||||||
|
toSend.fut.complete()
|
||||||
|
else:
|
||||||
|
toSend.fut.fail(error)
|
||||||
|
channel.sendQueue = @[]
|
||||||
|
channel.recvQueue.clear()
|
||||||
|
|
||||||
proc reset(channel: YamuxChannel, isLocal: bool = false) {.async: (raises: []).} =
|
proc reset(channel: YamuxChannel, isLocal: bool = false) {.async: (raises: []).} =
|
||||||
# If we reset locally, we want to flush up to a maximum of recvWindow
|
# If we reset locally, we want to flush up to a maximum of recvWindow
|
||||||
# bytes. It's because the peer we're connected to can send us data before
|
# bytes. It's because the peer we're connected to can send us data before
|
||||||
@@ -227,10 +239,8 @@ proc reset(channel: YamuxChannel, isLocal: bool = false) {.async: (raises: []).}
|
|||||||
trace "Reset channel"
|
trace "Reset channel"
|
||||||
channel.isReset = true
|
channel.isReset = true
|
||||||
channel.remoteReset = not isLocal
|
channel.remoteReset = not isLocal
|
||||||
for (d, s, fut) in channel.sendQueue:
|
channel.clearQueues(newLPStreamEOFError())
|
||||||
fut.fail(newLPStreamEOFError())
|
|
||||||
channel.sendQueue = @[]
|
|
||||||
channel.recvQueue = @[]
|
|
||||||
channel.sendWindow = 0
|
channel.sendWindow = 0
|
||||||
if not channel.closedLocally:
|
if not channel.closedLocally:
|
||||||
if isLocal and not channel.isSending:
|
if isLocal and not channel.isSending:
|
||||||
@@ -258,7 +268,7 @@ proc updateRecvWindow(
|
|||||||
return
|
return
|
||||||
|
|
||||||
let delta = channel.maxRecvWindow - inWindow
|
let delta = channel.maxRecvWindow - inWindow
|
||||||
channel.recvWindow.inc(delta)
|
channel.recvWindow.inc(delta.int)
|
||||||
await channel.conn.write(YamuxHeader.windowUpdate(channel.id, delta.uint32))
|
await channel.conn.write(YamuxHeader.windowUpdate(channel.id, delta.uint32))
|
||||||
trace "increasing the recvWindow", delta
|
trace "increasing the recvWindow", delta
|
||||||
|
|
||||||
@@ -270,14 +280,18 @@ method readOnce*(
|
|||||||
if channel.isReset:
|
if channel.isReset:
|
||||||
raise
|
raise
|
||||||
if channel.remoteReset:
|
if channel.remoteReset:
|
||||||
|
trace "stream is remote reset when readOnce", channel = $channel
|
||||||
newLPStreamResetError()
|
newLPStreamResetError()
|
||||||
elif channel.closedLocally:
|
elif channel.closedLocally:
|
||||||
|
trace "stream is closed locally when readOnce", channel = $channel
|
||||||
newLPStreamClosedError()
|
newLPStreamClosedError()
|
||||||
else:
|
else:
|
||||||
|
trace "stream is down when readOnce", channel = $channel
|
||||||
newLPStreamConnDownError()
|
newLPStreamConnDownError()
|
||||||
if channel.isEof:
|
if channel.isEof:
|
||||||
|
channel.clearQueues()
|
||||||
raise newLPStreamRemoteClosedError()
|
raise newLPStreamRemoteClosedError()
|
||||||
if channel.recvQueue.len == 0:
|
if channel.recvQueue.isEmpty():
|
||||||
channel.receivedData.clear()
|
channel.receivedData.clear()
|
||||||
let
|
let
|
||||||
closedRemotelyFut = channel.closedRemotely.wait()
|
closedRemotelyFut = channel.closedRemotely.wait()
|
||||||
@@ -288,28 +302,24 @@ method readOnce*(
|
|||||||
if not receivedDataFut.finished():
|
if not receivedDataFut.finished():
|
||||||
await receivedDataFut.cancelAndWait()
|
await receivedDataFut.cancelAndWait()
|
||||||
await closedRemotelyFut or receivedDataFut
|
await closedRemotelyFut or receivedDataFut
|
||||||
if channel.closedRemotely.isSet() and channel.recvQueue.len == 0:
|
if channel.closedRemotely.isSet() and channel.recvQueue.isEmpty():
|
||||||
channel.isEof = true
|
channel.isEof = true
|
||||||
|
channel.clearQueues()
|
||||||
return
|
return
|
||||||
0 # we return 0 to indicate that the channel is closed for reading from now on
|
0 # we return 0 to indicate that the channel is closed for reading from now on
|
||||||
|
|
||||||
let toRead = min(channel.recvQueue.len, nbytes)
|
let consumed = channel.recvQueue.consumeTo(pbytes, nbytes)
|
||||||
|
|
||||||
var p = cast[ptr UncheckedArray[byte]](pbytes)
|
|
||||||
toOpenArray(p, 0, nbytes - 1)[0 ..< toRead] =
|
|
||||||
channel.recvQueue.toOpenArray(0, toRead - 1)
|
|
||||||
channel.recvQueue = channel.recvQueue[toRead ..^ 1]
|
|
||||||
|
|
||||||
# We made some room in the recv buffer let the peer know
|
# We made some room in the recv buffer let the peer know
|
||||||
await channel.updateRecvWindow()
|
await channel.updateRecvWindow()
|
||||||
channel.activity = true
|
channel.activity = true
|
||||||
return toRead
|
return consumed
|
||||||
|
|
||||||
proc gotDataFromRemote(
|
proc gotDataFromRemote(
|
||||||
channel: YamuxChannel, b: seq[byte]
|
channel: YamuxChannel, b: seq[byte]
|
||||||
) {.async: (raises: [CancelledError, LPStreamError]).} =
|
) {.async: (raises: [CancelledError, LPStreamError]).} =
|
||||||
channel.recvWindow -= b.len
|
channel.recvWindow -= b.len
|
||||||
channel.recvQueue = channel.recvQueue.concat(b)
|
channel.recvQueue.push(b)
|
||||||
channel.receivedData.fire()
|
channel.receivedData.fire()
|
||||||
when defined(libp2p_yamux_metrics):
|
when defined(libp2p_yamux_metrics):
|
||||||
libp2p_yamux_recv_queue.observe(channel.recvQueue.len.int64)
|
libp2p_yamux_recv_queue.observe(channel.recvQueue.len.int64)
|
||||||
@@ -318,17 +328,18 @@ proc gotDataFromRemote(
|
|||||||
proc setMaxRecvWindow*(channel: YamuxChannel, maxRecvWindow: int) =
|
proc setMaxRecvWindow*(channel: YamuxChannel, maxRecvWindow: int) =
|
||||||
channel.maxRecvWindow = maxRecvWindow
|
channel.maxRecvWindow = maxRecvWindow
|
||||||
|
|
||||||
proc trySend(
|
proc sendLoop(channel: YamuxChannel) {.async: (raises: []).} =
|
||||||
channel: YamuxChannel
|
|
||||||
) {.async: (raises: [CancelledError, LPStreamError]).} =
|
|
||||||
if channel.isSending:
|
if channel.isSending:
|
||||||
return
|
return
|
||||||
channel.isSending = true
|
channel.isSending = true
|
||||||
defer:
|
defer:
|
||||||
channel.isSending = false
|
channel.isSending = false
|
||||||
|
|
||||||
while channel.sendQueue.len != 0:
|
const NumBytesHeader = 12
|
||||||
channel.sendQueue.keepItIf(not (it.fut.cancelled() and it.sent == 0))
|
|
||||||
|
while channel.sendQueue.len > 0:
|
||||||
|
channel.sendQueue.keepItIf(not it.fut.finished())
|
||||||
|
|
||||||
if channel.sendWindow == 0:
|
if channel.sendWindow == 0:
|
||||||
trace "trying to send while the sendWindow is empty"
|
trace "trying to send while the sendWindow is empty"
|
||||||
if channel.lengthSendQueueWithLimit() > channel.maxSendQueueSize:
|
if channel.lengthSendQueueWithLimit() > channel.maxSendQueueSize:
|
||||||
@@ -340,54 +351,57 @@ proc trySend(
|
|||||||
|
|
||||||
let
|
let
|
||||||
bytesAvailable = channel.lengthSendQueue()
|
bytesAvailable = channel.lengthSendQueue()
|
||||||
toSend = min(channel.sendWindow, bytesAvailable)
|
numBytesToSend = min(channel.sendWindow, bytesAvailable)
|
||||||
var
|
var
|
||||||
sendBuffer = newSeqUninitialized[byte](toSend + 12)
|
sendBuffer = newSeqUninit[byte](NumBytesHeader + numBytesToSend)
|
||||||
header = YamuxHeader.data(channel.id, toSend.uint32)
|
header = YamuxHeader.data(channel.id, numBytesToSend.uint32)
|
||||||
inBuffer = 0
|
inBuffer = 0
|
||||||
|
|
||||||
if toSend >= bytesAvailable and channel.closedLocally:
|
if numBytesToSend >= bytesAvailable and channel.closedLocally:
|
||||||
trace "last buffer we'll sent on this channel", toSend, bytesAvailable
|
trace "last buffer we will send on this channel", numBytesToSend, bytesAvailable
|
||||||
header.flags.incl({Fin})
|
header.flags.incl({Fin})
|
||||||
|
|
||||||
sendBuffer[0 ..< 12] = header.encode()
|
sendBuffer[0 ..< NumBytesHeader] = header.encode()
|
||||||
|
|
||||||
var futures: seq[Future[void].Raising([CancelledError, LPStreamError])]
|
var futures: seq[Future[void].Raising([CancelledError, LPStreamError])]
|
||||||
while inBuffer < toSend:
|
while inBuffer < numBytesToSend:
|
||||||
|
var toSend = channel.sendQueue[0]
|
||||||
# concatenate the different message we try to send into one buffer
|
# concatenate the different message we try to send into one buffer
|
||||||
let (data, sent, fut) = channel.sendQueue[0]
|
let bufferToSend = min(toSend.data.len - toSend.sent, numBytesToSend - inBuffer)
|
||||||
let bufferToSend = min(data.len - sent, toSend - inBuffer)
|
|
||||||
|
|
||||||
sendBuffer.toOpenArray(12, 12 + toSend - 1)[
|
sendBuffer.toOpenArray(NumBytesHeader, NumBytesHeader + numBytesToSend - 1)[
|
||||||
inBuffer ..< (inBuffer + bufferToSend)
|
inBuffer ..< (inBuffer + bufferToSend)
|
||||||
] = channel.sendQueue[0].data.toOpenArray(sent, sent + bufferToSend - 1)
|
] = toSend.data.toOpenArray(toSend.sent, toSend.sent + bufferToSend - 1)
|
||||||
|
|
||||||
channel.sendQueue[0].sent.inc(bufferToSend)
|
channel.sendQueue[0].sent.inc(bufferToSend)
|
||||||
if channel.sendQueue[0].sent >= data.len:
|
|
||||||
|
if toSend.sent >= toSend.data.len:
|
||||||
# if every byte of the message is in the buffer, add the write future to the
|
# if every byte of the message is in the buffer, add the write future to the
|
||||||
# sequence of futures to be completed (or failed) when the buffer is sent
|
# sequence of futures to be completed (or failed) when the buffer is sent
|
||||||
futures.add(fut)
|
futures.add(toSend.fut)
|
||||||
channel.sendQueue.delete(0)
|
channel.sendQueue.delete(0)
|
||||||
|
|
||||||
inBuffer.inc(bufferToSend)
|
inBuffer.inc(bufferToSend)
|
||||||
|
|
||||||
trace "try to send the buffer", h = $header
|
trace "try to send the buffer", h = $header
|
||||||
channel.sendWindow.dec(toSend)
|
|
||||||
try:
|
try:
|
||||||
await channel.conn.write(sendBuffer)
|
await channel.conn.write(sendBuffer)
|
||||||
|
channel.sendWindow.dec(inBuffer)
|
||||||
except CancelledError:
|
except CancelledError:
|
||||||
trace "cancelled sending the buffer"
|
## Just for compiler. This should never happen as sendLoop is started by asyncSpawn.
|
||||||
for fut in futures.items():
|
## Therefore, no one owns that sendLoop's future and no one can cancel it.
|
||||||
fut.cancelSoon()
|
discard
|
||||||
await channel.reset()
|
|
||||||
break
|
|
||||||
except LPStreamError as exc:
|
except LPStreamError as exc:
|
||||||
trace "failed to send the buffer"
|
error "failed to send the buffer", description = exc.msg
|
||||||
let connDown = newLPStreamConnDownError(exc)
|
let connDown = newLPStreamConnDownError(exc)
|
||||||
for fut in futures.items():
|
for fut in futures:
|
||||||
fut.fail(connDown)
|
fut.fail(connDown)
|
||||||
await channel.reset()
|
await channel.reset()
|
||||||
break
|
break
|
||||||
for fut in futures.items():
|
|
||||||
|
for fut in futures:
|
||||||
fut.complete()
|
fut.complete()
|
||||||
|
|
||||||
channel.activity = true
|
channel.activity = true
|
||||||
|
|
||||||
method write*(
|
method write*(
|
||||||
@@ -395,20 +409,29 @@ method write*(
|
|||||||
): Future[void] {.async: (raises: [CancelledError, LPStreamError], raw: true).} =
|
): Future[void] {.async: (raises: [CancelledError, LPStreamError], raw: true).} =
|
||||||
## Write to yamux channel
|
## Write to yamux channel
|
||||||
##
|
##
|
||||||
result = newFuture[void]("Yamux Send")
|
var resFut = newFuture[void]("Yamux Send")
|
||||||
|
|
||||||
if channel.remoteReset:
|
if channel.remoteReset:
|
||||||
result.fail(newLPStreamResetError())
|
trace "stream is reset when write", channel = $channel
|
||||||
return result
|
resFut.fail(newLPStreamResetError())
|
||||||
|
return resFut
|
||||||
|
|
||||||
if channel.closedLocally or channel.isReset:
|
if channel.closedLocally or channel.isReset:
|
||||||
result.fail(newLPStreamClosedError())
|
resFut.fail(newLPStreamClosedError())
|
||||||
return result
|
return resFut
|
||||||
|
|
||||||
if msg.len == 0:
|
if msg.len == 0:
|
||||||
result.complete()
|
resFut.complete()
|
||||||
return result
|
return resFut
|
||||||
channel.sendQueue.add((msg, 0, result))
|
|
||||||
|
channel.sendQueue.add(ToSend(data: msg, sent: 0, fut: resFut))
|
||||||
|
|
||||||
when defined(libp2p_yamux_metrics):
|
when defined(libp2p_yamux_metrics):
|
||||||
libp2p_yamux_send_queue.observe(channel.lengthSendQueue().int64)
|
libp2p_yamux_send_queue.observe(channel.lengthSendQueue().int64)
|
||||||
asyncSpawn channel.trySend()
|
|
||||||
|
asyncSpawn channel.sendLoop()
|
||||||
|
|
||||||
|
return resFut
|
||||||
|
|
||||||
proc open(channel: YamuxChannel) {.async: (raises: [CancelledError, LPStreamError]).} =
|
proc open(channel: YamuxChannel) {.async: (raises: [CancelledError, LPStreamError]).} =
|
||||||
## Open a yamux channel by sending a window update with Syn or Ack flag
|
## Open a yamux channel by sending a window update with Syn or Ack flag
|
||||||
@@ -417,6 +440,8 @@ proc open(channel: YamuxChannel) {.async: (raises: [CancelledError, LPStreamErro
|
|||||||
trace "Try to open channel twice"
|
trace "Try to open channel twice"
|
||||||
return
|
return
|
||||||
channel.opened = true
|
channel.opened = true
|
||||||
|
channel.isReset = false
|
||||||
|
|
||||||
await channel.conn.write(
|
await channel.conn.write(
|
||||||
YamuxHeader.windowUpdate(
|
YamuxHeader.windowUpdate(
|
||||||
channel.id,
|
channel.id,
|
||||||
@@ -490,6 +515,7 @@ proc createStream(
|
|||||||
stream.initStream()
|
stream.initStream()
|
||||||
stream.peerId = m.connection.peerId
|
stream.peerId = m.connection.peerId
|
||||||
stream.observedAddr = m.connection.observedAddr
|
stream.observedAddr = m.connection.observedAddr
|
||||||
|
stream.localAddr = m.connection.localAddr
|
||||||
stream.transportDir = m.connection.transportDir
|
stream.transportDir = m.connection.transportDir
|
||||||
when defined(libp2p_agents_metrics):
|
when defined(libp2p_agents_metrics):
|
||||||
stream.shortAgent = m.connection.shortAgent
|
stream.shortAgent = m.connection.shortAgent
|
||||||
@@ -504,12 +530,19 @@ method close*(m: Yamux) {.async: (raises: []).} =
|
|||||||
if m.isClosed == true:
|
if m.isClosed == true:
|
||||||
trace "Already closed"
|
trace "Already closed"
|
||||||
return
|
return
|
||||||
m.isClosed = true
|
|
||||||
|
|
||||||
trace "Closing yamux"
|
trace "Closing yamux"
|
||||||
let channels = toSeq(m.channels.values())
|
let channels = toSeq(m.channels.values())
|
||||||
for channel in channels:
|
for channel in channels:
|
||||||
await channel.reset(isLocal = true)
|
channel.clearQueues(newLPStreamEOFError())
|
||||||
|
channel.recvWindow = 0
|
||||||
|
channel.sendWindow = 0
|
||||||
|
channel.closedLocally = true
|
||||||
|
channel.isReset = true
|
||||||
|
channel.opened = false
|
||||||
|
channel.isClosed = true
|
||||||
|
await channel.remoteClosed()
|
||||||
|
channel.receivedData.fire()
|
||||||
try:
|
try:
|
||||||
await m.connection.write(YamuxHeader.goAway(NormalTermination))
|
await m.connection.write(YamuxHeader.goAway(NormalTermination))
|
||||||
except CancelledError as exc:
|
except CancelledError as exc:
|
||||||
@@ -517,6 +550,8 @@ method close*(m: Yamux) {.async: (raises: []).} =
|
|||||||
except LPStreamError as exc:
|
except LPStreamError as exc:
|
||||||
trace "failed to send goAway", description = exc.msg
|
trace "failed to send goAway", description = exc.msg
|
||||||
await m.connection.close()
|
await m.connection.close()
|
||||||
|
|
||||||
|
m.isClosed = true
|
||||||
trace "Closed yamux"
|
trace "Closed yamux"
|
||||||
|
|
||||||
proc handleStream(m: Yamux, channel: YamuxChannel) {.async: (raises: []).} =
|
proc handleStream(m: Yamux, channel: YamuxChannel) {.async: (raises: []).} =
|
||||||
@@ -575,31 +610,35 @@ method handle*(m: Yamux) {.async: (raises: []).} =
|
|||||||
raise
|
raise
|
||||||
newException(YamuxError, "Peer exhausted the recvWindow after reset")
|
newException(YamuxError, "Peer exhausted the recvWindow after reset")
|
||||||
if header.length > 0:
|
if header.length > 0:
|
||||||
var buffer = newSeqUninitialized[byte](header.length)
|
var buffer = newSeqUninit[byte](header.length)
|
||||||
await m.connection.readExactly(addr buffer[0], int(header.length))
|
await m.connection.readExactly(addr buffer[0], int(header.length))
|
||||||
do:
|
|
||||||
raise newException(YamuxError, "Unknown stream ID: " & $header.streamId)
|
# If we do not have a stream, likely we sent a RST and/or closed the stream
|
||||||
|
trace "unknown stream id", id = header.streamId
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
let channel =
|
let channel =
|
||||||
try:
|
try:
|
||||||
m.channels[header.streamId]
|
m.channels[header.streamId]
|
||||||
except KeyError:
|
except KeyError as e:
|
||||||
raise newException(
|
raise newException(
|
||||||
YamuxError,
|
YamuxError,
|
||||||
"Stream was cleaned up before handling data: " & $header.streamId,
|
"Stream was cleaned up before handling data: " & $header.streamId & " : " &
|
||||||
|
e.msg,
|
||||||
|
e,
|
||||||
)
|
)
|
||||||
|
|
||||||
if header.msgType == WindowUpdate:
|
if header.msgType == WindowUpdate:
|
||||||
channel.sendWindow += int(header.length)
|
channel.sendWindow += int(header.length)
|
||||||
await channel.trySend()
|
asyncSpawn channel.sendLoop()
|
||||||
else:
|
else:
|
||||||
if header.length.int > channel.recvWindow.int:
|
if header.length.int > channel.recvWindow.int:
|
||||||
# check before allocating the buffer
|
# check before allocating the buffer
|
||||||
raise newException(YamuxError, "Peer exhausted the recvWindow")
|
raise newException(YamuxError, "Peer exhausted the recvWindow")
|
||||||
|
|
||||||
if header.length > 0:
|
if header.length > 0:
|
||||||
var buffer = newSeqUninitialized[byte](header.length)
|
var buffer = newSeqUninit[byte](header.length)
|
||||||
await m.connection.readExactly(addr buffer[0], int(header.length))
|
await m.connection.readExactly(addr buffer[0], int(header.length))
|
||||||
trace "Msg Rcv", description = shortLog(buffer)
|
trace "Msg Rcv", description = shortLog(buffer)
|
||||||
await channel.gotDataFromRemote(buffer)
|
await channel.gotDataFromRemote(buffer)
|
||||||
@@ -632,7 +671,7 @@ method handle*(m: Yamux) {.async: (raises: []).} =
|
|||||||
await m.close()
|
await m.close()
|
||||||
trace "Stopped yamux handler"
|
trace "Stopped yamux handler"
|
||||||
|
|
||||||
method getStreams*(m: Yamux): seq[Connection] =
|
method getStreams*(m: Yamux): seq[Connection] {.gcsafe.} =
|
||||||
for c in m.channels.values:
|
for c in m.channels.values:
|
||||||
result.add(c)
|
result.add(c)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Nim-LibP2P
|
# Nim-LibP2P
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
@@ -10,12 +10,13 @@
|
|||||||
{.push raises: [].}
|
{.push raises: [].}
|
||||||
|
|
||||||
import
|
import
|
||||||
std/[streams, strutils, sets, sequtils],
|
std/[streams, sets, sequtils],
|
||||||
chronos,
|
chronos,
|
||||||
chronicles,
|
chronicles,
|
||||||
stew/byteutils,
|
stew/byteutils,
|
||||||
dnsclientpkg/[protocol, types],
|
dnsclientpkg/[protocol, types],
|
||||||
../utility
|
../utility,
|
||||||
|
../utils/sequninit
|
||||||
|
|
||||||
import nameresolver
|
import nameresolver
|
||||||
|
|
||||||
@@ -37,26 +38,34 @@ proc questionToBuf(address: string, kind: QKind): seq[byte] =
|
|||||||
let dataLen = requestStream.getPosition()
|
let dataLen = requestStream.getPosition()
|
||||||
requestStream.setPosition(0)
|
requestStream.setPosition(0)
|
||||||
|
|
||||||
var buf = newSeq[byte](dataLen)
|
var buf = newSeqUninit[byte](dataLen)
|
||||||
discard requestStream.readData(addr buf[0], dataLen)
|
discard requestStream.readData(addr buf[0], dataLen)
|
||||||
return buf
|
buf
|
||||||
except CatchableError as exc:
|
except IOError as exc:
|
||||||
info "Failed to created DNS buffer", description = exc.msg
|
info "Failed to created DNS buffer", description = exc.msg
|
||||||
return newSeq[byte](0)
|
newSeqUninit[byte](0)
|
||||||
|
except OSError as exc:
|
||||||
|
info "Failed to created DNS buffer", description = exc.msg
|
||||||
|
newSeqUninit[byte](0)
|
||||||
|
except ValueError as exc:
|
||||||
|
info "Failed to created DNS buffer", description = exc.msg
|
||||||
|
newSeqUninit[byte](0)
|
||||||
|
|
||||||
proc getDnsResponse(
|
proc getDnsResponse(
|
||||||
dnsServer: TransportAddress, address: string, kind: QKind
|
dnsServer: TransportAddress, address: string, kind: QKind
|
||||||
): Future[Response] {.async.} =
|
): Future[Response] {.
|
||||||
|
async: (raises: [CancelledError, IOError, OSError, TransportError, ValueError])
|
||||||
|
.} =
|
||||||
var sendBuf = questionToBuf(address, kind)
|
var sendBuf = questionToBuf(address, kind)
|
||||||
|
|
||||||
if sendBuf.len == 0:
|
if sendBuf.len == 0:
|
||||||
raise newException(ValueError, "Incorrect DNS query")
|
raise newException(ValueError, "Incorrect DNS query")
|
||||||
|
|
||||||
let receivedDataFuture = newFuture[void]()
|
let receivedDataFuture = Future[void].Raising([CancelledError]).init()
|
||||||
|
|
||||||
proc datagramDataReceived(
|
proc datagramDataReceived(
|
||||||
transp: DatagramTransport, raddr: TransportAddress
|
transp: DatagramTransport, raddr: TransportAddress
|
||||||
): Future[void] {.async, closure.} =
|
): Future[void] {.async: (raises: []).} =
|
||||||
receivedDataFuture.complete()
|
receivedDataFuture.complete()
|
||||||
|
|
||||||
let sock =
|
let sock =
|
||||||
@@ -68,27 +77,41 @@ proc getDnsResponse(
|
|||||||
try:
|
try:
|
||||||
await sock.sendTo(dnsServer, addr sendBuf[0], sendBuf.len)
|
await sock.sendTo(dnsServer, addr sendBuf[0], sendBuf.len)
|
||||||
|
|
||||||
await receivedDataFuture or sleepAsync(5.seconds) #unix default
|
try:
|
||||||
|
await receivedDataFuture.wait(5.seconds) #unix default
|
||||||
if not receivedDataFuture.finished:
|
except AsyncTimeoutError as e:
|
||||||
raise newException(IOError, "DNS server timeout")
|
raise newException(IOError, "DNS server timeout: " & e.msg, e)
|
||||||
|
|
||||||
let rawResponse = sock.getMessage()
|
let rawResponse = sock.getMessage()
|
||||||
# parseResponse can has a raises: [Exception, ..] because of
|
try:
|
||||||
# https://github.com/nim-lang/Nim/commit/035134de429b5d99c5607c5fae912762bebb6008
|
|
||||||
# it can't actually raise though
|
|
||||||
return exceptionToAssert:
|
|
||||||
parseResponse(string.fromBytes(rawResponse))
|
parseResponse(string.fromBytes(rawResponse))
|
||||||
|
except IOError as exc:
|
||||||
|
raise newException(IOError, "Failed to parse DNS response: " & exc.msg, exc)
|
||||||
|
except OSError as exc:
|
||||||
|
raise newException(OSError, "Failed to parse DNS response: " & exc.msg, exc)
|
||||||
|
except ValueError as exc:
|
||||||
|
raise newException(ValueError, "Failed to parse DNS response: " & exc.msg, exc)
|
||||||
|
except Exception as exc:
|
||||||
|
# Nim 1.6: parseResponse can has a raises: [Exception, ..] because of
|
||||||
|
# https://github.com/nim-lang/Nim/commit/035134de429b5d99c5607c5fae912762bebb6008
|
||||||
|
# it can't actually raise though
|
||||||
|
raiseAssert "Exception parsing DN response: " & exc.msg
|
||||||
finally:
|
finally:
|
||||||
await sock.closeWait()
|
await sock.closeWait()
|
||||||
|
|
||||||
method resolveIp*(
|
method resolveIp*(
|
||||||
self: DnsResolver, address: string, port: Port, domain: Domain = Domain.AF_UNSPEC
|
self: DnsResolver, address: string, port: Port, domain: Domain = Domain.AF_UNSPEC
|
||||||
): Future[seq[TransportAddress]] {.async.} =
|
): Future[seq[TransportAddress]] {.
|
||||||
|
async: (raises: [CancelledError, TransportAddressError])
|
||||||
|
.} =
|
||||||
trace "Resolving IP using DNS", address, servers = self.nameServers.mapIt($it), domain
|
trace "Resolving IP using DNS", address, servers = self.nameServers.mapIt($it), domain
|
||||||
for _ in 0 ..< self.nameServers.len:
|
for _ in 0 ..< self.nameServers.len:
|
||||||
let server = self.nameServers[0]
|
let server = self.nameServers[0]
|
||||||
var responseFutures: seq[Future[Response]]
|
var responseFutures: seq[
|
||||||
|
Future[Response].Raising(
|
||||||
|
[CancelledError, IOError, OSError, TransportError, ValueError]
|
||||||
|
)
|
||||||
|
]
|
||||||
if domain == Domain.AF_INET or domain == Domain.AF_UNSPEC:
|
if domain == Domain.AF_INET or domain == Domain.AF_UNSPEC:
|
||||||
responseFutures.add(getDnsResponse(server, address, A))
|
responseFutures.add(getDnsResponse(server, address, A))
|
||||||
|
|
||||||
@@ -103,23 +126,32 @@ method resolveIp*(
|
|||||||
var
|
var
|
||||||
resolvedAddresses: OrderedSet[string]
|
resolvedAddresses: OrderedSet[string]
|
||||||
resolveFailed = false
|
resolveFailed = false
|
||||||
|
template handleFail(e): untyped =
|
||||||
|
info "Failed to query DNS", address, error = e.msg
|
||||||
|
resolveFailed = true
|
||||||
|
break
|
||||||
|
|
||||||
for fut in responseFutures:
|
for fut in responseFutures:
|
||||||
try:
|
try:
|
||||||
let resp = await fut
|
let resp = await fut
|
||||||
for answer in resp.answers:
|
for answer in resp.answers:
|
||||||
# toString can has a raises: [Exception, ..] because of
|
resolvedAddresses.incl(answer.toString())
|
||||||
# https://github.com/nim-lang/Nim/commit/035134de429b5d99c5607c5fae912762bebb6008
|
|
||||||
# it can't actually raise though
|
|
||||||
resolvedAddresses.incl(exceptionToAssert(answer.toString()))
|
|
||||||
except CancelledError as e:
|
except CancelledError as e:
|
||||||
raise e
|
raise e
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
info "Invalid DNS query", address, error = e.msg
|
info "Invalid DNS query", address, error = e.msg
|
||||||
return @[]
|
return @[]
|
||||||
except CatchableError as e:
|
except IOError as e:
|
||||||
info "Failed to query DNS", address, error = e.msg
|
handleFail(e)
|
||||||
resolveFailed = true
|
except OSError as e:
|
||||||
break
|
handleFail(e)
|
||||||
|
except TransportError as e:
|
||||||
|
handleFail(e)
|
||||||
|
except Exception as e:
|
||||||
|
# Nim 1.6: answer.toString can has a raises: [Exception, ..] because of
|
||||||
|
# https://github.com/nim-lang/Nim/commit/035134de429b5d99c5607c5fae912762bebb6008
|
||||||
|
# it can't actually raise though
|
||||||
|
raiseAssert e.msg
|
||||||
|
|
||||||
if resolveFailed:
|
if resolveFailed:
|
||||||
self.nameServers.add(self.nameServers[0])
|
self.nameServers.add(self.nameServers[0])
|
||||||
@@ -132,27 +164,39 @@ method resolveIp*(
|
|||||||
debug "Failed to resolve address, returning empty set"
|
debug "Failed to resolve address, returning empty set"
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
method resolveTxt*(self: DnsResolver, address: string): Future[seq[string]] {.async.} =
|
method resolveTxt*(
|
||||||
|
self: DnsResolver, address: string
|
||||||
|
): Future[seq[string]] {.async: (raises: [CancelledError]).} =
|
||||||
trace "Resolving TXT using DNS", address, servers = self.nameServers.mapIt($it)
|
trace "Resolving TXT using DNS", address, servers = self.nameServers.mapIt($it)
|
||||||
for _ in 0 ..< self.nameServers.len:
|
for _ in 0 ..< self.nameServers.len:
|
||||||
let server = self.nameServers[0]
|
let server = self.nameServers[0]
|
||||||
try:
|
template handleFail(e): untyped =
|
||||||
# toString can has a raises: [Exception, ..] because of
|
|
||||||
# https://github.com/nim-lang/Nim/commit/035134de429b5d99c5607c5fae912762bebb6008
|
|
||||||
# it can't actually raise though
|
|
||||||
let response = await getDnsResponse(server, address, TXT)
|
|
||||||
return exceptionToAssert:
|
|
||||||
trace "Got TXT response",
|
|
||||||
server = $server, answer = response.answers.mapIt(it.toString())
|
|
||||||
response.answers.mapIt(it.toString())
|
|
||||||
except CancelledError as e:
|
|
||||||
raise e
|
|
||||||
except CatchableError as e:
|
|
||||||
info "Failed to query DNS", address, error = e.msg
|
info "Failed to query DNS", address, error = e.msg
|
||||||
self.nameServers.add(self.nameServers[0])
|
self.nameServers.add(self.nameServers[0])
|
||||||
self.nameServers.delete(0)
|
self.nameServers.delete(0)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
let response = await getDnsResponse(server, address, TXT)
|
||||||
|
trace "Got TXT response",
|
||||||
|
server = $server, answer = response.answers.mapIt(it.toString())
|
||||||
|
return response.answers.mapIt(it.toString())
|
||||||
|
except CancelledError as e:
|
||||||
|
raise e
|
||||||
|
except IOError as e:
|
||||||
|
handleFail(e)
|
||||||
|
except OSError as e:
|
||||||
|
handleFail(e)
|
||||||
|
except TransportError as e:
|
||||||
|
handleFail(e)
|
||||||
|
except ValueError as e:
|
||||||
|
handleFail(e)
|
||||||
|
except Exception as e:
|
||||||
|
# Nim 1.6: toString can has a raises: [Exception, ..] because of
|
||||||
|
# https://github.com/nim-lang/Nim/commit/035134de429b5d99c5607c5fae912762bebb6008
|
||||||
|
# it can't actually raise though
|
||||||
|
raiseAssert e.msg
|
||||||
|
|
||||||
debug "Failed to resolve TXT, returning empty set"
|
debug "Failed to resolve TXT, returning empty set"
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Nim-LibP2P
|
# Nim-LibP2P
|
||||||
# Copyright (c) 2023 Status Research & Development GmbH
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
||||||
# Licensed under either of
|
# Licensed under either of
|
||||||
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
|
||||||
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
|
||||||
@@ -25,17 +25,25 @@ type MockResolver* = ref object of NameResolver
|
|||||||
|
|
||||||
method resolveIp*(
|
method resolveIp*(
|
||||||
self: MockResolver, address: string, port: Port, domain: Domain = Domain.AF_UNSPEC
|
self: MockResolver, address: string, port: Port, domain: Domain = Domain.AF_UNSPEC
|
||||||
): Future[seq[TransportAddress]] {.async.} =
|
): Future[seq[TransportAddress]] {.
|
||||||
|
async: (raises: [CancelledError, TransportAddressError])
|
||||||
|
.} =
|
||||||
|
var res: seq[TransportAddress]
|
||||||
|
|
||||||
if domain == Domain.AF_INET or domain == Domain.AF_UNSPEC:
|
if domain == Domain.AF_INET or domain == Domain.AF_UNSPEC:
|
||||||
for resp in self.ipResponses.getOrDefault((address, false)):
|
for resp in self.ipResponses.getOrDefault((address, false)):
|
||||||
result.add(initTAddress(resp, port))
|
res.add(initTAddress(resp, port))
|
||||||
|
|
||||||
if domain == Domain.AF_INET6 or domain == Domain.AF_UNSPEC:
|
if domain == Domain.AF_INET6 or domain == Domain.AF_UNSPEC:
|
||||||
for resp in self.ipResponses.getOrDefault((address, true)):
|
for resp in self.ipResponses.getOrDefault((address, true)):
|
||||||
result.add(initTAddress(resp, port))
|
res.add(initTAddress(resp, port))
|
||||||
|
|
||||||
method resolveTxt*(self: MockResolver, address: string): Future[seq[string]] {.async.} =
|
res
|
||||||
return self.txtResponses.getOrDefault(address)
|
|
||||||
|
method resolveTxt*(
|
||||||
|
self: MockResolver, address: string
|
||||||
|
): Future[seq[string]] {.async: (raises: [CancelledError]).} =
|
||||||
|
self.txtResponses.getOrDefault(address)
|
||||||
|
|
||||||
proc new*(T: typedesc[MockResolver]): T =
|
proc new*(T: typedesc[MockResolver]): T =
|
||||||
T()
|
T()
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user