mirror of
https://source.quilibrium.com/quilibrium/ceremonyclient.git
synced 2024-12-24 15:45:18 +00:00
Launch readiness: import utility
This commit is contained in:
parent
ffab09ae6b
commit
529f434393
@ -56,18 +56,18 @@ func (t *Bitmask) SetScoreParams(p *BitmaskScoreParams) error {
|
|||||||
|
|
||||||
result := make(chan error, 1)
|
result := make(chan error, 1)
|
||||||
update := func() {
|
update := func() {
|
||||||
gs, ok := t.p.rt.(*BlossomSubRouter)
|
bs, ok := t.p.rt.(*BlossomSubRouter)
|
||||||
if !ok {
|
if !ok {
|
||||||
result <- fmt.Errorf("pubsub router is not BlossomSub")
|
result <- fmt.Errorf("pubsub router is not BlossomSub")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if gs.score == nil {
|
if bs.score == nil {
|
||||||
result <- fmt.Errorf("peer scoring is not enabled in router")
|
result <- fmt.Errorf("peer scoring is not enabled in router")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err := gs.score.SetBitmaskScoreParams(t.bitmask, p)
|
err := bs.score.SetBitmaskScoreParams(t.bitmask, p)
|
||||||
result <- err
|
result <- err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -38,9 +38,9 @@ func TestBlossomSubCustomProtocols(t *testing.T) {
|
|||||||
defer cancel()
|
defer cancel()
|
||||||
hosts := getNetHosts(t, ctx, 3)
|
hosts := getNetHosts(t, ctx, 3)
|
||||||
|
|
||||||
gsubs := getBlossomSubs(ctx, hosts[:2], WithBlossomSubProtocols(protos, features))
|
bsubs := getBlossomSubs(ctx, hosts[:2], WithBlossomSubProtocols(protos, features))
|
||||||
fsub := getPubsub(ctx, hosts[2])
|
fsub := getPubsub(ctx, hosts[2])
|
||||||
psubs := append(gsubs, fsub)
|
psubs := append(bsubs, fsub)
|
||||||
|
|
||||||
connectAll(t, hosts)
|
connectAll(t, hosts)
|
||||||
|
|
||||||
@ -58,33 +58,33 @@ func TestBlossomSubCustomProtocols(t *testing.T) {
|
|||||||
// wait for heartbeats to build mesh
|
// wait for heartbeats to build mesh
|
||||||
time.Sleep(time.Second * 2)
|
time.Sleep(time.Second * 2)
|
||||||
|
|
||||||
// check the meshes of the gsubs, the BlossomSub meshes should include each other but not the
|
// check the meshes of the bsubs, the BlossomSub meshes should include each other but not the
|
||||||
// floddsub peer
|
// floddsub peer
|
||||||
gsubs[0].eval <- func() {
|
bsubs[0].eval <- func() {
|
||||||
gs := gsubs[0].rt.(*BlossomSubRouter)
|
bs := bsubs[0].rt.(*BlossomSubRouter)
|
||||||
|
|
||||||
_, ok := gs.mesh[string(bitmask)][hosts[1].ID()]
|
_, ok := bs.mesh[string(bitmask)][hosts[1].ID()]
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("expected gs0 to have gs1 in its mesh")
|
t.Fatal("expected bs0 to have bs1 in its mesh")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok = gs.mesh[string(bitmask)][hosts[2].ID()]
|
_, ok = bs.mesh[string(bitmask)][hosts[2].ID()]
|
||||||
if ok {
|
if ok {
|
||||||
t.Fatal("expected gs0 to not have fs in its mesh")
|
t.Fatal("expected bs0 to not have fs in its mesh")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gsubs[1].eval <- func() {
|
bsubs[1].eval <- func() {
|
||||||
gs := gsubs[1].rt.(*BlossomSubRouter)
|
bs := bsubs[1].rt.(*BlossomSubRouter)
|
||||||
|
|
||||||
_, ok := gs.mesh[string(bitmask)][hosts[0].ID()]
|
_, ok := bs.mesh[string(bitmask)][hosts[0].ID()]
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatal("expected gs1 to have gs0 in its mesh")
|
t.Fatal("expected bs1 to have bs0 in its mesh")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, ok = gs.mesh[string(bitmask)][hosts[2].ID()]
|
_, ok = bs.mesh[string(bitmask)][hosts[2].ID()]
|
||||||
if ok {
|
if ok {
|
||||||
t.Fatal("expected gs1 to not have fs in its mesh")
|
t.Fatal("expected bs1 to not have fs in its mesh")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ func TestBlossomSubAttackSpamIWANT(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
newMockGS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||||
// When the legit host connects it will send us its subscriptions
|
// When the legit host connects it will send us its subscriptions
|
||||||
for _, sub := range irpc.GetSubscriptions() {
|
for _, sub := range irpc.GetSubscriptions() {
|
||||||
if sub.GetSubscribe() {
|
if sub.GetSubscribe() {
|
||||||
@ -185,7 +185,7 @@ func TestBlossomSubAttackSpamIHAVE(t *testing.T) {
|
|||||||
iWantCount += i
|
iWantCount += i
|
||||||
}
|
}
|
||||||
|
|
||||||
newMockGS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||||
// When the legit host connects it will send us its subscriptions
|
// When the legit host connects it will send us its subscriptions
|
||||||
for _, sub := range irpc.GetSubscriptions() {
|
for _, sub := range irpc.GetSubscriptions() {
|
||||||
if sub.GetSubscribe() {
|
if sub.GetSubscribe() {
|
||||||
@ -319,7 +319,7 @@ func TestBlossomSubAttackGRAFTNonExistentBitmask(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newMockGS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||||
// When the legit host connects it will send us its subscriptions
|
// When the legit host connects it will send us its subscriptions
|
||||||
for _, sub := range irpc.GetSubscriptions() {
|
for _, sub := range irpc.GetSubscriptions() {
|
||||||
if sub.GetSubscribe() {
|
if sub.GetSubscribe() {
|
||||||
@ -419,7 +419,7 @@ func TestBlossomSubAttackGRAFTDuringBackoff(t *testing.T) {
|
|||||||
pruneCount += i
|
pruneCount += i
|
||||||
}
|
}
|
||||||
|
|
||||||
newMockGS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||||
// When the legit host connects it will send us its subscriptions
|
// When the legit host connects it will send us its subscriptions
|
||||||
for _, sub := range irpc.GetSubscriptions() {
|
for _, sub := range irpc.GetSubscriptions() {
|
||||||
if sub.GetSubscribe() {
|
if sub.GetSubscribe() {
|
||||||
@ -599,11 +599,11 @@ func TestBlossomSubAttackGRAFTDuringBackoff(t *testing.T) {
|
|||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
type gsAttackInvalidMsgTracer struct {
|
type bsAttackInvalidMsgTracer struct {
|
||||||
rejectCount int
|
rejectCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *gsAttackInvalidMsgTracer) Trace(evt *pb.TraceEvent) {
|
func (t *bsAttackInvalidMsgTracer) Trace(evt *pb.TraceEvent) {
|
||||||
// fmt.Printf(" %s %s\n", evt.Type, evt)
|
// fmt.Printf(" %s %s\n", evt.Type, evt)
|
||||||
if evt.GetType() == pb.TraceEvent_REJECT_MESSAGE {
|
if evt.GetType() == pb.TraceEvent_REJECT_MESSAGE {
|
||||||
t.rejectCount++
|
t.rejectCount++
|
||||||
@ -660,7 +660,7 @@ func TestBlossomSubAttackInvalidMessageSpam(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set up BlossomSub on the legit host
|
// Set up BlossomSub on the legit host
|
||||||
tracer := &gsAttackInvalidMsgTracer{}
|
tracer := &bsAttackInvalidMsgTracer{}
|
||||||
ps, err := NewBlossomSub(ctx, legit,
|
ps, err := NewBlossomSub(ctx, legit,
|
||||||
WithEventTracer(tracer),
|
WithEventTracer(tracer),
|
||||||
WithPeerScore(params, thresholds),
|
WithPeerScore(params, thresholds),
|
||||||
@ -692,7 +692,7 @@ func TestBlossomSubAttackInvalidMessageSpam(t *testing.T) {
|
|||||||
pruneCount += i
|
pruneCount += i
|
||||||
}
|
}
|
||||||
|
|
||||||
newMockGS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
newMockBS(ctx, t, attacker, func(writeMsg func(*pb.RPC), irpc *pb.RPC) {
|
||||||
// When the legit host connects it will send us its subscriptions
|
// When the legit host connects it will send us its subscriptions
|
||||||
for _, sub := range irpc.GetSubscriptions() {
|
for _, sub := range irpc.GetSubscriptions() {
|
||||||
if sub.GetSubscribe() {
|
if sub.GetSubscribe() {
|
||||||
@ -762,9 +762,9 @@ func TestBlossomSubAttackInvalidMessageSpam(t *testing.T) {
|
|||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
type mockGSOnRead func(writeMsg func(*pb.RPC), irpc *pb.RPC)
|
type MockBSOnRead func(writeMsg func(*pb.RPC), irpc *pb.RPC)
|
||||||
|
|
||||||
func newMockGS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg mockGSOnRead) {
|
func newMockBS(ctx context.Context, t *testing.T, attacker host.Host, onReadMsg MockBSOnRead) {
|
||||||
// Listen on the BlossomSub protocol
|
// Listen on the BlossomSub protocol
|
||||||
const BlossomSubID = protocol.ID("/meshsub/1.0.0")
|
const BlossomSubID = protocol.ID("/meshsub/1.0.0")
|
||||||
const maxMessageSize = 1024 * 1024
|
const maxMessageSize = 1024 * 1024
|
||||||
|
@ -35,13 +35,13 @@ func newGossipTracer() *gossipTracer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gt *gossipTracer) Start(gs *BlossomSubRouter) {
|
func (gt *gossipTracer) Start(bs *BlossomSubRouter) {
|
||||||
if gt == nil {
|
if gt == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
gt.idGen = gs.p.idGen
|
gt.idGen = bs.p.idGen
|
||||||
gt.followUpTime = gs.params.IWantFollowupTime
|
gt.followUpTime = bs.params.IWantFollowupTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// track a promise to deliver a message from a list of msgIDs we are requesting
|
// track a promise to deliver a message from a list of msgIDs we are requesting
|
||||||
|
2
go-libp2p/.codecov.yml
Normal file
2
go-libp2p/.codecov.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
github_checks:
|
||||||
|
annotations: false
|
8
go-libp2p/.githooks/README.md
Normal file
8
go-libp2p/.githooks/README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Git Hooks
|
||||||
|
|
||||||
|
This directory contains useful Git hooks for working with go-libp2p.
|
||||||
|
|
||||||
|
Install them by running
|
||||||
|
```bash
|
||||||
|
git config core.hooksPath .githooks
|
||||||
|
```
|
13
go-libp2p/.githooks/pre-commit
Executable file
13
go-libp2p/.githooks/pre-commit
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
pushd ./test-plans > /dev/null
|
||||||
|
go mod tidy
|
||||||
|
if [[ -n $(git diff --name-only -- "go.mod" "go.sum") ]]; then
|
||||||
|
echo "go.mod / go.sum in test-plans not tidied"
|
||||||
|
errored=true
|
||||||
|
fi
|
||||||
|
popd > /dev/null
|
||||||
|
|
||||||
|
if [ "$errored" = true ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
20
go-libp2p/.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
20
go-libp2p/.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
---
|
||||||
|
name: 'Bug Report'
|
||||||
|
about: 'Report a bug in go-libp2p.'
|
||||||
|
labels: bug
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- This is where you get to tell us what went wrong. When doing so, please make sure to include *all* relevant information.
|
||||||
|
|
||||||
|
Please try to include:
|
||||||
|
* What you were doing when you experienced the bug.
|
||||||
|
* Any error messages you saw, *where* you saw them, and what you believe may have caused them (if you have any ideas).
|
||||||
|
* When possible, steps to reliably produce the bug.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Version Information</summary>
|
||||||
|
<pre>
|
||||||
|
<!-- Insert the output of `go list -m all` HERE -->
|
||||||
|
</pre>
|
||||||
|
</details>
|
13
go-libp2p/.github/ISSUE_TEMPLATE/doc.md
vendored
Normal file
13
go-libp2p/.github/ISSUE_TEMPLATE/doc.md
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
name: 'Documentation Issue'
|
||||||
|
about: 'Report missing/erroneous documentation, propose new documentation, report broken links, etc.'
|
||||||
|
labels: documentation
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Location
|
||||||
|
|
||||||
|
<!-- In the case of missing/erroneous documentation, where is the error? If possible, a link/url would be great! -->
|
||||||
|
|
||||||
|
#### Description
|
||||||
|
|
||||||
|
<!-- Describe the documentation issue. -->
|
11
go-libp2p/.github/ISSUE_TEMPLATE/enhancement.md
vendored
Normal file
11
go-libp2p/.github/ISSUE_TEMPLATE/enhancement.md
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
---
|
||||||
|
name: 'Enhancement'
|
||||||
|
about: 'Suggest an improvement to an existing go-libp2p feature.'
|
||||||
|
labels: enhancement
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Note: If you'd like to suggest an idea related to libp2p but not specifically related to the Go implementation, please file an issue at https://github.com/libp2p/specs instead. Even better, create a new topic on the forums (https://discuss.libp2p.io).
|
||||||
|
|
||||||
|
When requesting an _enhancement_, please be sure to include your motivation and try to be as specific as possible.
|
||||||
|
-->
|
15
go-libp2p/.github/ISSUE_TEMPLATE/feature.md
vendored
Normal file
15
go-libp2p/.github/ISSUE_TEMPLATE/feature.md
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
name: 'Feature'
|
||||||
|
about: 'Suggest a new feature in go-libp2p.'
|
||||||
|
labels: feature
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Note: If you'd like to suggest an idea related to libp2p but not specifically related to the Go implementation, please file an issue at https://github.com/libp2p/specs instead. Even better, create a new topic on the forums (https://discuss.libp2p.io).
|
||||||
|
|
||||||
|
When requesting a _feature_, please be sure to include:
|
||||||
|
* Your motivation. Why do you need the feature?
|
||||||
|
* How the feature should work.
|
||||||
|
|
||||||
|
Please try to be as specific and concrete as possible.
|
||||||
|
-->
|
9
go-libp2p/.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
9
go-libp2p/.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
name: 'Question/Support'
|
||||||
|
about: 'Ask a question about go-libp2p or request support.'
|
||||||
|
labels: question, invalid
|
||||||
|
---
|
||||||
|
|
||||||
|
This bug tracker is only for actionable bug reports and feature requests. Please direct any questions to https://discuss.libp2p.io or to our Matrix (#libp2p:matrix.org) or IRC (#libp2p on freenode) channels.
|
||||||
|
|
||||||
|
If you don't get an immediate response, please keep trying.
|
34
go-libp2p/.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
34
go-libp2p/.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
name: 'Libp2p Release'
|
||||||
|
about: 'Start a new libp2p release.'
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗺 What's left for release
|
||||||
|
|
||||||
|
<List of items with remaining PRs and/or Issues to be considered for this release>
|
||||||
|
|
||||||
|
## 🔦 Highlights
|
||||||
|
|
||||||
|
< top highlights for this release notes >
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
< changelog generated by scripts/mkreleaselog >
|
||||||
|
|
||||||
|
## ✅ Release Checklist
|
||||||
|
|
||||||
|
- [ ] **Stage 0 - Finishing Touches**
|
||||||
|
- [ ] Go through relevant libp2p repos looking for unreleased changes that should make it into the release. If you find any, cut releases.
|
||||||
|
- [ ] Run `go get -u ./...` to see if there are any out-of-date deps that look important. If there are, bubble them. Try to avoid _directly_ updating indirect deps in go-libp2p's `go.mod` when possible.
|
||||||
|
- [ ] **Stage 1 - Release**
|
||||||
|
- [ ] Publish the release through the GitHub UI, adding the release notes. Some users rely on this to receive notifications of new releases.
|
||||||
|
- [ ] Announce the release on the [discuss.libp2p.io](https://discuss.libp2p.io).
|
||||||
|
- [ ] **Stage 2 - Update Upstream**
|
||||||
|
- [ ] Update the examples to the final release
|
||||||
|
- [ ] Update the upstream dependencies to the final release and create PRs.
|
||||||
|
- [ ] [filecoin-project/lotus](https://github.com/filecoin-project/lotus)
|
||||||
|
- [ ] [go-libp2p-kad-dht](https://github.com/libp2p/go-libp2p-kad-dht/)
|
||||||
|
- [ ] [go-libp2p-pubsub](https://github.com/libp2p/go-libp2p-pubsub) (In case of breaking changes.)
|
||||||
|
- [ ] [ipfs/kubo](https://github.com/ipfs/kubo)
|
||||||
|
- [ ] Add new release to interop tester in [test-plans](https://github.com/libp2p/test-plans/)
|
||||||
|
- [ ] Make required changes to the release process.
|
11
go-libp2p/.github/actions/go-check-setup/action.yml
vendored
Normal file
11
go-libp2p/.github/actions/go-check-setup/action.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Install Protoc
|
||||||
|
uses: trail-of-forks/setup-protoc@a97892a429d98fae78d26f40334ab7eb616d08b9 # include https://github.com/arduino/setup-protoc/pull/58
|
||||||
|
with:
|
||||||
|
version: '21.12'
|
||||||
|
repo-token: ${{ github.token }}
|
||||||
|
- name: Install Protobuf compiler
|
||||||
|
shell: bash
|
||||||
|
run: go install google.golang.org/protobuf/cmd/protoc-gen-go
|
11
go-libp2p/.github/actions/go-test-setup/action.yml
vendored
Normal file
11
go-libp2p/.github/actions/go-test-setup/action.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: increase the UDP receive buffer size # see https://github.com/lucas-clemente/quic-go/wiki/UDP-Receive-Buffer-Size
|
||||||
|
shell: bash
|
||||||
|
run: sysctl -w net.core.rmem_max=2500000
|
||||||
|
if: ${{ matrix.os == 'ubuntu' }}
|
||||||
|
- name: Run nocover tests. These are tests that require the coverage analysis to be off # See https://github.com/protocol/.github/issues/460
|
||||||
|
shell: bash
|
||||||
|
# This matches only tests with "NoCover" in their test name to avoid running all tests again.
|
||||||
|
run: go test -tags nocover -run NoCover -v ./...
|
11
go-libp2p/.github/workflows/automerge.yml
vendored
Normal file
11
go-libp2p/.github/workflows/automerge.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# File managed by web3-bot. DO NOT EDIT.
|
||||||
|
# See https://github.com/protocol/.github/ for details.
|
||||||
|
|
||||||
|
name: Automerge
|
||||||
|
on: [ pull_request ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
automerge:
|
||||||
|
uses: protocol/.github/.github/workflows/automerge.yml@master
|
||||||
|
with:
|
||||||
|
job: 'automerge'
|
3
go-libp2p/.github/workflows/go-check-config.json
vendored
Normal file
3
go-libp2p/.github/workflows/go-check-config.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"gogenerate": true
|
||||||
|
}
|
67
go-libp2p/.github/workflows/go-check.yml
vendored
Normal file
67
go-libp2p/.github/workflows/go-check.yml
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# File managed by web3-bot. DO NOT EDIT.
|
||||||
|
# See https://github.com/protocol/.github/ for details.
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
name: Go Checks
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
unit:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: All
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- id: config
|
||||||
|
uses: protocol/.github/.github/actions/read-config@master
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: 1.20.x
|
||||||
|
- name: Run repo-specific setup
|
||||||
|
uses: ./.github/actions/go-check-setup
|
||||||
|
if: hashFiles('./.github/actions/go-check-setup') != ''
|
||||||
|
- name: Install staticcheck
|
||||||
|
run: go install honnef.co/go/tools/cmd/staticcheck@4970552d932f48b71485287748246cf3237cebdf # 2023.1 (v0.4.0)
|
||||||
|
- name: Check that go.mod is tidy
|
||||||
|
uses: protocol/multiple-go-modules@v1.2
|
||||||
|
with:
|
||||||
|
run: |
|
||||||
|
go mod tidy
|
||||||
|
if [[ -n $(git ls-files --other --exclude-standard --directory -- go.sum) ]]; then
|
||||||
|
echo "go.sum was added by go mod tidy"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
git diff --exit-code -- go.sum go.mod
|
||||||
|
- name: gofmt
|
||||||
|
if: success() || failure() # run this step even if the previous one failed
|
||||||
|
run: |
|
||||||
|
out=$(gofmt -s -l .)
|
||||||
|
if [[ -n "$out" ]]; then
|
||||||
|
echo $out | awk '{print "::error file=" $0 ",line=0,col=0::File is not gofmt-ed."}'
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
- name: go vet
|
||||||
|
if: success() || failure() # run this step even if the previous one failed
|
||||||
|
uses: protocol/multiple-go-modules@v1.2
|
||||||
|
with:
|
||||||
|
run: go vet ./...
|
||||||
|
- name: staticcheck
|
||||||
|
if: success() || failure() # run this step even if the previous one failed
|
||||||
|
uses: protocol/multiple-go-modules@v1.2
|
||||||
|
with:
|
||||||
|
run: |
|
||||||
|
set -o pipefail
|
||||||
|
staticcheck ./... | sed -e 's@\(.*\)\.go@./\1.go@g'
|
||||||
|
- name: go generate
|
||||||
|
uses: protocol/multiple-go-modules@v1.2
|
||||||
|
if: (success() || failure()) && fromJSON(steps.config.outputs.json).gogenerate == true
|
||||||
|
with:
|
||||||
|
run: |
|
||||||
|
git clean -fd # make sure there aren't untracked files / directories
|
||||||
|
go generate -x ./...
|
||||||
|
# check if go generate modified or added any files
|
||||||
|
if ! $(git add . && git diff-index HEAD --exit-code --quiet); then
|
||||||
|
echo "go generated caused changes to the repository:"
|
||||||
|
git status --short
|
||||||
|
exit 1
|
||||||
|
fi
|
76
go-libp2p/.github/workflows/go-test.yml
vendored
Normal file
76
go-libp2p/.github/workflows/go-test.yml
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# File managed by web3-bot. DO NOT EDIT.
|
||||||
|
# See https://github.com/protocol/.github/ for details.
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
name: Go Test
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
unit:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: [ "ubuntu", "windows", "macos" ]
|
||||||
|
go: ["1.19.x","1.20.x"]
|
||||||
|
env:
|
||||||
|
COVERAGES: ""
|
||||||
|
runs-on: ${{ fromJSON(vars[format('UCI_GO_TEST_RUNNER_{0}', matrix.os)] || format('"{0}-latest"', matrix.os)) }}
|
||||||
|
name: ${{ matrix.os }} (go ${{ matrix.go }})
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: recursive
|
||||||
|
- id: config
|
||||||
|
uses: protocol/.github/.github/actions/read-config@master
|
||||||
|
- uses: actions/setup-go@v3
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
- name: Go information
|
||||||
|
run: |
|
||||||
|
go version
|
||||||
|
go env
|
||||||
|
- name: Use msys2 on windows
|
||||||
|
if: matrix.os == 'windows'
|
||||||
|
shell: bash
|
||||||
|
# The executable for msys2 is also called bash.cmd
|
||||||
|
# https://github.com/actions/virtual-environments/blob/main/images/win/Windows2019-Readme.md#shells
|
||||||
|
# If we prepend its location to the PATH
|
||||||
|
# subsequent 'shell: bash' steps will use msys2 instead of gitbash
|
||||||
|
run: echo "C:/msys64/usr/bin" >> $GITHUB_PATH
|
||||||
|
- name: Run repo-specific setup
|
||||||
|
uses: ./.github/actions/go-test-setup
|
||||||
|
if: hashFiles('./.github/actions/go-test-setup') != ''
|
||||||
|
- name: Run tests
|
||||||
|
if: contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false
|
||||||
|
uses: protocol/multiple-go-modules@v1.2
|
||||||
|
with:
|
||||||
|
# Use -coverpkg=./..., so that we include cross-package coverage.
|
||||||
|
# If package ./A imports ./B, and ./A's tests also cover ./B,
|
||||||
|
# this means ./B's coverage will be significantly higher than 0%.
|
||||||
|
run: go test -v -shuffle=on -coverprofile=module-coverage.txt -coverpkg=./... ./...
|
||||||
|
- name: Run tests (32 bit)
|
||||||
|
# can't run 32 bit tests on OSX.
|
||||||
|
if: matrix.os != 'macos' &&
|
||||||
|
fromJSON(steps.config.outputs.json).skip32bit != true &&
|
||||||
|
contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false
|
||||||
|
uses: protocol/multiple-go-modules@v1.2
|
||||||
|
env:
|
||||||
|
GOARCH: 386
|
||||||
|
with:
|
||||||
|
run: |
|
||||||
|
export "PATH=$PATH_386:$PATH"
|
||||||
|
go test -v -shuffle=on ./...
|
||||||
|
- name: Run tests with race detector
|
||||||
|
# speed things up. Windows and OSX VMs are slow
|
||||||
|
if: matrix.os == 'ubuntu' &&
|
||||||
|
contains(fromJSON(steps.config.outputs.json).skipOSes, matrix.os) == false
|
||||||
|
uses: protocol/multiple-go-modules@v1.2
|
||||||
|
with:
|
||||||
|
run: go test -v -race ./...
|
||||||
|
- name: Collect coverage files
|
||||||
|
shell: bash
|
||||||
|
run: echo "COVERAGES=$(find . -type f -name 'module-coverage.txt' | tr -s '\n' ',' | sed 's/,$//')" >> $GITHUB_ENV
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@d9f34f8cd5cb3b3eb79b3e4b5dae3a16df499a70 # v3.1.1
|
||||||
|
with:
|
||||||
|
files: '${{ env.COVERAGES }}'
|
||||||
|
env_vars: OS=${{ matrix.os }}, GO=${{ matrix.go }}
|
22
go-libp2p/.github/workflows/interop-test.yml
vendored
Normal file
22
go-libp2p/.github/workflows/interop-test.yml
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
name: Interoperability Testing
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-multidim-interop:
|
||||||
|
name: Run multidimensional interoperability tests
|
||||||
|
runs-on: ${{ fromJSON(vars['INTEROP_TEST_RUNNER_UBUNTU'] || '"ubuntu-22.04"') }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Build image
|
||||||
|
run: docker build -t go-libp2p-head -f test-plans/PingDockerfile .
|
||||||
|
- uses: libp2p/test-plans/.github/actions/run-interop-ping-test@master
|
||||||
|
with:
|
||||||
|
test-filter: go-libp2p-head
|
||||||
|
extra-versions: ${{ github.workspace }}/test-plans/ping-version.json
|
||||||
|
s3-cache-bucket: libp2p-by-tf-aws-bootstrap
|
||||||
|
s3-access-key-id: ${{ vars.TEST_PLANS_BUILD_CACHE_KEY_ID }}
|
||||||
|
s3-secret-access-key: ${{ secrets.TEST_PLANS_BUILD_CACHE_KEY }}
|
17
go-libp2p/.github/workflows/link-check.yml
vendored
Normal file
17
go-libp2p/.github/workflows/link-check.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: Markdown Link Checking
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-links:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: gaurav-nelson/github-action-markdown-link-check@v1
|
||||||
|
with:
|
||||||
|
use-quiet-mode: 'yes' # show only broken links
|
||||||
|
use-verbose-mode: 'yes'
|
||||||
|
config-file: .github/workflows/markdown-links-config.json # for removing any false positives
|
22
go-libp2p/.github/workflows/markdown-links-config.json
vendored
Normal file
22
go-libp2p/.github/workflows/markdown-links-config.json
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"ignorePatterns": [
|
||||||
|
{
|
||||||
|
"pattern": "^http://localhost"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "^https://twitter.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "^https://opensource.org/"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"aliveStatusCodes": [200],
|
||||||
|
"httpHeaders": [
|
||||||
|
{
|
||||||
|
"urls": ["https://docs.github.com/"],
|
||||||
|
"headers": {
|
||||||
|
"Accept-Encoding": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
13
go-libp2p/.github/workflows/release-check.yml
vendored
Normal file
13
go-libp2p/.github/workflows/release-check.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# File managed by web3-bot. DO NOT EDIT.
|
||||||
|
# See https://github.com/protocol/.github/ for details.
|
||||||
|
|
||||||
|
name: Release Checker
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
paths: [ 'version.json' ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-check:
|
||||||
|
uses: protocol/.github/.github/workflows/release-check.yml@master
|
||||||
|
with:
|
||||||
|
go-version: 1.20.x
|
11
go-libp2p/.github/workflows/releaser.yml
vendored
Normal file
11
go-libp2p/.github/workflows/releaser.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# File managed by web3-bot. DO NOT EDIT.
|
||||||
|
# See https://github.com/protocol/.github/ for details.
|
||||||
|
|
||||||
|
name: Releaser
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths: [ 'version.json' ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
releaser:
|
||||||
|
uses: protocol/.github/.github/workflows/releaser.yml@master
|
13
go-libp2p/.github/workflows/stale.yml
vendored
Normal file
13
go-libp2p/.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
name: Close and mark stale issue
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
uses: pl-strflt/.github/.github/workflows/reusable-stale-issue.yml@v0.3
|
12
go-libp2p/.github/workflows/tagpush.yml
vendored
Normal file
12
go-libp2p/.github/workflows/tagpush.yml
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# File managed by web3-bot. DO NOT EDIT.
|
||||||
|
# See https://github.com/protocol/.github/ for details.
|
||||||
|
|
||||||
|
name: Tag Push Checker
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
releaser:
|
||||||
|
uses: protocol/.github/.github/workflows/tagpush.yml@master
|
59
go-libp2p/.github/workflows/upstream.yml
vendored
Normal file
59
go-libp2p/.github/workflows/upstream.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
name: Upstream Test
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
unit:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- 'ubuntu'
|
||||||
|
go:
|
||||||
|
# - '1.15.x'
|
||||||
|
- '1.16.x'
|
||||||
|
upstream:
|
||||||
|
# - 'libp2p/go-libp2p-pubsub' flaky
|
||||||
|
# - 'ipfs/go-bitswap' flaky
|
||||||
|
# - 'libp2p/go-libp2p-kad-dht'
|
||||||
|
- 'libp2p/go-libp2p-daemon'
|
||||||
|
runs-on: ${{ matrix.os }}-latest
|
||||||
|
name: ${{ matrix.upstream }} unit tests (${{ matrix.os }}, Go ${{ matrix.go }})
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go }}
|
||||||
|
- name: Go information
|
||||||
|
run: |
|
||||||
|
go version
|
||||||
|
go env
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: 'libp2p'
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: ${{ matrix.upstream }}
|
||||||
|
path: upstream
|
||||||
|
- name: Patch in new go-libp2p
|
||||||
|
working-directory: upstream
|
||||||
|
run: |
|
||||||
|
go mod edit -replace "github.com/libp2p/go-libp2p=../libp2p"
|
||||||
|
go mod tidy
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: upstream
|
||||||
|
run: go test -v ./...
|
||||||
|
- name: Run tests (32 bit)
|
||||||
|
working-directory: upstream
|
||||||
|
if: ${{ matrix.os != 'macos' }} # can't run 32 bit tests on OSX.
|
||||||
|
env:
|
||||||
|
GOARCH: 386
|
||||||
|
run: go test -v ./...
|
||||||
|
- name: Run tests with race detector
|
||||||
|
working-directory: upstream
|
||||||
|
if: ${{ matrix.os == 'ubuntu' }} # speed things up. Windows and OSX VMs are slow
|
||||||
|
run: go test -v -race ./...
|
6
go-libp2p/.gitignore
vendored
Normal file
6
go-libp2p/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
*.swp
|
||||||
|
.idea
|
||||||
|
*.qlog
|
||||||
|
*.sqlog
|
||||||
|
*.qlog.zst
|
||||||
|
*.sqlog.zst
|
293
go-libp2p/CHANGELOG.md
Normal file
293
go-libp2p/CHANGELOG.md
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
# Table Of Contents <!-- omit in toc -->
|
||||||
|
- [v0.28.0](#v0280)
|
||||||
|
- [v0.27.0](#v0270)
|
||||||
|
- [v0.26.4](#v0264)
|
||||||
|
- [v0.26.3](#v0263)
|
||||||
|
- [v0.26.2](#v0262)
|
||||||
|
- [v0.26.1](#v0261)
|
||||||
|
- [v0.26.0](#v0260)
|
||||||
|
- [v0.25.1](#v0251)
|
||||||
|
- [v0.25.0](#v0250)
|
||||||
|
|
||||||
|
# [v0.28.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.28.0)
|
||||||
|
|
||||||
|
## 🔦 Highlights <!-- omit in toc -->
|
||||||
|
|
||||||
|
### Smart Dialing <!-- omit in toc -->
|
||||||
|
|
||||||
|
This release introduces smart dialing logic. Currently, libp2p dials all addresses of a remote peer in parallel, and
|
||||||
|
aborts all outstanding dials as soon as the first one succeeds.
|
||||||
|
Dialing many addresses in parallel creates a lot of churn on the client side, and unnecessary load on the network and
|
||||||
|
on the server side, and is heavily discouraged by the networking community (see [RFC 8305](https://www.rfc-editor.org/rfc/rfc8305) for example).
|
||||||
|
|
||||||
|
When connecting to a peer we first determine the order to dial its addresses. This ranking logic considers a number of corner cases
|
||||||
|
described in detail in the documentation of the swarm package (`swarm.DefaultDialRanker`).
|
||||||
|
At a high level, this is what happens:
|
||||||
|
* If a peer offers a WebTransport and a QUIC address (on the same IP:port), the QUIC address is preferred.
|
||||||
|
* If a peer has a QUIC and a TCP address, the QUIC address is dialed first. Only if the connection attempt doesn't succeed within 250ms, a TCP connection is started.
|
||||||
|
|
||||||
|
Our measurements on the IPFS network show that for >90% of established libp2p connections, the first connection attempt succeeds,
|
||||||
|
leading a dramatic decrease in the number of aborted connection attempts.
|
||||||
|
|
||||||
|
We also added new metrics to the swarm Grafana dashboard, showing:
|
||||||
|
* The number of connection attempts it took to establish a connection
|
||||||
|
* The delay introduced by the ranking logic
|
||||||
|
|
||||||
|
This feature should be safe to enable for nodes running in data centers and for most nodes in home networks.
|
||||||
|
However, there are some (mostly home and corporate networks) that block all UDP traffic. If enabled, the current implementation
|
||||||
|
of the smart dialing logic will lead to a regression, since it preferes QUIC addresses over TCP addresses. Nodes would still be
|
||||||
|
able to connect, but connection establishment of the TCP connection would be delayed by 250ms.
|
||||||
|
|
||||||
|
In a future release (see #1605 for details), we will introduce a feature called blackhole detection. By observing the outcome of
|
||||||
|
QUIC connection attempts, we can determine if UDP traffic is blocked (namely, if all QUIC connection attempts fail), and stop
|
||||||
|
dialing QUIC in this case altogether. Once this detection logic is in place, smart dialing will be enabled by default.
|
||||||
|
|
||||||
|
### More Metrics! <!-- omit in toc -->
|
||||||
|
Since the last release, we've added metrics for:
|
||||||
|
* [Holepunching](https://github.com/libp2p/go-libp2p/pull/2246)
|
||||||
|
* Smart Dialing (see above)
|
||||||
|
|
||||||
|
### WebTransport <!-- omit in toc -->
|
||||||
|
* [#2251](https://github.com/libp2p/go-libp2p/pull/2251): Infer public WebTransport address from `quic-v1` addresses if both transports are using the same port for both quic-v1 and WebTransport addresses.
|
||||||
|
* [#2271](https://github.com/libp2p/go-libp2p/pull/2271): Only add certificate hashes to WebTransport mulitaddress if listening on WebTransport
|
||||||
|
|
||||||
|
## Housekeeping updates <!-- omit in toc -->
|
||||||
|
* Identify
|
||||||
|
* [#2303](https://github.com/libp2p/go-libp2p/pull/2303): Don't send default protocol version
|
||||||
|
* Prevent polluting PeerStore with local addrs
|
||||||
|
* [#2325](https://github.com/libp2p/go-libp2p/pull/2325): Don't save signed peer records
|
||||||
|
* [#2300](https://github.com/libp2p/go-libp2p/pull/2300): Filter received addresses based on the node's remote address
|
||||||
|
* WebSocket
|
||||||
|
* [#2280](https://github.com/libp2p/go-libp2p/pull/2280): Reverted back to the Gorilla library for WebSocket
|
||||||
|
* NAT
|
||||||
|
* [#2248](https://github.com/libp2p/go-libp2p/pull/2248): Move NAT mapping logic out of the host
|
||||||
|
|
||||||
|
## 🐞 Bugfixes <!-- omit in toc -->
|
||||||
|
* Identify
|
||||||
|
* [Reject signed peer records on peer ID mismatch](https://github.com/libp2p/go-libp2p/commit/8d771355b41297623e05b04a865d029a2522a074)
|
||||||
|
* [#2299](https://github.com/libp2p/go-libp2p/pull/2299): Avoid spuriously pushing updates
|
||||||
|
* Swarm
|
||||||
|
* [#2322](https://github.com/libp2p/go-libp2p/pull/2322): Dedup addresses to dial
|
||||||
|
* [#2284](https://github.com/libp2p/go-libp2p/pull/2284): Change maps with multiaddress keys to use strings
|
||||||
|
* QUIC
|
||||||
|
* [#2262](https://github.com/libp2p/go-libp2p/pull/2262): Prioritize listen connections for reuse
|
||||||
|
* [#2276](https://github.com/libp2p/go-libp2p/pull/2276): Don't panic when quic-go's accept call errors
|
||||||
|
* [#2263](https://github.com/libp2p/go-libp2p/pull/2263): Fix race condition when generating random holepunch packet
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/libp2p/go-libp2p/compare/v0.27.0...v0.28.0
|
||||||
|
|
||||||
|
# [v0.27.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.27.0)
|
||||||
|
|
||||||
|
### Breaking Changes <!-- omit in toc -->
|
||||||
|
|
||||||
|
* The `LocalPrivateKey` method was removed from the `network.Conn` interface. [#2144](https://github.com/libp2p/go-libp2p/pull/2144)
|
||||||
|
|
||||||
|
## 🔦 Highlights <!-- omit in toc -->
|
||||||
|
|
||||||
|
### Additional metrics <!-- omit in toc -->
|
||||||
|
Since the last release, we've added metrics for:
|
||||||
|
* [Relay Service](https://github.com/libp2p/go-libp2p/pull/2154): RequestStatus, RequestCounts, RejectionReasons for Reservation and Connection Requests,
|
||||||
|
ConnectionDuration, BytesTransferred, Relay Service Status.
|
||||||
|
* [Autorelay](https://github.com/libp2p/go-libp2p/pull/2185): relay finder status, reservation request outcomes, current reservations, candidate circuit v2 support, current candidates, relay addresses updated, num relay address, and scheduled work times
|
||||||
|
|
||||||
|
## 🐞 Bugfixes <!-- omit in toc -->
|
||||||
|
|
||||||
|
* autonat: don't change status on dial request refused [2225](https://github.com/libp2p/go-libp2p/pull/2225)
|
||||||
|
* relaysvc: fix flaky TestReachabilityChangeEvent [2215](https://github.com/libp2p/go-libp2p/pull/2215)
|
||||||
|
* basichost: prevent duplicate dials [2196](https://github.com/libp2p/go-libp2p/pull/2196)
|
||||||
|
* websocket: don't set a WSS multiaddr for accepted unencrypted conns [2199](https://github.com/libp2p/go-libp2p/pull/2199)
|
||||||
|
* identify: Fix IdentifyWait when Connected events happen out of order [2173](https://github.com/libp2p/go-libp2p/pull/2173)
|
||||||
|
* circuitv2: cleanup relay service properly [2164](https://github.com/libp2p/go-libp2p/pull/2164)
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/libp2p/go-libp2p/compare/v0.26.4...v0.27.0
|
||||||
|
|
||||||
|
# [v0.26.4](https://github.com/libp2p/go-libp2p/releases/tag/v0.26.4)
|
||||||
|
|
||||||
|
This patch release fixes a busy-looping happening inside AutoRelay on private nodes, see [2208](https://github.com/libp2p/go-libp2p/pull/2208).
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/libp2p/go-libp2p/compare/v0.26.0...v0.26.4
|
||||||
|
|
||||||
|
# [v0.26.3](https://github.com/libp2p/go-libp2p/releases/tag/v0.26.3)
|
||||||
|
|
||||||
|
* rcmgr: fix JSON marshalling of ResourceManagerStat peer map [2156](https://github.com/libp2p/go-libp2p/pull/2156)
|
||||||
|
* websocket: Don't limit message sizes in the websocket reader [2193](https://github.com/libp2p/go-libp2p/pull/2193)
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/libp2p/go-libp2p/compare/v0.26.0...v0.26.3
|
||||||
|
|
||||||
|
# [v0.26.2](https://github.com/libp2p/go-libp2p/releases/tag/v0.26.2)
|
||||||
|
|
||||||
|
This patch release fixes two bugs:
|
||||||
|
* A panic in WebTransport: https://github.com/quic-go/webtransport-go/releases/tag/v0.5.2
|
||||||
|
* Incorrect accounting of accepted connections in the swarm metrics: [#2147](https://github.com/libp2p/go-libp2p/pull/2147)
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/libp2p/go-libp2p/compare/v0.26.0...v0.26.2
|
||||||
|
|
||||||
|
# v0.26.1
|
||||||
|
|
||||||
|
This version was retracted due to errors when publishing the release.
|
||||||
|
|
||||||
|
# [v0.26.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.26.0)
|
||||||
|
|
||||||
|
## 🔦 Highlights <!-- omit in toc -->
|
||||||
|
|
||||||
|
### Circuit Relay Changes <!-- omit in toc -->
|
||||||
|
|
||||||
|
#### [Removed Circuit Relay v1](https://github.com/libp2p/go-libp2p/pull/2107) <!-- omit in toc -->
|
||||||
|
|
||||||
|
We've decided to remove support for Circuit Relay v1 in this release. v1 Relays have been retired a few months ago. Notably, running the Relay v1 protocol was expensive and resulted in only a small number of nodes in the network. Users had to either manually configure these nodes as static relays, or discover them from the DHT.
|
||||||
|
Furthermore, rust-libp2p [has dropped support](https://github.com/libp2p/rust-libp2p/pull/2549) and js-libp2p [is dropping support](https://github.com/libp2p/js-libp2p/pull/1533) for Relay v1.
|
||||||
|
|
||||||
|
Support for Relay v2 was first added in [late 2021 in v0.16.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.16.0). With Circuit Relay v2 it became cheap to run (limited) relays. Public nodes also started the relay service by default. There's now a massive number of Relay v2 nodes on the IPFS network, and they don't advertise their service to the DHT any more. Because there's now so many of these nodes, connecting to just a small number of nodes (e.g. by joining the DHT), a node is statistically guaranteed to connect to some relays.
|
||||||
|
|
||||||
|
#### [Unlimited Relay v2](https://github.com/libp2p/go-libp2p/pull/2125) <!-- omit in toc -->
|
||||||
|
|
||||||
|
In conjunction with removing relay v1, we also added an option to Circuit Relay v2 to disable limits.
|
||||||
|
This done by enabling `WithInfiniteLimits`. When enabled this allows for users to have a drop in replacement for Relay v1 with Relay v2.
|
||||||
|
|
||||||
|
### Additional metrics <!-- omit in toc -->
|
||||||
|
|
||||||
|
Since the last release, we've added additional metrics to different components.
|
||||||
|
Metrics were added to:
|
||||||
|
* [AutoNat](https://github.com/libp2p/go-libp2p/pull/2086): Current Reachability Status and Confidence, Client and Server DialResponses, Server DialRejections. The dashboard is [available here](https://github.com/libp2p/go-libp2p/blob/master/dashboards/autonat/autonat.json).
|
||||||
|
* Swarm:
|
||||||
|
- [Early Muxer Selection](https://github.com/libp2p/go-libp2p/pull/2119): Added early_muxer label indicating whether a connection was established using early muxer selection.
|
||||||
|
- [IP Version](https://github.com/libp2p/go-libp2p/pull/2114): Added ip_version label to connection metrics
|
||||||
|
* Identify:
|
||||||
|
- Metrics for Identify, IdentifyPush, PushesTriggered (https://github.com/libp2p/go-libp2p/pull/2069)
|
||||||
|
- Address Count, Protocol Count, Connection IDPush Support (https://github.com/libp2p/go-libp2p/pull/2126)
|
||||||
|
|
||||||
|
|
||||||
|
We also migrated the metric dashboards to a top-level [dashboards](https://github.com/libp2p/go-libp2p/tree/master/dashboards) directory.
|
||||||
|
|
||||||
|
## 🐞 Bugfixes <!-- omit in toc -->
|
||||||
|
|
||||||
|
### AutoNat <!-- omit in toc -->
|
||||||
|
* [Fixed a bug](https://github.com/libp2p/go-libp2p/issues/2091) where AutoNat would emit events when the observed address has changed even though the node reachability hadn't changed.
|
||||||
|
|
||||||
|
### Relay Manager <!-- omit in toc -->
|
||||||
|
* [Fixed a bug](https://github.com/libp2p/go-libp2p/pull/2093) where the Relay Manager started a new relay even though the previous reachability was `Public` or if a relay already existed.
|
||||||
|
|
||||||
|
### [Stop sending detailed error messages on closing QUIC connections](https://github.com/libp2p/go-libp2p/pull/2112) <!-- omit in toc -->
|
||||||
|
|
||||||
|
Users reported seeing confusing error messages and could not determine the root cause or if the error was from a local or remote peer:
|
||||||
|
|
||||||
|
```{12D... Application error 0x0: conn-27571160: system: cannot reserve inbound connection: resource limit exceeded}```
|
||||||
|
|
||||||
|
This error occurred when a connection had been made with a remote peer but the remote peer dropped the connection (due to it exceeding limits).
|
||||||
|
This was actually an `Application error` emitted by `quic-go` and it was a bug in go-libp2p that we sent the whole message.
|
||||||
|
For now, we decided to stop sending this confusing error message. In the future, we will report such errors via [error codes](https://github.com/libp2p/specs/issues/479).
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/libp2p/go-libp2p/compare/v0.25.1...v0.26.0
|
||||||
|
|
||||||
|
# [v0.25.1](https://github.com/libp2p/go-libp2p/releases/tag/v0.25.1)
|
||||||
|
|
||||||
|
Fix some test-utils used by https://github.com/libp2p/go-libp2p-kad-dht
|
||||||
|
|
||||||
|
* mocknet: Start host in mocknet by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2078
|
||||||
|
* chore: update go-multistream by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2081
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/libp2p/go-libp2p/compare/v0.25.0...v0.25.1
|
||||||
|
|
||||||
|
# [v0.25.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.25.0)
|
||||||
|
|
||||||
|
## 🔦 Highlights <!-- omit in toc -->
|
||||||
|
|
||||||
|
### Metrics <!-- omit in toc -->
|
||||||
|
|
||||||
|
We've started instrumenting the entire stack. In this release, we're adding metrics for:
|
||||||
|
* the swarm: tracking incoming and outgoing connections, transports, security protocols and stream multiplexers in use: (https://github.com/libp2p/go-libp2p/blob/master/dashboards/swarm/swarm.json)
|
||||||
|
* the event bus: tracking how different events are propagated through the stack and to external consumers (https://github.com/libp2p/go-libp2p/blob/master/dashboards/eventbus/eventbus.json)
|
||||||
|
|
||||||
|
Our metrics effort is still ongoing, see https://github.com/libp2p/go-libp2p/issues/1356 for progress. We'll add metrics and dashboards for more libp2p components in a future release.
|
||||||
|
|
||||||
|
### Switching to Google's official Protobuf compiler <!-- omit in toc -->
|
||||||
|
|
||||||
|
So far, we were using GoGo Protobuf to compile our Protobuf definitions to Go code. However, this library was deprecated in October last year: https://twitter.com/awalterschulze/status/1584553056100057088. We [benchmarked](https://github.com/libp2p/go-libp2p/issues/1976#issuecomment-1371527732) serialization and deserialization, and found that it's (only) 20% slower than GoGo. Since the vast majority of go-libp2p's CPU time is spent in code paths other than Protobuf handling, switching to the official compiler seemed like a worthwhile tradeoff.
|
||||||
|
|
||||||
|
### Removal of OpenSSL <!-- omit in toc -->
|
||||||
|
|
||||||
|
Before this release, go-libp2p had an option to use OpenSSL bindings for certain cryptographic primitives, mostly to speed up the generation of signatures and their verification. When building go-libp2p using `go build`, we'd use the standard library crypto packages. OpenSSL was only used when passing in a build tag: `go build -tags openssl`.
|
||||||
|
Maintaining our own fork of the long unmaintained [go-openssl package](https://github.com/libp2p/go-openssl) has proven to place a larger than expected maintenance burden on the libp2p stewards, and when we recently discovered a range of new bugs ([this](https://github.com/libp2p/go-openssl/issues/38) and [this](https://github.com/libp2p/go-libp2p/issues/1892) and [this](https://github.com/libp2p/go-libp2p/issues/1951)), we decided to re-evaluate if this code path is really worth it. The results surprised us, it turns out that:
|
||||||
|
* The Go standard library is faster than OpenSSL for all key types that are not RSA.
|
||||||
|
* Verifying RSA signatures is as fast as Ed25519 signatures using the Go standard library, and even faster in OpenSSL.
|
||||||
|
* Generating RSA signatures is painfully slow, both using Go standard library crypto and using OpenSSL (but even slower using Go standard library).
|
||||||
|
|
||||||
|
Now the good news is, that if your node is not using an RSA key, it will never create any RSA signatures (it might need to verify them though, when it connects to a node that uses RSA keys). If you're concerned about CPU performance, it's a good idea to avoid RSA keys (the same applies to bandwidth, RSA keys are huge!). Even for nodes using RSA keys, it turns out that generating the signatures is not a significant part of their CPU load, as verified by profiling one of Kubo's bootstrap nodes.
|
||||||
|
|
||||||
|
We therefore concluded that it's safe to drop this code path altogether, and thereby reduce our maintenance burden.
|
||||||
|
|
||||||
|
### New Resource Manager types <!-- omit in toc -->
|
||||||
|
|
||||||
|
* Introduces a new type `LimitVal` which can explicitly specify "use default", "unlimited", "block all", as well as any positive number. The zero value of `LimitVal` (the value when you create the object in Go) is "Use default".
|
||||||
|
* The JSON marshalling of this is straightforward.
|
||||||
|
* Introduces a new `ResourceLimits` type which uses `LimitVal` instead of ints so it can encode the above for the resources.
|
||||||
|
* Changes `LimitConfig` to `PartialLimitConfig` and uses `ResourceLimits`. This along with the marshalling changes means you can now marshal the fact that some resource limit is set to block all.
|
||||||
|
* Because the default is to use the defaults, this avoids the footgun of initializing the resource manager with 0 limits (that would block everything).
|
||||||
|
|
||||||
|
In general, you can go from a resource config with defaults to a concrete one with `.Build()`. e.g. `ResourceLimits.Build() => BaseLimit`, `PartialLimitConfig.Build() => ConcreteLimitConfig`, `LimitVal.Build() => int`. See PR #2000 for more details.
|
||||||
|
|
||||||
|
If you're using the defaults for the resource manager, there should be no changes needed.
|
||||||
|
|
||||||
|
### Other Breaking Changes <!-- omit in toc -->
|
||||||
|
|
||||||
|
We've cleaned up our API to consistently use `protocol.ID` for libp2p and application protocols. Specifically, this means that the peer store now uses `protocol.ID`s, and the host's `SetStreamHandler` as well.
|
||||||
|
|
||||||
|
## What's Changed <!-- omit in toc -->
|
||||||
|
* chore: use generic LRU cache by @muXxer in https://github.com/libp2p/go-libp2p/pull/1980
|
||||||
|
* core/crypto: drop all OpenSSL code paths by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1953
|
||||||
|
* add WebTransport to the list of default transports by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1915
|
||||||
|
* identify: remove old code targeting Go 1.17 by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1964
|
||||||
|
* core: remove introspection package by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1978
|
||||||
|
* identify: remove support for Identify Delta by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1975
|
||||||
|
* roadmap: remove optimizations of the TCP-based handshake by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1959
|
||||||
|
* circuitv2: correctly set the transport in the ConnectionState by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1972
|
||||||
|
* switch to Google's Protobuf library, make protobufs compile with go generate by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1979
|
||||||
|
* ci: run go generate as part of the go-check workflow by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1986
|
||||||
|
* ci: use GitHub token to install protoc by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1996
|
||||||
|
* feat: add some users to the readme by @p-shahi in https://github.com/libp2p/go-libp2p/pull/1981
|
||||||
|
* CI: Fast multidimensional Interop tests by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/1991
|
||||||
|
* Fix: Ignore zero values when marshalling Limits. by @ajnavarro in https://github.com/libp2p/go-libp2p/pull/1998
|
||||||
|
* feat: add ci flakiness score to readme by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2002
|
||||||
|
* peerstore: make it possible to use an empty peer ID by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2006
|
||||||
|
* feat: rcmgr: Export resource manager errors by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2008
|
||||||
|
* feat: ci test-plans: Parse test timeout parameter for interop test by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2014
|
||||||
|
* Clean addresses with peer id before adding to addrbook by @sukunrt in https://github.com/libp2p/go-libp2p/pull/2007
|
||||||
|
* Expose muxer ids by @aschmahmann in https://github.com/libp2p/go-libp2p/pull/2012
|
||||||
|
* swarm: add a basic metrics tracer by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1973
|
||||||
|
* consistently use protocol.ID instead of strings by @sukunrt in https://github.com/libp2p/go-libp2p/pull/2004
|
||||||
|
* swarm metrics: fix datasource for dashboard by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2024
|
||||||
|
* chore: remove textual roadmap in favor for Starmap by @p-shahi in https://github.com/libp2p/go-libp2p/pull/2036
|
||||||
|
* rcmgr: *: Always close connscope by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2037
|
||||||
|
* chore: remove license files from the eventbus package by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2042
|
||||||
|
* Migrate to test-plan composite action by @thomaseizinger in https://github.com/libp2p/go-libp2p/pull/2039
|
||||||
|
* use quic-go and webtransport-go from quic-go organization by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2040
|
||||||
|
* holepunch: fix flaky test by not removing holepunch protocol handler by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1948
|
||||||
|
* quic / webtransport: extend test to test dialing a draft-29 and a v1 by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1957
|
||||||
|
* p2p/test: add test for EvtLocalAddressesUpdated event by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2016
|
||||||
|
* quic, tcp: only register Prometheus counters when metrics are enabled by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1971
|
||||||
|
* p2p/test: fix flaky notification test by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2051
|
||||||
|
* quic: disable sending of Version Negotiation packets by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2015
|
||||||
|
* eventbus: add metrics by @sukunrt in https://github.com/libp2p/go-libp2p/pull/2038
|
||||||
|
* metrics: use a single slice pool for all metrics tracer by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2054
|
||||||
|
* webtransport: tidy up some test output by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2053
|
||||||
|
* set names for eventbus event subscriptions by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2057
|
||||||
|
* autorelay: Split libp2p.EnableAutoRelay into 2 functions by @sukunrt in https://github.com/libp2p/go-libp2p/pull/2022
|
||||||
|
* rcmgr: Use prometheus SDK for rcmgr metrics by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2044
|
||||||
|
* websocket: Replace gorilla websocket transport with nhooyr websocket transport by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/1982
|
||||||
|
* rcmgr: add libp2p prefix to all metrics by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2063
|
||||||
|
* chore: git-ignore various flavors of qlog files by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2064
|
||||||
|
* interop: Update interop test to match spec by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2049
|
||||||
|
* chore: update webtransport-go to v0.5.1 by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2072
|
||||||
|
* identify: refactor sending of Identify pushes by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/1984
|
||||||
|
* feat!: rcmgr: Change LimitConfig to use LimitVal type by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2000
|
||||||
|
* p2p/test/quic: use contexts with a timeout for Connect calls by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2070
|
||||||
|
* identify: add some basic metrics by @marten-seemann in https://github.com/libp2p/go-libp2p/pull/2069
|
||||||
|
* chore: Release v0.25.0 by @MarcoPolo in https://github.com/libp2p/go-libp2p/pull/2077
|
||||||
|
|
||||||
|
## New Contributors <!-- omit in toc -->
|
||||||
|
* @muXxer made their first contribution in https://github.com/libp2p/go-libp2p/pull/1980
|
||||||
|
* @ajnavarro made their first contribution in https://github.com/libp2p/go-libp2p/pull/1998
|
||||||
|
* @sukunrt made their first contribution in https://github.com/libp2p/go-libp2p/pull/2007
|
||||||
|
* @thomaseizinger made their first contribution in https://github.com/libp2p/go-libp2p/pull/2039
|
||||||
|
|
||||||
|
**Full Changelog**: https://github.com/libp2p/go-libp2p/compare/v0.24.2...v0.25.0
|
21
go-libp2p/LICENSE
Normal file
21
go-libp2p/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Juan Batiz-Benet
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
100
go-libp2p/README.md
Normal file
100
go-libp2p/README.md
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
|
||||||
|
<h1 align="center">
|
||||||
|
<a href="libp2p.io"><img width="250" src="https://github.com/libp2p/libp2p/blob/master/logo/black-bg-2.png?raw=true" alt="libp2p hex logo" /></a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h3 align="center">The Go implementation of the libp2p Networking Stack.</h3>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="http://protocol.ai"><img src="https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square" /></a>
|
||||||
|
<a href="http://libp2p.io/"><img src="https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square" /></a>
|
||||||
|
<a href="https://pkg.go.dev/github.com/libp2p/go-libp2p"><img src="https://pkg.go.dev/badge/github.com/libp2p/go-libp2p.svg" alt="Go Reference"></a>
|
||||||
|
<a href="https://discuss.libp2p.io"><img src="https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg"/></a>
|
||||||
|
<a href="https://marcopolo.github.io/FlakyTests/"><img src="https://marcopolo.github.io/FlakyTests/current-score.svg"/></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
# Table of Contents <!-- omit in toc -->
|
||||||
|
- [Background](#background)
|
||||||
|
- [Roadmap](#roadmap)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Examples](#examples)
|
||||||
|
- [Contribute](#contribute)
|
||||||
|
- [Supported Go Versions](#supported-go-versions)
|
||||||
|
- [Notable Users](#notable-users)
|
||||||
|
|
||||||
|
# Background
|
||||||
|
|
||||||
|
[libp2p](https://github.com/libp2p/specs) is a networking stack and library modularized out of [The IPFS Project](https://github.com/ipfs/ipfs), and bundled separately for other tools to use.
|
||||||
|
>
|
||||||
|
libp2p is the product of a long, and arduous quest of understanding -- a deep dive into the internet's network stack, and plentiful peer-to-peer protocols from the past. 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 is a "network stack" -- a protocol suite -- that cleanly separates concerns, and enables sophisticated applications to only use the protocols they absolutely need, without giving up interoperability and upgradeability. libp2p grew out of IPFS, but it is built so that lots of people can use it, for lots of different projects.
|
||||||
|
|
||||||
|
To learn more, check out the following resources:
|
||||||
|
- [**Our documentation**](https://docs.libp2p.io)
|
||||||
|
- [**Our community discussion forum**](https://discuss.libp2p.io)
|
||||||
|
- [**The libp2p Specification**](https://github.com/libp2p/specs)
|
||||||
|
- [**js-libp2p implementation**](https://github.com/libp2p/js-libp2p)
|
||||||
|
- [**rust-libp2p implementation**](https://github.com/libp2p/rust-libp2p)
|
||||||
|
|
||||||
|
# Roadmap
|
||||||
|
|
||||||
|
Our roadmap for go-libp2p can be found here: https://github.com/libp2p/go-libp2p/blob/master/ROADMAP.md
|
||||||
|
This document represents current projects the go-libp2p team is focused on and provides an estimation of completion targets. It is a complementary roadmap to the overarching libp2p project roadmap: https://github.com/libp2p/specs/blob/master/ROADMAP.md
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
This repository (`go-libp2p`) serves as the entrypoint to the universe of packages that compose the Go implementation of the libp2p stack.
|
||||||
|
|
||||||
|
You can start using go-libp2p in your Go application simply by adding imports from our repos, e.g.:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/libp2p/go-libp2p"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Examples can be found in the [examples folder](examples).
|
||||||
|
|
||||||
|
|
||||||
|
# Contribute
|
||||||
|
|
||||||
|
go-libp2p is MIT-licensed open source software. We welcome contributions big and small! Take a look at the [community contributing notes](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md). Please make sure to check the [issues](https://github.com/libp2p/go-libp2p/issues). Search the closed ones before reporting things, and help us with the open ones.
|
||||||
|
|
||||||
|
Guidelines:
|
||||||
|
|
||||||
|
- read the [libp2p spec](https://github.com/libp2p/specs)
|
||||||
|
- ask questions or talk about things in our [discussion forums](https://discuss.libp2p.io), or open an [issue](https://github.com/libp2p/go-libp2p/issues) for bug reports, or #libp2p-implementers on [Filecoin slack](https://filecoin.io/slack).
|
||||||
|
- ensure you are able to contribute (no legal issues please -- we use the DCO)
|
||||||
|
- get in touch with @libp2p/go-libp2p-maintainers about how best to contribute
|
||||||
|
- have fun!
|
||||||
|
|
||||||
|
There's a few things you can do right now to help out:
|
||||||
|
- Go through the modules below 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 - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically.
|
||||||
|
- **Perform code reviews**.
|
||||||
|
- **Add tests**. There can never be enough tests.
|
||||||
|
|
||||||
|
## Supported Go Versions
|
||||||
|
|
||||||
|
We test against and support the two most recent major releases of Go. This is
|
||||||
|
informed by Go's own [security policy](https://go.dev/security).
|
||||||
|
|
||||||
|
# Notable Users
|
||||||
|
Some notable users of go-libp2p are:
|
||||||
|
- [Kubo](https://github.com/ipfs/kubo) - The original Go implementation of IPFS
|
||||||
|
- [Lotus](https://github.com/filecoin-project/lotus) - An implementation of the Filecoin protocol
|
||||||
|
- [Drand](https://github.com/drand/drand) - A distributed random beacon daemon
|
||||||
|
- [Prysm](https://github.com/prysmaticlabs/prysm) - An Ethereum Beacon Chain consensus client built by [Prysmatic Labs](https://prysmaticlabs.com/)
|
||||||
|
- [Berty](https://github.com/berty/berty) - An open, secure, offline-first, peer-to-peer and zero trust messaging app.
|
||||||
|
- [Wasp](https://github.com/iotaledger/wasp) - A node that runs IOTA Smart Contracts built by the [IOTA Foundation](https://www.iota.org/)
|
||||||
|
- [Mina](https://github.com/minaprotocol/mina) - A lightweight, constant-sized blockchain that runs zero-knowledge smart contracts
|
||||||
|
- [Polygon Edge](https://github.com/0xPolygon/polygon-edge) - A modular, extensible framework for building Ethereum compatible networks
|
||||||
|
- [Celestia Node](https://github.com/celestiaorg/celestia-node) - The Go implementation of Celestia's data availability nodes
|
||||||
|
- [Status go](https://github.com/status-im/status-go) - Status bindings for go-ethereum, built by [Status.im](https://status.im/)
|
||||||
|
- [Flow](https://github.com/onflow/flow-go) - A blockchain built to support games, apps, and digital assets built by [Dapper Labs](https://www.dapperlabs.com/)
|
||||||
|
- [Swarm Bee](https://github.com/ethersphere/bee) - A client for connecting to the [Swarm network](https://www.ethswarm.org/)
|
||||||
|
- [Elrond Go](https://github.com/multiversx/mx-chain-go) - The Go implementation of the the Elrond network protocol
|
||||||
|
- [Sonr](https://github.com/sonr-io/sonr) - A platform to integrate DID Documents, WebAuthn, and IPFS and manage digital identity and assets.
|
||||||
|
- [EdgeVPN](https://github.com/mudler/edgevpn) - A decentralized, immutable, portable VPN and reverse proxy over p2p.
|
||||||
|
- [Kairos](https://github.com/kairos-io/kairos) - A Kubernetes-focused, Cloud Native Linux meta-distribution.
|
||||||
|
- [Oasis Core](https://github.com/oasisprotocol/oasis-core) - The consensus and runtime layers of the [Oasis protocol](https://oasisprotocol.org/).
|
||||||
|
|
||||||
|
Please open a pull request if you want your project (min. 250 GitHub stars) to be added here.
|
5
go-libp2p/ROADMAP.md
Normal file
5
go-libp2p/ROADMAP.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# go-libp2p roadmap Q4’22/Q1’23
|
||||||
|
|
||||||
|
Please see our roadmap in [Starmap](https://starmap.site/roadmap/github.com/libp2p/go-libp2p/issues/1806#simple)
|
||||||
|
|
||||||
|
Please add any feedback or questions in: https://github.com/libp2p/go-libp2p/issues/1806
|
20
go-libp2p/SECURITY.md
Normal file
20
go-libp2p/SECURITY.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
go-libp2p is still in development. This means that there may be problems in our protocols,
|
||||||
|
or there may be mistakes in our implementations.
|
||||||
|
We take security vulnerabilities very seriously. If you discover a security issue,
|
||||||
|
please bring it to our attention right away!
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you find a vulnerability that may affect live deployments -- for example, by exposing
|
||||||
|
a remote execution exploit -- please [**report privately**](https://github.com/libp2p/go-libp2p/security/advisories/new).
|
||||||
|
Please **DO NOT file a public issue**.
|
||||||
|
|
||||||
|
If the issue is an implementation weakness that cannot be immediately exploited or
|
||||||
|
something not yet deployed, just discuss it openly.
|
||||||
|
If you need assistance, please reach out to [security@libp2p.io](mailto:security@libp2p.io).
|
||||||
|
|
||||||
|
## Reporting a non security bug
|
||||||
|
|
||||||
|
For non-security bugs, please simply file a GitHub [issue](https://github.com/libp2p/go-libp2p/issues/new).
|
483
go-libp2p/config/config.go
Normal file
483
go-libp2p/config/config.go
Normal file
@ -0,0 +1,483 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/connmgr"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p/core/event"
|
||||||
|
"github.com/libp2p/go-libp2p/core/host"
|
||||||
|
"github.com/libp2p/go-libp2p/core/metrics"
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||||
|
"github.com/libp2p/go-libp2p/core/pnet"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
"github.com/libp2p/go-libp2p/core/routing"
|
||||||
|
"github.com/libp2p/go-libp2p/core/sec"
|
||||||
|
"github.com/libp2p/go-libp2p/core/sec/insecure"
|
||||||
|
"github.com/libp2p/go-libp2p/core/transport"
|
||||||
|
"github.com/libp2p/go-libp2p/p2p/host/autonat"
|
||||||
|
"github.com/libp2p/go-libp2p/p2p/host/autorelay"
|
||||||
|
bhost "github.com/libp2p/go-libp2p/p2p/host/basic"
|
||||||
|
blankhost "github.com/libp2p/go-libp2p/p2p/host/blank"
|
||||||
|
"github.com/libp2p/go-libp2p/p2p/host/eventbus"
|
||||||
|
"github.com/libp2p/go-libp2p/p2p/host/peerstore/pstoremem"
|
||||||
|
rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
|
||||||
|
routed "github.com/libp2p/go-libp2p/p2p/host/routed"
|
||||||
|
"github.com/libp2p/go-libp2p/p2p/net/swarm"
|
||||||
|
tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader"
|
||||||
|
circuitv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/client"
|
||||||
|
relayv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
|
||||||
|
"github.com/libp2p/go-libp2p/p2p/protocol/holepunch"
|
||||||
|
"github.com/libp2p/go-libp2p/p2p/transport/quicreuse"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
madns "github.com/multiformats/go-multiaddr-dns"
|
||||||
|
"go.uber.org/fx"
|
||||||
|
"go.uber.org/fx/fxevent"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddrsFactory is a function that takes a set of multiaddrs we're listening on and
|
||||||
|
// returns the set of multiaddrs we should advertise to the network.
|
||||||
|
type AddrsFactory = bhost.AddrsFactory
|
||||||
|
|
||||||
|
// NATManagerC is a NATManager constructor.
|
||||||
|
type NATManagerC func(network.Network) bhost.NATManager
|
||||||
|
|
||||||
|
type RoutingC func(host.Host) (routing.PeerRouting, error)
|
||||||
|
|
||||||
|
// AutoNATConfig defines the AutoNAT behavior for the libp2p host.
|
||||||
|
type AutoNATConfig struct {
|
||||||
|
ForceReachability *network.Reachability
|
||||||
|
EnableService bool
|
||||||
|
ThrottleGlobalLimit int
|
||||||
|
ThrottlePeerLimit int
|
||||||
|
ThrottleInterval time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type Security struct {
|
||||||
|
ID protocol.ID
|
||||||
|
Constructor interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config describes a set of settings for a libp2p node
|
||||||
|
//
|
||||||
|
// This is *not* a stable interface. Use the options defined in the root
|
||||||
|
// package.
|
||||||
|
type Config struct {
|
||||||
|
// UserAgent is the identifier this node will send to other peers when
|
||||||
|
// identifying itself, e.g. via the identify protocol.
|
||||||
|
//
|
||||||
|
// Set it via the UserAgent option function.
|
||||||
|
UserAgent string
|
||||||
|
|
||||||
|
// ProtocolVersion is the protocol version that identifies the family
|
||||||
|
// of protocols used by the peer in the Identify protocol. It is set
|
||||||
|
// using the [ProtocolVersion] option.
|
||||||
|
ProtocolVersion string
|
||||||
|
|
||||||
|
PeerKey crypto.PrivKey
|
||||||
|
|
||||||
|
QUICReuse []fx.Option
|
||||||
|
Transports []fx.Option
|
||||||
|
Muxers []tptu.StreamMuxer
|
||||||
|
SecurityTransports []Security
|
||||||
|
Insecure bool
|
||||||
|
PSK pnet.PSK
|
||||||
|
|
||||||
|
DialTimeout time.Duration
|
||||||
|
|
||||||
|
RelayCustom bool
|
||||||
|
Relay bool // should the relay transport be used
|
||||||
|
|
||||||
|
EnableRelayService bool // should we run a circuitv2 relay (if publicly reachable)
|
||||||
|
RelayServiceOpts []relayv2.Option
|
||||||
|
|
||||||
|
ListenAddrs []ma.Multiaddr
|
||||||
|
AddrsFactory bhost.AddrsFactory
|
||||||
|
ConnectionGater connmgr.ConnectionGater
|
||||||
|
|
||||||
|
ConnManager connmgr.ConnManager
|
||||||
|
ResourceManager network.ResourceManager
|
||||||
|
|
||||||
|
NATManager NATManagerC
|
||||||
|
Peerstore peerstore.Peerstore
|
||||||
|
Reporter metrics.Reporter
|
||||||
|
|
||||||
|
MultiaddrResolver *madns.Resolver
|
||||||
|
|
||||||
|
DisablePing bool
|
||||||
|
|
||||||
|
Routing RoutingC
|
||||||
|
|
||||||
|
EnableAutoRelay bool
|
||||||
|
AutoRelayOpts []autorelay.Option
|
||||||
|
AutoNATConfig
|
||||||
|
|
||||||
|
EnableHolePunching bool
|
||||||
|
HolePunchingOptions []holepunch.Option
|
||||||
|
|
||||||
|
DisableMetrics bool
|
||||||
|
PrometheusRegisterer prometheus.Registerer
|
||||||
|
|
||||||
|
DialRanker network.DialRanker
|
||||||
|
|
||||||
|
SwarmOpts []swarm.Option
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) makeSwarm(eventBus event.Bus, enableMetrics bool) (*swarm.Swarm, error) {
|
||||||
|
if cfg.Peerstore == nil {
|
||||||
|
return nil, fmt.Errorf("no peerstore specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check this early. Prevents us from even *starting* without verifying this.
|
||||||
|
if pnet.ForcePrivateNetwork && len(cfg.PSK) == 0 {
|
||||||
|
log.Error("tried to create a libp2p node with no Private" +
|
||||||
|
" Network Protector but usage of Private Networks" +
|
||||||
|
" is forced by the environment")
|
||||||
|
// Note: This is *also* checked the upgrader itself, so it'll be
|
||||||
|
// enforced even *if* you don't use the libp2p constructor.
|
||||||
|
return nil, pnet.ErrNotInPrivateNetwork
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.PeerKey == nil {
|
||||||
|
return nil, fmt.Errorf("no peer key specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Obtain Peer ID from public key
|
||||||
|
pid, err := peer.IDFromPublicKey(cfg.PeerKey.GetPublic())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cfg.Peerstore.AddPrivKey(pid, cfg.PeerKey); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := cfg.Peerstore.AddPubKey(pid, cfg.PeerKey.GetPublic()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := cfg.SwarmOpts
|
||||||
|
if cfg.Reporter != nil {
|
||||||
|
opts = append(opts, swarm.WithMetrics(cfg.Reporter))
|
||||||
|
}
|
||||||
|
if cfg.ConnectionGater != nil {
|
||||||
|
opts = append(opts, swarm.WithConnectionGater(cfg.ConnectionGater))
|
||||||
|
}
|
||||||
|
if cfg.DialTimeout != 0 {
|
||||||
|
opts = append(opts, swarm.WithDialTimeout(cfg.DialTimeout))
|
||||||
|
}
|
||||||
|
if cfg.ResourceManager != nil {
|
||||||
|
opts = append(opts, swarm.WithResourceManager(cfg.ResourceManager))
|
||||||
|
}
|
||||||
|
if cfg.MultiaddrResolver != nil {
|
||||||
|
opts = append(opts, swarm.WithMultiaddrResolver(cfg.MultiaddrResolver))
|
||||||
|
}
|
||||||
|
if cfg.DialRanker != nil {
|
||||||
|
opts = append(opts, swarm.WithDialRanker(cfg.DialRanker))
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableMetrics {
|
||||||
|
opts = append(opts,
|
||||||
|
swarm.WithMetricsTracer(swarm.NewMetricsTracer(swarm.WithRegisterer(cfg.PrometheusRegisterer))))
|
||||||
|
}
|
||||||
|
// TODO: Make the swarm implementation configurable.
|
||||||
|
return swarm.NewSwarm(pid, cfg.Peerstore, eventBus, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) addTransports(h host.Host) error {
|
||||||
|
swrm, ok := h.Network().(transport.TransportNetwork)
|
||||||
|
if !ok {
|
||||||
|
// Should probably skip this if no transports.
|
||||||
|
return fmt.Errorf("swarm does not support transports")
|
||||||
|
}
|
||||||
|
|
||||||
|
fxopts := []fx.Option{
|
||||||
|
fx.WithLogger(func() fxevent.Logger { return getFXLogger() }),
|
||||||
|
fx.Provide(fx.Annotate(tptu.New, fx.ParamTags(`name:"security"`))),
|
||||||
|
fx.Supply(cfg.Muxers),
|
||||||
|
fx.Supply(h.ID()),
|
||||||
|
fx.Provide(func() host.Host { return h }),
|
||||||
|
fx.Provide(func() crypto.PrivKey { return h.Peerstore().PrivKey(h.ID()) }),
|
||||||
|
fx.Provide(func() connmgr.ConnectionGater { return cfg.ConnectionGater }),
|
||||||
|
fx.Provide(func() pnet.PSK { return cfg.PSK }),
|
||||||
|
fx.Provide(func() network.ResourceManager { return cfg.ResourceManager }),
|
||||||
|
fx.Provide(func() *madns.Resolver { return cfg.MultiaddrResolver }),
|
||||||
|
}
|
||||||
|
fxopts = append(fxopts, cfg.Transports...)
|
||||||
|
if cfg.Insecure {
|
||||||
|
fxopts = append(fxopts,
|
||||||
|
fx.Provide(
|
||||||
|
fx.Annotate(
|
||||||
|
func(id peer.ID, priv crypto.PrivKey) []sec.SecureTransport {
|
||||||
|
return []sec.SecureTransport{insecure.NewWithIdentity(insecure.ID, id, priv)}
|
||||||
|
},
|
||||||
|
fx.ResultTags(`name:"security"`),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// fx groups are unordered, but we need to preserve the order of the security transports
|
||||||
|
// First of all, we construct the security transports that are needed,
|
||||||
|
// and save them to a group call security_unordered.
|
||||||
|
for _, s := range cfg.SecurityTransports {
|
||||||
|
fxName := fmt.Sprintf(`name:"security_%s"`, s.ID)
|
||||||
|
fxopts = append(fxopts, fx.Supply(fx.Annotate(s.ID, fx.ResultTags(fxName))))
|
||||||
|
fxopts = append(fxopts,
|
||||||
|
fx.Provide(fx.Annotate(
|
||||||
|
s.Constructor,
|
||||||
|
fx.ParamTags(fxName),
|
||||||
|
fx.As(new(sec.SecureTransport)),
|
||||||
|
fx.ResultTags(`group:"security_unordered"`),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Then we consume the group security_unordered, and order them by the user's preference.
|
||||||
|
fxopts = append(fxopts, fx.Provide(
|
||||||
|
fx.Annotate(
|
||||||
|
func(secs []sec.SecureTransport) ([]sec.SecureTransport, error) {
|
||||||
|
if len(secs) != len(cfg.SecurityTransports) {
|
||||||
|
return nil, errors.New("inconsistent length for security transports")
|
||||||
|
}
|
||||||
|
t := make([]sec.SecureTransport, 0, len(secs))
|
||||||
|
for _, s := range cfg.SecurityTransports {
|
||||||
|
for _, st := range secs {
|
||||||
|
if s.ID != st.ID() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
t = append(t, st)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
},
|
||||||
|
fx.ParamTags(`group:"security_unordered"`),
|
||||||
|
fx.ResultTags(`name:"security"`),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fxopts = append(fxopts, fx.Provide(PrivKeyToStatelessResetKey))
|
||||||
|
if cfg.QUICReuse != nil {
|
||||||
|
fxopts = append(fxopts, cfg.QUICReuse...)
|
||||||
|
} else {
|
||||||
|
fxopts = append(fxopts, fx.Provide(quicreuse.NewConnManager)) // TODO: close the ConnManager when shutting down the node
|
||||||
|
}
|
||||||
|
|
||||||
|
fxopts = append(fxopts, fx.Invoke(
|
||||||
|
fx.Annotate(
|
||||||
|
func(tpts []transport.Transport) error {
|
||||||
|
for _, t := range tpts {
|
||||||
|
if err := swrm.AddTransport(t); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
fx.ParamTags(`group:"transport"`),
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
if cfg.Relay {
|
||||||
|
fxopts = append(fxopts, fx.Invoke(circuitv2.AddTransport))
|
||||||
|
}
|
||||||
|
app := fx.New(fxopts...)
|
||||||
|
if err := app.Err(); err != nil {
|
||||||
|
h.Close()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNode constructs a new libp2p Host from the Config.
|
||||||
|
//
|
||||||
|
// This function consumes the config. Do not reuse it (really!).
|
||||||
|
func (cfg *Config) NewNode() (host.Host, error) {
|
||||||
|
eventBus := eventbus.NewBus(eventbus.WithMetricsTracer(eventbus.NewMetricsTracer(eventbus.WithRegisterer(cfg.PrometheusRegisterer))))
|
||||||
|
swrm, err := cfg.makeSwarm(eventBus, !cfg.DisableMetrics)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cfg.DisableMetrics {
|
||||||
|
rcmgr.MustRegisterWith(cfg.PrometheusRegisterer)
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := bhost.NewHost(swrm, &bhost.HostOpts{
|
||||||
|
EventBus: eventBus,
|
||||||
|
ConnManager: cfg.ConnManager,
|
||||||
|
AddrsFactory: cfg.AddrsFactory,
|
||||||
|
NATManager: cfg.NATManager,
|
||||||
|
EnablePing: !cfg.DisablePing,
|
||||||
|
UserAgent: cfg.UserAgent,
|
||||||
|
ProtocolVersion: cfg.ProtocolVersion,
|
||||||
|
EnableHolePunching: cfg.EnableHolePunching,
|
||||||
|
HolePunchingOptions: cfg.HolePunchingOptions,
|
||||||
|
EnableRelayService: cfg.EnableRelayService,
|
||||||
|
RelayServiceOpts: cfg.RelayServiceOpts,
|
||||||
|
EnableMetrics: !cfg.DisableMetrics,
|
||||||
|
PrometheusRegisterer: cfg.PrometheusRegisterer,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
swrm.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Relay {
|
||||||
|
// If we've enabled the relay, we should filter out relay
|
||||||
|
// addresses by default.
|
||||||
|
//
|
||||||
|
// TODO: We shouldn't be doing this here.
|
||||||
|
oldFactory := h.AddrsFactory
|
||||||
|
h.AddrsFactory = func(addrs []ma.Multiaddr) []ma.Multiaddr {
|
||||||
|
return oldFactory(autorelay.Filter(addrs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cfg.addTransports(h); err != nil {
|
||||||
|
h.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This method succeeds if listening on one address succeeds. We
|
||||||
|
// should probably fail if listening on *any* addr fails.
|
||||||
|
if err := h.Network().Listen(cfg.ListenAddrs...); err != nil {
|
||||||
|
h.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure routing and autorelay
|
||||||
|
var router routing.PeerRouting
|
||||||
|
if cfg.Routing != nil {
|
||||||
|
router, err = cfg.Routing(h)
|
||||||
|
if err != nil {
|
||||||
|
h.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: h.AddrsFactory may be changed by relayFinder, but non-relay version is
|
||||||
|
// used by AutoNAT below.
|
||||||
|
var ar *autorelay.AutoRelay
|
||||||
|
addrF := h.AddrsFactory
|
||||||
|
if cfg.EnableAutoRelay {
|
||||||
|
if !cfg.Relay {
|
||||||
|
h.Close()
|
||||||
|
return nil, fmt.Errorf("cannot enable autorelay; relay is not enabled")
|
||||||
|
}
|
||||||
|
if !cfg.DisableMetrics {
|
||||||
|
mt := autorelay.WithMetricsTracer(
|
||||||
|
autorelay.NewMetricsTracer(autorelay.WithRegisterer(cfg.PrometheusRegisterer)))
|
||||||
|
mtOpts := []autorelay.Option{mt}
|
||||||
|
cfg.AutoRelayOpts = append(mtOpts, cfg.AutoRelayOpts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
ar, err = autorelay.NewAutoRelay(h, cfg.AutoRelayOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
autonatOpts := []autonat.Option{
|
||||||
|
autonat.UsingAddresses(func() []ma.Multiaddr {
|
||||||
|
return addrF(h.AllAddrs())
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
if !cfg.DisableMetrics {
|
||||||
|
autonatOpts = append(autonatOpts,
|
||||||
|
autonat.WithMetricsTracer(
|
||||||
|
autonat.NewMetricsTracer(autonat.WithRegisterer(cfg.PrometheusRegisterer))))
|
||||||
|
}
|
||||||
|
if cfg.AutoNATConfig.ThrottleInterval != 0 {
|
||||||
|
autonatOpts = append(autonatOpts,
|
||||||
|
autonat.WithThrottling(cfg.AutoNATConfig.ThrottleGlobalLimit, cfg.AutoNATConfig.ThrottleInterval),
|
||||||
|
autonat.WithPeerThrottling(cfg.AutoNATConfig.ThrottlePeerLimit))
|
||||||
|
}
|
||||||
|
if cfg.AutoNATConfig.EnableService {
|
||||||
|
autonatPrivKey, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ps, err := pstoremem.NewPeerstore()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pull out the pieces of the config that we _actually_ care about.
|
||||||
|
// Specifically, don't set up things like autorelay, listeners,
|
||||||
|
// identify, etc.
|
||||||
|
autoNatCfg := Config{
|
||||||
|
Transports: cfg.Transports,
|
||||||
|
Muxers: cfg.Muxers,
|
||||||
|
SecurityTransports: cfg.SecurityTransports,
|
||||||
|
Insecure: cfg.Insecure,
|
||||||
|
PSK: cfg.PSK,
|
||||||
|
ConnectionGater: cfg.ConnectionGater,
|
||||||
|
Reporter: cfg.Reporter,
|
||||||
|
PeerKey: autonatPrivKey,
|
||||||
|
Peerstore: ps,
|
||||||
|
DialRanker: swarm.NoDelayDialRanker,
|
||||||
|
}
|
||||||
|
|
||||||
|
dialer, err := autoNatCfg.makeSwarm(eventbus.NewBus(), false)
|
||||||
|
if err != nil {
|
||||||
|
h.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dialerHost := blankhost.NewBlankHost(dialer)
|
||||||
|
if err := autoNatCfg.addTransports(dialerHost); err != nil {
|
||||||
|
dialerHost.Close()
|
||||||
|
h.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// NOTE: We're dropping the blank host here but that's fine. It
|
||||||
|
// doesn't really _do_ anything and doesn't even need to be
|
||||||
|
// closed (as long as we close the underlying network).
|
||||||
|
autonatOpts = append(autonatOpts, autonat.EnableService(dialerHost.Network()))
|
||||||
|
}
|
||||||
|
if cfg.AutoNATConfig.ForceReachability != nil {
|
||||||
|
autonatOpts = append(autonatOpts, autonat.WithReachability(*cfg.AutoNATConfig.ForceReachability))
|
||||||
|
}
|
||||||
|
|
||||||
|
autonat, err := autonat.New(h, autonatOpts...)
|
||||||
|
if err != nil {
|
||||||
|
h.Close()
|
||||||
|
return nil, fmt.Errorf("cannot enable autorelay; autonat failed to start: %v", err)
|
||||||
|
}
|
||||||
|
h.SetAutoNat(autonat)
|
||||||
|
|
||||||
|
// start the host background tasks
|
||||||
|
h.Start()
|
||||||
|
|
||||||
|
var ho host.Host
|
||||||
|
ho = h
|
||||||
|
if router != nil {
|
||||||
|
ho = routed.Wrap(h, router)
|
||||||
|
}
|
||||||
|
if ar != nil {
|
||||||
|
arh := autorelay.NewAutoRelayHost(ho, ar)
|
||||||
|
arh.Start()
|
||||||
|
ho = arh
|
||||||
|
}
|
||||||
|
return ho, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option is a libp2p config option that can be given to the libp2p constructor
|
||||||
|
// (`libp2p.New`).
|
||||||
|
type Option func(cfg *Config) error
|
||||||
|
|
||||||
|
// Apply applies the given options to the config, returning the first error
|
||||||
|
// encountered (if any).
|
||||||
|
func (cfg *Config) Apply(opts ...Option) error {
|
||||||
|
for _, opt := range opts {
|
||||||
|
if opt == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := opt(cfg); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
23
go-libp2p/config/config_test.go
Normal file
23
go-libp2p/config/config_test.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNilOption(t *testing.T) {
|
||||||
|
var cfg Config
|
||||||
|
optsRun := 0
|
||||||
|
opt := func(c *Config) error {
|
||||||
|
optsRun++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := cfg.Apply(nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := cfg.Apply(opt, nil, nil, opt, opt, nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if optsRun != 3 {
|
||||||
|
t.Fatalf("expected to have handled 3 options, handled %d", optsRun)
|
||||||
|
}
|
||||||
|
}
|
28
go-libp2p/config/log.go
Normal file
28
go-libp2p/config/log.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
"go.uber.org/fx/fxevent"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = logging.Logger("p2p-config")
|
||||||
|
|
||||||
|
var (
|
||||||
|
fxLogger fxevent.Logger
|
||||||
|
logInitOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
type fxLogWriter struct{}
|
||||||
|
|
||||||
|
func (l *fxLogWriter) Write(b []byte) (int, error) {
|
||||||
|
log.Debug(strings.TrimSuffix(string(b), "\n"))
|
||||||
|
return len(b), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFXLogger() fxevent.Logger {
|
||||||
|
logInitOnce.Do(func() { fxLogger = &fxevent.ConsoleLogger{W: &fxLogWriter{}} })
|
||||||
|
return fxLogger
|
||||||
|
}
|
27
go-libp2p/config/quic_stateless_reset.go
Normal file
27
go-libp2p/config/quic_stateless_reset.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/hkdf"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const statelessResetKeyInfo = "libp2p quic stateless reset key"
|
||||||
|
|
||||||
|
func PrivKeyToStatelessResetKey(key crypto.PrivKey) (quic.StatelessResetKey, error) {
|
||||||
|
var statelessResetKey quic.StatelessResetKey
|
||||||
|
keyBytes, err := key.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return statelessResetKey, err
|
||||||
|
}
|
||||||
|
keyReader := hkdf.New(sha256.New, keyBytes, nil, []byte(statelessResetKeyInfo))
|
||||||
|
if _, err := io.ReadFull(keyReader, statelessResetKey[:]); err != nil {
|
||||||
|
return statelessResetKey, err
|
||||||
|
}
|
||||||
|
return statelessResetKey, nil
|
||||||
|
}
|
51
go-libp2p/core/alias.go
Normal file
51
go-libp2p/core/alias.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// Package core provides convenient access to foundational, central go-libp2p primitives via type aliases.
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/libp2p/go-libp2p/core/host"
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Multiaddr aliases the Multiaddr type from github.com/multiformats/go-multiaddr.
|
||||||
|
//
|
||||||
|
// Refer to the docs on that type for more info.
|
||||||
|
type Multiaddr = multiaddr.Multiaddr
|
||||||
|
|
||||||
|
// PeerID aliases peer.ID.
|
||||||
|
//
|
||||||
|
// Refer to the docs on that type for more info.
|
||||||
|
type PeerID = peer.ID
|
||||||
|
|
||||||
|
// ProtocolID aliases protocol.ID.
|
||||||
|
//
|
||||||
|
// Refer to the docs on that type for more info.
|
||||||
|
type ProtocolID = protocol.ID
|
||||||
|
|
||||||
|
// PeerAddrInfo aliases peer.AddrInfo.
|
||||||
|
//
|
||||||
|
// Refer to the docs on that type for more info.
|
||||||
|
type PeerAddrInfo = peer.AddrInfo
|
||||||
|
|
||||||
|
// Host aliases host.Host.
|
||||||
|
//
|
||||||
|
// Refer to the docs on that type for more info.
|
||||||
|
type Host = host.Host
|
||||||
|
|
||||||
|
// Network aliases network.Network.
|
||||||
|
//
|
||||||
|
// Refer to the docs on that type for more info.
|
||||||
|
type Network = network.Network
|
||||||
|
|
||||||
|
// Conn aliases network.Conn.
|
||||||
|
//
|
||||||
|
// Refer to the docs on that type for more info.
|
||||||
|
type Conn = network.Conn
|
||||||
|
|
||||||
|
// Stream aliases network.Stream.
|
||||||
|
//
|
||||||
|
// Refer to the docs on that type for more info.
|
||||||
|
type Stream = network.Stream
|
57
go-libp2p/core/canonicallog/canonicallog.go
Normal file
57
go-libp2p/core/canonicallog/canonicallog.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package canonicallog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
manet "github.com/multiformats/go-multiaddr/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log = logging.WithSkip(logging.Logger("canonical-log"), 1)
|
||||||
|
|
||||||
|
// LogMisbehavingPeer is the canonical way to log a misbehaving peer.
|
||||||
|
// Protocols should use this to identify a misbehaving peer to allow the end
|
||||||
|
// user to easily identify these nodes across protocols and libp2p.
|
||||||
|
func LogMisbehavingPeer(p peer.ID, peerAddr multiaddr.Multiaddr, component string, err error, msg string) {
|
||||||
|
log.Warnf("CANONICAL_MISBEHAVING_PEER: peer=%s addr=%s component=%s err=%q msg=%q", p, peerAddr.String(), component, err, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogMisbehavingPeerNetAddr is the canonical way to log a misbehaving peer.
|
||||||
|
// Protocols should use this to identify a misbehaving peer to allow the end
|
||||||
|
// user to easily identify these nodes across protocols and libp2p.
|
||||||
|
func LogMisbehavingPeerNetAddr(p peer.ID, peerAddr net.Addr, component string, originalErr error, msg string) {
|
||||||
|
ma, err := manet.FromNetAddr(peerAddr)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("CANONICAL_MISBEHAVING_PEER: peer=%s net_addr=%s component=%s err=%q msg=%q", p, peerAddr.String(), component, originalErr, msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
LogMisbehavingPeer(p, ma, component, originalErr, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogPeerStatus logs any useful information about a peer. It takes in a sample
|
||||||
|
// rate and will only log one in every sampleRate messages (randomly). This is
|
||||||
|
// useful in surfacing events that are normal in isolation, but may be abnormal
|
||||||
|
// in large quantities. For example, a successful connection from an IP address
|
||||||
|
// is normal. 10,000 connections from that same IP address is not normal. libp2p
|
||||||
|
// itself does nothing besides emitting this log. Hook this up to another tool
|
||||||
|
// like fail2ban to action on the log.
|
||||||
|
func LogPeerStatus(sampleRate int, p peer.ID, peerAddr multiaddr.Multiaddr, keyVals ...string) {
|
||||||
|
if rand.Intn(sampleRate) == 0 {
|
||||||
|
keyValsStr := strings.Builder{}
|
||||||
|
for i, kOrV := range keyVals {
|
||||||
|
if i%2 == 0 {
|
||||||
|
fmt.Fprintf(&keyValsStr, " %v=", kOrV)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(&keyValsStr, "%q", kOrV)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Infof("CANONICAL_PEER_STATUS: peer=%s addr=%s sample_rate=%v%s", p, peerAddr.String(), sampleRate, keyValsStr.String())
|
||||||
|
}
|
||||||
|
}
|
26
go-libp2p/core/canonicallog/canonicallog_test.go
Normal file
26
go-libp2p/core/canonicallog/canonicallog_test.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package canonicallog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/test"
|
||||||
|
|
||||||
|
logging "github.com/ipfs/go-log/v2"
|
||||||
|
"github.com/multiformats/go-multiaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLogs(t *testing.T) {
|
||||||
|
err := logging.SetLogLevel("canonical-log", "info")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
LogMisbehavingPeer(test.RandPeerIDFatal(t), multiaddr.StringCast("/ip4/1.2.3.4"), "somecomponent", fmt.Errorf("something"), "hi")
|
||||||
|
|
||||||
|
netAddr := &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 80}
|
||||||
|
LogMisbehavingPeerNetAddr(test.RandPeerIDFatal(t), netAddr, "somecomponent", fmt.Errorf("something"), "hello \"world\"")
|
||||||
|
|
||||||
|
LogPeerStatus(1, test.RandPeerIDFatal(t), multiaddr.StringCast("/ip4/1.2.3.4"), "extra", "info")
|
||||||
|
}
|
109
go-libp2p/core/connmgr/decay.go
Normal file
109
go-libp2p/core/connmgr/decay.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package connmgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Decayer is implemented by connection managers supporting decaying tags. A
|
||||||
|
// decaying tag is one whose value automatically decays over time.
|
||||||
|
//
|
||||||
|
// The actual application of the decay behaviour is encapsulated in a
|
||||||
|
// user-provided decaying function (DecayFn). The function is called on every
|
||||||
|
// tick (determined by the interval parameter), and returns either the new value
|
||||||
|
// of the tag, or whether it should be erased altogether.
|
||||||
|
//
|
||||||
|
// We do not set values on a decaying tag. Rather, we "bump" decaying tags by a
|
||||||
|
// delta. This calls the BumpFn with the old value and the delta, to determine
|
||||||
|
// the new value.
|
||||||
|
//
|
||||||
|
// Such a pluggable design affords a great deal of flexibility and versatility.
|
||||||
|
// Behaviours that are straightforward to implement include:
|
||||||
|
//
|
||||||
|
// - Decay a tag by -1, or by half its current value, on every tick.
|
||||||
|
// - Every time a value is bumped, sum it to its current value.
|
||||||
|
// - Exponentially boost a score with every bump.
|
||||||
|
// - Sum the incoming score, but keep it within min, max bounds.
|
||||||
|
//
|
||||||
|
// Commonly used DecayFns and BumpFns are provided in this package.
|
||||||
|
type Decayer interface {
|
||||||
|
io.Closer
|
||||||
|
|
||||||
|
// RegisterDecayingTag creates and registers a new decaying tag, if and only
|
||||||
|
// if a tag with the supplied name doesn't exist yet. Otherwise, an error is
|
||||||
|
// returned.
|
||||||
|
//
|
||||||
|
// The caller provides the interval at which the tag is refreshed, as well
|
||||||
|
// as the decay function and the bump function. Refer to godocs on DecayFn
|
||||||
|
// and BumpFn for more info.
|
||||||
|
RegisterDecayingTag(name string, interval time.Duration, decayFn DecayFn, bumpFn BumpFn) (DecayingTag, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecayFn applies a decay to the peer's score. The implementation must call
|
||||||
|
// DecayFn at the interval supplied when registering the tag.
|
||||||
|
//
|
||||||
|
// It receives a copy of the decaying value, and returns the score after
|
||||||
|
// applying the decay, as well as a flag to signal if the tag should be erased.
|
||||||
|
type DecayFn func(value DecayingValue) (after int, rm bool)
|
||||||
|
|
||||||
|
// BumpFn applies a delta onto an existing score, and returns the new score.
|
||||||
|
//
|
||||||
|
// Non-trivial bump functions include exponential boosting, moving averages,
|
||||||
|
// ceilings, etc.
|
||||||
|
type BumpFn func(value DecayingValue, delta int) (after int)
|
||||||
|
|
||||||
|
// DecayingTag represents a decaying tag. The tag is a long-lived general
|
||||||
|
// object, used to operate on tag values for peers.
|
||||||
|
type DecayingTag interface {
|
||||||
|
// Name returns the name of the tag.
|
||||||
|
Name() string
|
||||||
|
|
||||||
|
// Interval is the effective interval at which this tag will tick. Upon
|
||||||
|
// registration, the desired interval may be overwritten depending on the
|
||||||
|
// decayer's resolution, and this method allows you to obtain the effective
|
||||||
|
// interval.
|
||||||
|
Interval() time.Duration
|
||||||
|
|
||||||
|
// Bump applies a delta to a tag value, calling its bump function. The bump
|
||||||
|
// will be applied asynchronously, and a non-nil error indicates a fault
|
||||||
|
// when queuing.
|
||||||
|
Bump(peer peer.ID, delta int) error
|
||||||
|
|
||||||
|
// Remove removes a decaying tag from a peer. The removal will be applied
|
||||||
|
// asynchronously, and a non-nil error indicates a fault when queuing.
|
||||||
|
Remove(peer peer.ID) error
|
||||||
|
|
||||||
|
// Close closes a decaying tag. The Decayer will stop tracking this tag,
|
||||||
|
// and the state of all peers in the Connection Manager holding this tag
|
||||||
|
// will be updated.
|
||||||
|
//
|
||||||
|
// The deletion is performed asynchronously.
|
||||||
|
//
|
||||||
|
// Once deleted, a tag should not be used, and further calls to Bump/Remove
|
||||||
|
// will error.
|
||||||
|
//
|
||||||
|
// Duplicate calls to Remove will not return errors, but a failure to queue
|
||||||
|
// the first actual removal, will (e.g. when the system is backlogged).
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecayingValue represents a value for a decaying tag.
|
||||||
|
type DecayingValue struct {
|
||||||
|
// Tag points to the tag this value belongs to.
|
||||||
|
Tag DecayingTag
|
||||||
|
|
||||||
|
// Peer is the peer ID to whom this value is associated.
|
||||||
|
Peer peer.ID
|
||||||
|
|
||||||
|
// Added is the timestamp when this value was added for the first time for
|
||||||
|
// a tag and a peer.
|
||||||
|
Added time.Time
|
||||||
|
|
||||||
|
// LastVisit is the timestamp of the last visit.
|
||||||
|
LastVisit time.Time
|
||||||
|
|
||||||
|
// Value is the current value of the tag.
|
||||||
|
Value int
|
||||||
|
}
|
89
go-libp2p/core/connmgr/gater.go
Normal file
89
go-libp2p/core/connmgr/gater.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package connmgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/control"
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConnectionGater can be implemented by a type that supports active
|
||||||
|
// inbound or outbound connection gating.
|
||||||
|
//
|
||||||
|
// ConnectionGaters are active, whereas ConnManagers tend to be passive.
|
||||||
|
//
|
||||||
|
// A ConnectionGater will be consulted during different states in the lifecycle
|
||||||
|
// of a connection being established/upgraded. Specific functions will be called
|
||||||
|
// throughout the process, to allow you to intercept the connection at that stage.
|
||||||
|
//
|
||||||
|
// InterceptPeerDial is called on an imminent outbound peer dial request, prior
|
||||||
|
// to the addresses of that peer being available/resolved. Blocking connections
|
||||||
|
// at this stage is typical for blacklisting scenarios.
|
||||||
|
//
|
||||||
|
// InterceptAddrDial is called on an imminent outbound dial to a peer on a
|
||||||
|
// particular address. Blocking connections at this stage is typical for
|
||||||
|
// address filtering.
|
||||||
|
//
|
||||||
|
// InterceptAccept is called as soon as a transport listener receives an
|
||||||
|
// inbound connection request, before any upgrade takes place. Transports who
|
||||||
|
// accept already secure and/or multiplexed connections (e.g. possibly QUIC)
|
||||||
|
// MUST call this method regardless, for correctness/consistency.
|
||||||
|
//
|
||||||
|
// InterceptSecured is called for both inbound and outbound connections,
|
||||||
|
// after a security handshake has taken place and we've authenticated the peer.
|
||||||
|
//
|
||||||
|
// InterceptUpgraded is called for inbound and outbound connections, after
|
||||||
|
// libp2p has finished upgrading the connection entirely to a secure,
|
||||||
|
// multiplexed channel.
|
||||||
|
//
|
||||||
|
// This interface can be used to implement *strict/active* connection management
|
||||||
|
// policies, such as hard limiting of connections once a maximum count has been
|
||||||
|
// reached, maintaining a peer blacklist, or limiting connections by transport
|
||||||
|
// quotas.
|
||||||
|
//
|
||||||
|
// EXPERIMENTAL: a DISCONNECT protocol/message will be supported in the future.
|
||||||
|
// This allows gaters and other components to communicate the intention behind
|
||||||
|
// a connection closure, to curtail potential reconnection attempts.
|
||||||
|
//
|
||||||
|
// For now, InterceptUpgraded can return a non-zero DisconnectReason when
|
||||||
|
// blocking a connection, but this interface is likely to change in the future
|
||||||
|
// as we solidify this feature. The reason why only this method can handle
|
||||||
|
// DisconnectReasons is that we require stream multiplexing capability to open a
|
||||||
|
// control protocol stream to transmit the message.
|
||||||
|
type ConnectionGater interface {
|
||||||
|
// InterceptPeerDial tests whether we're permitted to Dial the specified peer.
|
||||||
|
//
|
||||||
|
// This is called by the network.Network implementation when dialling a peer.
|
||||||
|
InterceptPeerDial(p peer.ID) (allow bool)
|
||||||
|
|
||||||
|
// InterceptAddrDial tests whether we're permitted to dial the specified
|
||||||
|
// multiaddr for the given peer.
|
||||||
|
//
|
||||||
|
// This is called by the network.Network implementation after it has
|
||||||
|
// resolved the peer's addrs, and prior to dialling each.
|
||||||
|
InterceptAddrDial(peer.ID, ma.Multiaddr) (allow bool)
|
||||||
|
|
||||||
|
// InterceptAccept tests whether an incipient inbound connection is allowed.
|
||||||
|
//
|
||||||
|
// This is called by the upgrader, or by the transport directly (e.g. QUIC,
|
||||||
|
// Bluetooth), straight after it has accepted a connection from its socket.
|
||||||
|
InterceptAccept(network.ConnMultiaddrs) (allow bool)
|
||||||
|
|
||||||
|
// InterceptSecured tests whether a given connection, now authenticated,
|
||||||
|
// is allowed.
|
||||||
|
//
|
||||||
|
// This is called by the upgrader, after it has performed the security
|
||||||
|
// handshake, and before it negotiates the muxer, or by the directly by the
|
||||||
|
// transport, at the exact same checkpoint.
|
||||||
|
InterceptSecured(network.Direction, peer.ID, network.ConnMultiaddrs) (allow bool)
|
||||||
|
|
||||||
|
// InterceptUpgraded tests whether a fully capable connection is allowed.
|
||||||
|
//
|
||||||
|
// At this point, the connection a multiplexer has been selected.
|
||||||
|
// When rejecting a connection, the gater can return a DisconnectReason.
|
||||||
|
// Refer to the godoc on the ConnectionGater type for more information.
|
||||||
|
//
|
||||||
|
// NOTE: the go-libp2p implementation currently IGNORES the disconnect reason.
|
||||||
|
InterceptUpgraded(network.Conn) (allow bool, reason control.DisconnectReason)
|
||||||
|
}
|
91
go-libp2p/core/connmgr/manager.go
Normal file
91
go-libp2p/core/connmgr/manager.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// Package connmgr provides connection tracking and management interfaces for libp2p.
|
||||||
|
//
|
||||||
|
// The ConnManager interface exported from this package allows libp2p to enforce an
|
||||||
|
// upper bound on the total number of open connections. To avoid service disruptions,
|
||||||
|
// connections can be tagged with metadata and optionally "protected" to ensure that
|
||||||
|
// essential connections are not arbitrarily cut.
|
||||||
|
package connmgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SupportsDecay evaluates if the provided ConnManager supports decay, and if
|
||||||
|
// so, it returns the Decayer object. Refer to godocs on Decayer for more info.
|
||||||
|
func SupportsDecay(mgr ConnManager) (Decayer, bool) {
|
||||||
|
d, ok := mgr.(Decayer)
|
||||||
|
return d, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnManager tracks connections to peers, and allows consumers to associate
|
||||||
|
// metadata with each peer.
|
||||||
|
//
|
||||||
|
// It enables connections to be trimmed based on implementation-defined
|
||||||
|
// heuristics. The ConnManager allows libp2p to enforce an upper bound on the
|
||||||
|
// total number of open connections.
|
||||||
|
//
|
||||||
|
// ConnManagers supporting decaying tags implement Decayer. Use the
|
||||||
|
// SupportsDecay function to safely cast an instance to Decayer, if supported.
|
||||||
|
type ConnManager interface {
|
||||||
|
// TagPeer tags a peer with a string, associating a weight with the tag.
|
||||||
|
TagPeer(peer.ID, string, int)
|
||||||
|
|
||||||
|
// Untag removes the tagged value from the peer.
|
||||||
|
UntagPeer(p peer.ID, tag string)
|
||||||
|
|
||||||
|
// UpsertTag updates an existing tag or inserts a new one.
|
||||||
|
//
|
||||||
|
// The connection manager calls the upsert function supplying the current
|
||||||
|
// value of the tag (or zero if inexistent). The return value is used as
|
||||||
|
// the new value of the tag.
|
||||||
|
UpsertTag(p peer.ID, tag string, upsert func(int) int)
|
||||||
|
|
||||||
|
// GetTagInfo returns the metadata associated with the peer,
|
||||||
|
// or nil if no metadata has been recorded for the peer.
|
||||||
|
GetTagInfo(p peer.ID) *TagInfo
|
||||||
|
|
||||||
|
// TrimOpenConns terminates open connections based on an implementation-defined
|
||||||
|
// heuristic.
|
||||||
|
TrimOpenConns(ctx context.Context)
|
||||||
|
|
||||||
|
// Notifee returns an implementation that can be called back to inform of
|
||||||
|
// opened and closed connections.
|
||||||
|
Notifee() network.Notifiee
|
||||||
|
|
||||||
|
// Protect protects a peer from having its connection(s) pruned.
|
||||||
|
//
|
||||||
|
// Tagging allows different parts of the system to manage protections without interfering with one another.
|
||||||
|
//
|
||||||
|
// Calls to Protect() with the same tag are idempotent. They are not refcounted, so after multiple calls
|
||||||
|
// to Protect() with the same tag, a single Unprotect() call bearing the same tag will revoke the protection.
|
||||||
|
Protect(id peer.ID, tag string)
|
||||||
|
|
||||||
|
// Unprotect removes a protection that may have been placed on a peer, under the specified tag.
|
||||||
|
//
|
||||||
|
// The return value indicates whether the peer continues to be protected after this call, by way of a different tag.
|
||||||
|
// See notes on Protect() for more info.
|
||||||
|
Unprotect(id peer.ID, tag string) (protected bool)
|
||||||
|
|
||||||
|
// IsProtected returns true if the peer is protected for some tag; if the tag is the empty string
|
||||||
|
// then it will return true if the peer is protected for any tag
|
||||||
|
IsProtected(id peer.ID, tag string) (protected bool)
|
||||||
|
|
||||||
|
// Close closes the connection manager and stops background processes.
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// TagInfo stores metadata associated with a peer.
|
||||||
|
type TagInfo struct {
|
||||||
|
FirstSeen time.Time
|
||||||
|
Value int
|
||||||
|
|
||||||
|
// Tags maps tag ids to the numerical values.
|
||||||
|
Tags map[string]int
|
||||||
|
|
||||||
|
// Conns maps connection ids (such as remote multiaddr) to their creation time.
|
||||||
|
Conns map[string]time.Time
|
||||||
|
}
|
24
go-libp2p/core/connmgr/null.go
Normal file
24
go-libp2p/core/connmgr/null.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package connmgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NullConnMgr is a ConnMgr that provides no functionality.
|
||||||
|
type NullConnMgr struct{}
|
||||||
|
|
||||||
|
var _ ConnManager = (*NullConnMgr)(nil)
|
||||||
|
|
||||||
|
func (NullConnMgr) TagPeer(peer.ID, string, int) {}
|
||||||
|
func (NullConnMgr) UntagPeer(peer.ID, string) {}
|
||||||
|
func (NullConnMgr) UpsertTag(peer.ID, string, func(int) int) {}
|
||||||
|
func (NullConnMgr) GetTagInfo(peer.ID) *TagInfo { return &TagInfo{} }
|
||||||
|
func (NullConnMgr) TrimOpenConns(ctx context.Context) {}
|
||||||
|
func (NullConnMgr) Notifee() network.Notifiee { return network.GlobalNoopNotifiee }
|
||||||
|
func (NullConnMgr) Protect(peer.ID, string) {}
|
||||||
|
func (NullConnMgr) Unprotect(peer.ID, string) bool { return false }
|
||||||
|
func (NullConnMgr) IsProtected(peer.ID, string) bool { return false }
|
||||||
|
func (NullConnMgr) Close() error { return nil }
|
67
go-libp2p/core/connmgr/presets.go
Normal file
67
go-libp2p/core/connmgr/presets.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package connmgr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecayNone applies no decay.
|
||||||
|
func DecayNone() DecayFn {
|
||||||
|
return func(value DecayingValue) (_ int, rm bool) {
|
||||||
|
return value.Value, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecayFixed subtracts from by the provided minuend, and deletes the tag when
|
||||||
|
// first reaching 0 or negative.
|
||||||
|
func DecayFixed(minuend int) DecayFn {
|
||||||
|
return func(value DecayingValue) (_ int, rm bool) {
|
||||||
|
v := value.Value - minuend
|
||||||
|
return v, v <= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecayLinear applies a fractional coefficient to the value of the current tag,
|
||||||
|
// rounding down via math.Floor. It erases the tag when the result is zero.
|
||||||
|
func DecayLinear(coef float64) DecayFn {
|
||||||
|
return func(value DecayingValue) (after int, rm bool) {
|
||||||
|
v := math.Floor(float64(value.Value) * coef)
|
||||||
|
return int(v), v <= 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecayExpireWhenInactive expires a tag after a certain period of no bumps.
|
||||||
|
func DecayExpireWhenInactive(after time.Duration) DecayFn {
|
||||||
|
return func(value DecayingValue) (_ int, rm bool) {
|
||||||
|
rm = time.Until(value.LastVisit) >= after
|
||||||
|
return 0, rm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BumpSumUnbounded adds the incoming value to the peer's score.
|
||||||
|
func BumpSumUnbounded() BumpFn {
|
||||||
|
return func(value DecayingValue, delta int) (after int) {
|
||||||
|
return value.Value + delta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BumpSumBounded keeps summing the incoming score, keeping it within a
|
||||||
|
// [min, max] range.
|
||||||
|
func BumpSumBounded(min, max int) BumpFn {
|
||||||
|
return func(value DecayingValue, delta int) (after int) {
|
||||||
|
v := value.Value + delta
|
||||||
|
if v >= max {
|
||||||
|
return max
|
||||||
|
} else if v <= min {
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BumpOverwrite replaces the current value of the tag with the incoming one.
|
||||||
|
func BumpOverwrite() BumpFn {
|
||||||
|
return func(value DecayingValue, delta int) (after int) {
|
||||||
|
return delta
|
||||||
|
}
|
||||||
|
}
|
9
go-libp2p/core/control/disconnect.go
Normal file
9
go-libp2p/core/control/disconnect.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package control
|
||||||
|
|
||||||
|
// DisconnectReason communicates the reason why a connection is being closed.
|
||||||
|
//
|
||||||
|
// A zero value stands for "no reason" / NA.
|
||||||
|
//
|
||||||
|
// This is an EXPERIMENTAL type. It will change in the future. Refer to the
|
||||||
|
// connmgr.ConnectionGater godoc for more info.
|
||||||
|
type DisconnectReason int
|
84
go-libp2p/core/crypto/bench_test.go
Normal file
84
go-libp2p/core/crypto/bench_test.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func BenchmarkSignRSA1B(b *testing.B) { RunBenchmarkSignRSA(b, 1) }
|
||||||
|
func BenchmarkSignRSA10B(b *testing.B) { RunBenchmarkSignRSA(b, 10) }
|
||||||
|
func BenchmarkSignRSA100B(b *testing.B) { RunBenchmarkSignRSA(b, 100) }
|
||||||
|
func BenchmarkSignRSA1000B(b *testing.B) { RunBenchmarkSignRSA(b, 1000) }
|
||||||
|
func BenchmarkSignRSA10000B(b *testing.B) { RunBenchmarkSignRSA(b, 10000) }
|
||||||
|
func BenchmarkSignRSA100000B(b *testing.B) { RunBenchmarkSignRSA(b, 100000) }
|
||||||
|
|
||||||
|
func BenchmarkVerifyRSA1B(b *testing.B) { RunBenchmarkVerifyRSA(b, 1) }
|
||||||
|
func BenchmarkVerifyRSA10B(b *testing.B) { RunBenchmarkVerifyRSA(b, 10) }
|
||||||
|
func BenchmarkVerifyRSA100B(b *testing.B) { RunBenchmarkVerifyRSA(b, 100) }
|
||||||
|
func BenchmarkVerifyRSA1000B(b *testing.B) { RunBenchmarkVerifyRSA(b, 1000) }
|
||||||
|
func BenchmarkVerifyRSA10000B(b *testing.B) { RunBenchmarkVerifyRSA(b, 10000) }
|
||||||
|
func BenchmarkVerifyRSA100000B(b *testing.B) { RunBenchmarkVerifyRSA(b, 100000) }
|
||||||
|
|
||||||
|
func BenchmarkSignEd255191B(b *testing.B) { RunBenchmarkSignEd25519(b, 1) }
|
||||||
|
func BenchmarkSignEd2551910B(b *testing.B) { RunBenchmarkSignEd25519(b, 10) }
|
||||||
|
func BenchmarkSignEd25519100B(b *testing.B) { RunBenchmarkSignEd25519(b, 100) }
|
||||||
|
func BenchmarkSignEd255191000B(b *testing.B) { RunBenchmarkSignEd25519(b, 1000) }
|
||||||
|
func BenchmarkSignEd2551910000B(b *testing.B) { RunBenchmarkSignEd25519(b, 10000) }
|
||||||
|
func BenchmarkSignEd25519100000B(b *testing.B) { RunBenchmarkSignEd25519(b, 100000) }
|
||||||
|
|
||||||
|
func BenchmarkVerifyEd255191B(b *testing.B) { RunBenchmarkVerifyEd25519(b, 1) }
|
||||||
|
func BenchmarkVerifyEd2551910B(b *testing.B) { RunBenchmarkVerifyEd25519(b, 10) }
|
||||||
|
func BenchmarkVerifyEd25519100B(b *testing.B) { RunBenchmarkVerifyEd25519(b, 100) }
|
||||||
|
func BenchmarkVerifyEd255191000B(b *testing.B) { RunBenchmarkVerifyEd25519(b, 1000) }
|
||||||
|
func BenchmarkVerifyEd2551910000B(b *testing.B) { RunBenchmarkVerifyEd25519(b, 10000) }
|
||||||
|
func BenchmarkVerifyEd25519100000B(b *testing.B) { RunBenchmarkVerifyEd25519(b, 100000) }
|
||||||
|
|
||||||
|
func RunBenchmarkSignRSA(b *testing.B, numBytes int) {
|
||||||
|
runBenchmarkSign(b, numBytes, RSA)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunBenchmarkSignEd25519(b *testing.B, numBytes int) {
|
||||||
|
runBenchmarkSign(b, numBytes, Ed25519)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runBenchmarkSign(b *testing.B, numBytes int, t int) {
|
||||||
|
secret, _, err := GenerateKeyPair(t, 2048)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
someData := make([]byte, numBytes)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := secret.Sign(someData)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunBenchmarkVerifyRSA(b *testing.B, numBytes int) {
|
||||||
|
runBenchmarkVerify(b, numBytes, RSA)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunBenchmarkVerifyEd25519(b *testing.B, numBytes int) {
|
||||||
|
runBenchmarkVerify(b, numBytes, Ed25519)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runBenchmarkVerify(b *testing.B, numBytes int, t int) {
|
||||||
|
secret, public, err := GenerateKeyPair(t, 2048)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
someData := make([]byte, numBytes)
|
||||||
|
signature, err := secret.Sign(someData)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
valid, err := public.Verify(someData, signature)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if !valid {
|
||||||
|
b.Fatal("signature should be valid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
187
go-libp2p/core/crypto/ecdsa.go
Normal file
187
go-libp2p/core/crypto/ecdsa.go
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/asn1"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
"github.com/libp2p/go-libp2p/core/internal/catch"
|
||||||
|
|
||||||
|
"github.com/minio/sha256-simd"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ECDSAPrivateKey is an implementation of an ECDSA private key
|
||||||
|
type ECDSAPrivateKey struct {
|
||||||
|
priv *ecdsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECDSAPublicKey is an implementation of an ECDSA public key
|
||||||
|
type ECDSAPublicKey struct {
|
||||||
|
pub *ecdsa.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECDSASig holds the r and s values of an ECDSA signature
|
||||||
|
type ECDSASig struct {
|
||||||
|
R, S *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNotECDSAPubKey is returned when the public key passed is not an ecdsa public key
|
||||||
|
ErrNotECDSAPubKey = errors.New("not an ecdsa public key")
|
||||||
|
// ErrNilSig is returned when the signature is nil
|
||||||
|
ErrNilSig = errors.New("sig is nil")
|
||||||
|
// ErrNilPrivateKey is returned when a nil private key is provided
|
||||||
|
ErrNilPrivateKey = errors.New("private key is nil")
|
||||||
|
// ErrNilPublicKey is returned when a nil public key is provided
|
||||||
|
ErrNilPublicKey = errors.New("public key is nil")
|
||||||
|
// ECDSACurve is the default ecdsa curve used
|
||||||
|
ECDSACurve = elliptic.P256()
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateECDSAKeyPair generates a new ecdsa private and public key
|
||||||
|
func GenerateECDSAKeyPair(src io.Reader) (PrivKey, PubKey, error) {
|
||||||
|
return GenerateECDSAKeyPairWithCurve(ECDSACurve, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateECDSAKeyPairWithCurve generates a new ecdsa private and public key with a specified curve
|
||||||
|
func GenerateECDSAKeyPairWithCurve(curve elliptic.Curve, src io.Reader) (PrivKey, PubKey, error) {
|
||||||
|
priv, err := ecdsa.GenerateKey(curve, src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECDSAKeyPairFromKey generates a new ecdsa private and public key from an input private key
|
||||||
|
func ECDSAKeyPairFromKey(priv *ecdsa.PrivateKey) (PrivKey, PubKey, error) {
|
||||||
|
if priv == nil {
|
||||||
|
return nil, nil, ErrNilPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ECDSAPrivateKey{priv}, &ECDSAPublicKey{&priv.PublicKey}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ECDSAPublicKeyFromPubKey generates a new ecdsa public key from an input public key
|
||||||
|
func ECDSAPublicKeyFromPubKey(pub ecdsa.PublicKey) (PubKey, error) {
|
||||||
|
return &ECDSAPublicKey{pub: &pub}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalECDSAPrivateKey returns x509 bytes from a private key
|
||||||
|
func MarshalECDSAPrivateKey(ePriv ECDSAPrivateKey) (res []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "ECDSA private-key marshal") }()
|
||||||
|
return x509.MarshalECPrivateKey(ePriv.priv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalECDSAPublicKey returns x509 bytes from a public key
|
||||||
|
func MarshalECDSAPublicKey(ePub ECDSAPublicKey) (res []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "ECDSA public-key marshal") }()
|
||||||
|
return x509.MarshalPKIXPublicKey(ePub.pub)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalECDSAPrivateKey returns a private key from x509 bytes
|
||||||
|
func UnmarshalECDSAPrivateKey(data []byte) (res PrivKey, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "ECDSA private-key unmarshal") }()
|
||||||
|
|
||||||
|
priv, err := x509.ParseECPrivateKey(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ECDSAPrivateKey{priv}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalECDSAPublicKey returns the public key from x509 bytes
|
||||||
|
func UnmarshalECDSAPublicKey(data []byte) (key PubKey, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "ECDSA public-key unmarshal") }()
|
||||||
|
|
||||||
|
pubIfc, err := x509.ParsePKIXPublicKey(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, ok := pubIfc.(*ecdsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrNotECDSAPubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ECDSAPublicKey{pub}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the key type
|
||||||
|
func (ePriv *ECDSAPrivateKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_ECDSA
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw returns x509 bytes from a private key
|
||||||
|
func (ePriv *ECDSAPrivateKey) Raw() (res []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "ECDSA private-key marshal") }()
|
||||||
|
return x509.MarshalECPrivateKey(ePriv.priv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares two private keys
|
||||||
|
func (ePriv *ECDSAPrivateKey) Equals(o Key) bool {
|
||||||
|
return basicEquals(ePriv, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns the signature of the input data
|
||||||
|
func (ePriv *ECDSAPrivateKey) Sign(data []byte) (sig []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "ECDSA signing") }()
|
||||||
|
hash := sha256.Sum256(data)
|
||||||
|
r, s, err := ecdsa.Sign(rand.Reader, ePriv.priv, hash[:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return asn1.Marshal(ECDSASig{
|
||||||
|
R: r,
|
||||||
|
S: s,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublic returns a public key
|
||||||
|
func (ePriv *ECDSAPrivateKey) GetPublic() PubKey {
|
||||||
|
return &ECDSAPublicKey{&ePriv.priv.PublicKey}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the key type
|
||||||
|
func (ePub *ECDSAPublicKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_ECDSA
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw returns x509 bytes from a public key
|
||||||
|
func (ePub *ECDSAPublicKey) Raw() ([]byte, error) {
|
||||||
|
return x509.MarshalPKIXPublicKey(ePub.pub)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares to public keys
|
||||||
|
func (ePub *ECDSAPublicKey) Equals(o Key) bool {
|
||||||
|
return basicEquals(ePub, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify compares data to a signature
|
||||||
|
func (ePub *ECDSAPublicKey) Verify(data, sigBytes []byte) (success bool, err error) {
|
||||||
|
defer func() {
|
||||||
|
catch.HandlePanic(recover(), &err, "ECDSA signature verification")
|
||||||
|
|
||||||
|
// Just to be extra paranoid.
|
||||||
|
if err != nil {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
sig := new(ECDSASig)
|
||||||
|
if _, err := asn1.Unmarshal(sigBytes, sig); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.Sum256(data)
|
||||||
|
|
||||||
|
return ecdsa.Verify(ePub.pub, hash[:], sig.R, sig.S), nil
|
||||||
|
}
|
142
go-libp2p/core/crypto/ecdsa_test.go
Normal file
142
go-libp2p/core/crypto/ecdsa_test.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestECDSABasicSignAndVerify(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateECDSAKeyPair(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []byte("hello! and welcome to some awesome crypto primitives")
|
||||||
|
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didnt match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// change data
|
||||||
|
data[0] = ^data[0]
|
||||||
|
ok, err = pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Fatal("signature matched and shouldn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestECDSASignZero(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateECDSAKeyPair(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, 0)
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestECDSAMarshalLoop(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateECDSAKeyPair(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privB, err := MarshalPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privNew, err := UnmarshalPrivateKey(privB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !priv.Equals(privNew) || !privNew.Equals(priv) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubB, err := MarshalPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pubNew, err := UnmarshalPublicKey(pubB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pub.Equals(pubNew) || !pubNew.Equals(pub) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestECDSAPublicKeyFromPubKey(t *testing.T) {
|
||||||
|
ecdsaPrivK, err := ecdsa.GenerateKey(ECDSACurve, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privK, _, err := ECDSAKeyPairFromKey(ecdsaPrivK)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []byte("Hello world!")
|
||||||
|
signature, err := privK.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, err := ECDSAPublicKeyFromPubKey(ecdsaPrivK.PublicKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pubKey.Verify(data, signature)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubB, err := MarshalPublicKey(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pubNew, err := UnmarshalPublicKey(pubB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pubKey.Equals(pubNew) || !pubNew.Equals(pubKey) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
}
|
156
go-libp2p/core/crypto/ed25519.go
Normal file
156
go-libp2p/core/crypto/ed25519.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/subtle"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
"github.com/libp2p/go-libp2p/core/internal/catch"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ed25519PrivateKey is an ed25519 private key.
|
||||||
|
type Ed25519PrivateKey struct {
|
||||||
|
k ed25519.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ed25519PublicKey is an ed25519 public key.
|
||||||
|
type Ed25519PublicKey struct {
|
||||||
|
k ed25519.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateEd25519Key generates a new ed25519 private and public key pair.
|
||||||
|
func GenerateEd25519Key(src io.Reader) (PrivKey, PubKey, error) {
|
||||||
|
pub, priv, err := ed25519.GenerateKey(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ed25519PrivateKey{
|
||||||
|
k: priv,
|
||||||
|
},
|
||||||
|
&Ed25519PublicKey{
|
||||||
|
k: pub,
|
||||||
|
},
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of the private key (Ed25519).
|
||||||
|
func (k *Ed25519PrivateKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_Ed25519
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw private key bytes.
|
||||||
|
func (k *Ed25519PrivateKey) Raw() ([]byte, error) {
|
||||||
|
// The Ed25519 private key contains two 32-bytes curve points, the private
|
||||||
|
// key and the public key.
|
||||||
|
// It makes it more efficient to get the public key without re-computing an
|
||||||
|
// elliptic curve multiplication.
|
||||||
|
buf := make([]byte, len(k.k))
|
||||||
|
copy(buf, k.k)
|
||||||
|
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Ed25519PrivateKey) pubKeyBytes() []byte {
|
||||||
|
return k.k[ed25519.PrivateKeySize-ed25519.PublicKeySize:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares two ed25519 private keys.
|
||||||
|
func (k *Ed25519PrivateKey) Equals(o Key) bool {
|
||||||
|
edk, ok := o.(*Ed25519PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return basicEquals(k, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return subtle.ConstantTimeCompare(k.k, edk.k) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublic returns an ed25519 public key from a private key.
|
||||||
|
func (k *Ed25519PrivateKey) GetPublic() PubKey {
|
||||||
|
return &Ed25519PublicKey{k: k.pubKeyBytes()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns a signature from an input message.
|
||||||
|
func (k *Ed25519PrivateKey) Sign(msg []byte) (res []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "ed15519 signing") }()
|
||||||
|
|
||||||
|
return ed25519.Sign(k.k, msg), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of the public key (Ed25519).
|
||||||
|
func (k *Ed25519PublicKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_Ed25519
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw public key bytes.
|
||||||
|
func (k *Ed25519PublicKey) Raw() ([]byte, error) {
|
||||||
|
return k.k, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares two ed25519 public keys.
|
||||||
|
func (k *Ed25519PublicKey) Equals(o Key) bool {
|
||||||
|
edk, ok := o.(*Ed25519PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return basicEquals(k, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.Equal(k.k, edk.k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify checks a signature against the input data.
|
||||||
|
func (k *Ed25519PublicKey) Verify(data []byte, sig []byte) (success bool, err error) {
|
||||||
|
defer func() {
|
||||||
|
catch.HandlePanic(recover(), &err, "ed15519 signature verification")
|
||||||
|
|
||||||
|
// To be safe.
|
||||||
|
if err != nil {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ed25519.Verify(k.k, data, sig), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalEd25519PublicKey returns a public key from input bytes.
|
||||||
|
func UnmarshalEd25519PublicKey(data []byte) (PubKey, error) {
|
||||||
|
if len(data) != 32 {
|
||||||
|
return nil, errors.New("expect ed25519 public key data size to be 32")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ed25519PublicKey{
|
||||||
|
k: ed25519.PublicKey(data),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalEd25519PrivateKey returns a private key from input bytes.
|
||||||
|
func UnmarshalEd25519PrivateKey(data []byte) (PrivKey, error) {
|
||||||
|
switch len(data) {
|
||||||
|
case ed25519.PrivateKeySize + ed25519.PublicKeySize:
|
||||||
|
// Remove the redundant public key. See issue #36.
|
||||||
|
redundantPk := data[ed25519.PrivateKeySize:]
|
||||||
|
pk := data[ed25519.PrivateKeySize-ed25519.PublicKeySize : ed25519.PrivateKeySize]
|
||||||
|
if subtle.ConstantTimeCompare(pk, redundantPk) == 0 {
|
||||||
|
return nil, errors.New("expected redundant ed25519 public key to be redundant")
|
||||||
|
}
|
||||||
|
|
||||||
|
// No point in storing the extra data.
|
||||||
|
newKey := make([]byte, ed25519.PrivateKeySize)
|
||||||
|
copy(newKey, data[:ed25519.PrivateKeySize])
|
||||||
|
data = newKey
|
||||||
|
case ed25519.PrivateKeySize:
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"expected ed25519 data size to be %d or %d, got %d",
|
||||||
|
ed25519.PrivateKeySize,
|
||||||
|
ed25519.PrivateKeySize+ed25519.PublicKeySize,
|
||||||
|
len(data),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ed25519PrivateKey{
|
||||||
|
k: ed25519.PrivateKey(data),
|
||||||
|
}, nil
|
||||||
|
}
|
214
go-libp2p/core/crypto/ed25519_test.go
Normal file
214
go-libp2p/core/crypto/ed25519_test.go
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBasicSignAndVerify(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []byte("hello! and welcome to some awesome crypto primitives")
|
||||||
|
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// change data
|
||||||
|
data[0] = ^data[0]
|
||||||
|
ok, err = pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Fatal("signature matched and shouldn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSignZero(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, 0)
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalLoop(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("PrivateKey", func(t *testing.T) {
|
||||||
|
for name, f := range map[string]func() ([]byte, error){
|
||||||
|
"Marshal": func() ([]byte, error) {
|
||||||
|
return MarshalPrivateKey(priv)
|
||||||
|
},
|
||||||
|
"Redundant": func() ([]byte, error) {
|
||||||
|
// See issue #36.
|
||||||
|
// Ed25519 private keys used to contain the public key twice.
|
||||||
|
// For backwards-compatibility, we need to continue supporting
|
||||||
|
// that scenario.
|
||||||
|
data, err := priv.Raw()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
data = append(data, data[len(data)-ed25519.PublicKeySize:]...)
|
||||||
|
return proto.Marshal(&pb.PrivateKey{
|
||||||
|
Type: priv.Type().Enum(),
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
bts, err := f()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privNew, err := UnmarshalPrivateKey(bts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !priv.Equals(privNew) || !privNew.Equals(priv) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := []byte("My child, my sister,\nThink of the rapture\nOf living together there!")
|
||||||
|
signed, err := privNew.Sign(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := privNew.GetPublic().Verify(msg, signed)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PublicKey", func(t *testing.T) {
|
||||||
|
for name, f := range map[string]func() ([]byte, error){
|
||||||
|
"Marshal": func() ([]byte, error) {
|
||||||
|
return MarshalPublicKey(pub)
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
bts, err := f()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pubNew, err := UnmarshalPublicKey(bts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pub.Equals(pubNew) || !pubNew.Equals(pub) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalErrors(t *testing.T) {
|
||||||
|
t.Run("PublicKey", func(t *testing.T) {
|
||||||
|
t.Run("Invalid data length", func(t *testing.T) {
|
||||||
|
data, err := proto.Marshal(&pb.PublicKey{
|
||||||
|
Type: pb.KeyType_Ed25519.Enum(),
|
||||||
|
Data: []byte{42},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := UnmarshalPublicKey(data); err == nil {
|
||||||
|
t.Fatal("expected an error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PrivateKey", func(t *testing.T) {
|
||||||
|
t.Run("Redundant public key mismatch", func(t *testing.T) {
|
||||||
|
priv, _, err := GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := priv.Raw()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Append the private key instead of the public key.
|
||||||
|
data = append(data, data[:ed25519.PublicKeySize]...)
|
||||||
|
|
||||||
|
b, err := proto.Marshal(&pb.PrivateKey{
|
||||||
|
Type: priv.Type().Enum(),
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = UnmarshalPrivateKey(b)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected an error")
|
||||||
|
}
|
||||||
|
if err.Error() != "expected redundant ed25519 public key to be redundant" {
|
||||||
|
t.Fatalf("invalid error received: %s", err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Invalid data length", func(t *testing.T) {
|
||||||
|
data, err := proto.Marshal(&pb.PrivateKey{
|
||||||
|
Type: pb.KeyType_Ed25519.Enum(),
|
||||||
|
Data: []byte{42},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = UnmarshalPrivateKey(data)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected an error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
157
go-libp2p/core/crypto/ed448.go
Normal file
157
go-libp2p/core/crypto/ed448.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/subtle"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
"github.com/libp2p/go-libp2p/core/internal/catch"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/sign/ed448"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ed448PrivateKey is an ed448 private key.
|
||||||
|
type Ed448PrivateKey struct {
|
||||||
|
k ed448.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ed448PublicKey is an ed448 public key.
|
||||||
|
type Ed448PublicKey struct {
|
||||||
|
k ed448.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateEd448Key generates a new ed448 private and public key pair.
|
||||||
|
func GenerateEd448Key(src io.Reader) (PrivKey, PubKey, error) {
|
||||||
|
pub, priv, err := ed448.GenerateKey(src)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ed448PrivateKey{
|
||||||
|
k: priv,
|
||||||
|
},
|
||||||
|
&Ed448PublicKey{
|
||||||
|
k: pub,
|
||||||
|
},
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of the private key (Ed448).
|
||||||
|
func (k *Ed448PrivateKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_Ed448
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw private key bytes.
|
||||||
|
func (k *Ed448PrivateKey) Raw() ([]byte, error) {
|
||||||
|
// The Ed448 private key contains two 57-bytes curve points, the private
|
||||||
|
// key and the public key.
|
||||||
|
// It makes it more efficient to get the public key without re-computing an
|
||||||
|
// elliptic curve multiplication.
|
||||||
|
buf := make([]byte, len(k.k))
|
||||||
|
copy(buf, k.k)
|
||||||
|
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (k *Ed448PrivateKey) pubKeyBytes() []byte {
|
||||||
|
return k.k[ed448.PrivateKeySize-ed448.PublicKeySize:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares two ed448 private keys.
|
||||||
|
func (k *Ed448PrivateKey) Equals(o Key) bool {
|
||||||
|
edk, ok := o.(*Ed448PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return basicEquals(k, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return subtle.ConstantTimeCompare(k.k, edk.k) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublic returns an ed448 public key from a private key.
|
||||||
|
func (k *Ed448PrivateKey) GetPublic() PubKey {
|
||||||
|
return &Ed448PublicKey{k: k.pubKeyBytes()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns a signature from an input message.
|
||||||
|
func (k *Ed448PrivateKey) Sign(msg []byte) (res []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "ed448 signing") }()
|
||||||
|
|
||||||
|
return ed448.Sign(k.k, msg, ""), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type of the public key (Ed448).
|
||||||
|
func (k *Ed448PublicKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_Ed448
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw public key bytes.
|
||||||
|
func (k *Ed448PublicKey) Raw() ([]byte, error) {
|
||||||
|
return k.k, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares two ed448 public keys.
|
||||||
|
func (k *Ed448PublicKey) Equals(o Key) bool {
|
||||||
|
edk, ok := o.(*Ed448PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return basicEquals(k, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes.Equal(k.k, edk.k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify checks a signature against the input data.
|
||||||
|
func (k *Ed448PublicKey) Verify(data []byte, sig []byte) (success bool, err error) {
|
||||||
|
defer func() {
|
||||||
|
catch.HandlePanic(recover(), &err, "ed448 signature verification")
|
||||||
|
|
||||||
|
// To be safe.
|
||||||
|
if err != nil {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return ed448.Verify(k.k, data, sig, ""), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalEd448PublicKey returns a public key from input bytes.
|
||||||
|
func UnmarshalEd448PublicKey(data []byte) (PubKey, error) {
|
||||||
|
if len(data) != 57 {
|
||||||
|
return nil, errors.New("expect ed448 public key data size to be 57")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ed448PublicKey{
|
||||||
|
k: ed448.PublicKey(data),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalEd448PrivateKey returns a private key from input bytes.
|
||||||
|
func UnmarshalEd448PrivateKey(data []byte) (PrivKey, error) {
|
||||||
|
switch len(data) {
|
||||||
|
case ed448.PrivateKeySize + ed448.PublicKeySize:
|
||||||
|
// Remove the redundant public key. See issue #36.
|
||||||
|
redundantPk := data[ed448.PrivateKeySize:]
|
||||||
|
pk := data[ed448.PrivateKeySize-ed448.PublicKeySize : ed448.PrivateKeySize]
|
||||||
|
if subtle.ConstantTimeCompare(pk, redundantPk) == 0 {
|
||||||
|
return nil, errors.New("expected redundant ed448 public key to be redundant")
|
||||||
|
}
|
||||||
|
|
||||||
|
// No point in storing the extra data.
|
||||||
|
newKey := make([]byte, ed448.PrivateKeySize)
|
||||||
|
copy(newKey, data[:ed448.PrivateKeySize])
|
||||||
|
data = newKey
|
||||||
|
case ed448.PrivateKeySize:
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"expected ed448 data size to be %d or %d, got %d",
|
||||||
|
ed448.PrivateKeySize,
|
||||||
|
ed448.PrivateKeySize+ed448.PublicKeySize,
|
||||||
|
len(data),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Ed448PrivateKey{
|
||||||
|
k: ed448.PrivateKey(data),
|
||||||
|
}, nil
|
||||||
|
}
|
210
go-libp2p/core/crypto/ed448_test.go
Normal file
210
go-libp2p/core/crypto/ed448_test.go
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/sign/ed448"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEd448BasicSignAndVerify(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []byte("hello! and welcome to some awesome crypto primitives")
|
||||||
|
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// change data
|
||||||
|
data[0] = ^data[0]
|
||||||
|
ok, err = pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Fatal("signature matched and shouldn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEd448SignZero(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, 0)
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEd448MarshalLoop(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateEd448Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("PrivateKey", func(t *testing.T) {
|
||||||
|
for name, f := range map[string]func() ([]byte, error){
|
||||||
|
"Marshal": func() ([]byte, error) {
|
||||||
|
return MarshalPrivateKey(priv)
|
||||||
|
},
|
||||||
|
"Redundant": func() ([]byte, error) {
|
||||||
|
data, err := priv.Raw()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
data = append(data, data[len(data)-ed448.PublicKeySize:]...)
|
||||||
|
return proto.Marshal(&pb.PrivateKey{
|
||||||
|
Type: priv.Type().Enum(),
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
bts, err := f()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privNew, err := UnmarshalPrivateKey(bts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !priv.Equals(privNew) || !privNew.Equals(priv) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := []byte("My child, my sister,\nThink of the rapture\nOf living together there!")
|
||||||
|
signed, err := privNew.Sign(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := privNew.GetPublic().Verify(msg, signed)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PublicKey", func(t *testing.T) {
|
||||||
|
for name, f := range map[string]func() ([]byte, error){
|
||||||
|
"Marshal": func() ([]byte, error) {
|
||||||
|
return MarshalPublicKey(pub)
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
bts, err := f()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pubNew, err := UnmarshalPublicKey(bts)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pub.Equals(pubNew) || !pubNew.Equals(pub) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEd448UnmarshalErrors(t *testing.T) {
|
||||||
|
t.Run("PublicKey", func(t *testing.T) {
|
||||||
|
t.Run("Invalid data length", func(t *testing.T) {
|
||||||
|
data, err := proto.Marshal(&pb.PublicKey{
|
||||||
|
Type: pb.KeyType_Ed448.Enum(),
|
||||||
|
Data: []byte{74},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := UnmarshalPublicKey(data); err == nil {
|
||||||
|
t.Fatal("expected an error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("PrivateKey", func(t *testing.T) {
|
||||||
|
t.Run("Redundant public key mismatch", func(t *testing.T) {
|
||||||
|
priv, _, err := GenerateEd448Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := priv.Raw()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Append the private key instead of the public key.
|
||||||
|
data = append(data, data[:ed448.PublicKeySize]...)
|
||||||
|
|
||||||
|
b, err := proto.Marshal(&pb.PrivateKey{
|
||||||
|
Type: priv.Type().Enum(),
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = UnmarshalPrivateKey(b)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected an error")
|
||||||
|
}
|
||||||
|
if err.Error() != "expected redundant ed448 public key to be redundant" {
|
||||||
|
t.Fatalf("invalid error received: %s", err.Error())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Invalid data length", func(t *testing.T) {
|
||||||
|
data, err := proto.Marshal(&pb.PrivateKey{
|
||||||
|
Type: pb.KeyType_Ed448.Enum(),
|
||||||
|
Data: []byte{74},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = UnmarshalPrivateKey(data)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected an error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
132
go-libp2p/core/crypto/fixture_test.go
Normal file
132
go-libp2p/core/crypto/fixture_test.go
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
package crypto_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
crypto_pb "github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
)
|
||||||
|
|
||||||
|
var message = []byte("Libp2p is the _best_!")
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
keyType crypto_pb.KeyType
|
||||||
|
gen func(i io.Reader) (crypto.PrivKey, crypto.PubKey, error)
|
||||||
|
sigDeterministic bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var keyTypes = []testCase{
|
||||||
|
{
|
||||||
|
keyType: crypto_pb.KeyType_ECDSA,
|
||||||
|
gen: crypto.GenerateECDSAKeyPair,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keyType: crypto_pb.KeyType_Secp256k1,
|
||||||
|
sigDeterministic: true,
|
||||||
|
gen: crypto.GenerateSecp256k1Key,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
keyType: crypto_pb.KeyType_RSA,
|
||||||
|
sigDeterministic: true,
|
||||||
|
gen: func(i io.Reader) (crypto.PrivKey, crypto.PubKey, error) {
|
||||||
|
return crypto.GenerateRSAKeyPair(2048, i)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func fname(kt crypto_pb.KeyType, ext string) string {
|
||||||
|
return fmt.Sprintf("test_data/%d.%s", kt, ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFixtures(t *testing.T) {
|
||||||
|
for _, tc := range keyTypes {
|
||||||
|
t.Run(tc.keyType.String(), func(t *testing.T) {
|
||||||
|
pubBytes, err := os.ReadFile(fname(tc.keyType, "pub"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
privBytes, err := os.ReadFile(fname(tc.keyType, "priv"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
sigBytes, err := os.ReadFile(fname(tc.keyType, "sig"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pub, err := crypto.UnmarshalPublicKey(pubBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pubBytes2, err := crypto.MarshalPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(pubBytes2, pubBytes) {
|
||||||
|
t.Fatal("encoding round-trip failed")
|
||||||
|
}
|
||||||
|
priv, err := crypto.UnmarshalPrivateKey(privBytes)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
privBytes2, err := crypto.MarshalPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(privBytes2, privBytes) {
|
||||||
|
t.Fatal("encoding round-trip failed")
|
||||||
|
}
|
||||||
|
ok, err := pub.Verify(message, sigBytes)
|
||||||
|
if !ok || err != nil {
|
||||||
|
t.Fatal("failed to validate signature with public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.sigDeterministic {
|
||||||
|
sigBytes2, err := priv.Sign(message)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(sigBytes2, sigBytes) {
|
||||||
|
t.Fatal("signature not deterministic")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// set to true to re-generate test data
|
||||||
|
if false {
|
||||||
|
generate()
|
||||||
|
panic("generated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate re-generates test data
|
||||||
|
func generate() {
|
||||||
|
for _, tc := range keyTypes {
|
||||||
|
priv, pub, err := tc.gen(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
pubb, err := crypto.MarshalPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
privb, err := crypto.MarshalPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sig, err := priv.Sign(message)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
os.WriteFile(fname(tc.keyType, "pub"), pubb, 0666)
|
||||||
|
os.WriteFile(fname(tc.keyType, "priv"), privb, 0666)
|
||||||
|
os.WriteFile(fname(tc.keyType, "sig"), sig, 0666)
|
||||||
|
}
|
||||||
|
}
|
298
go-libp2p/core/crypto/key.go
Normal file
298
go-libp2p/core/crypto/key.go
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
// Package crypto implements various cryptographic utilities used by libp2p.
|
||||||
|
// This includes a Public and Private key interface and key implementations
|
||||||
|
// for supported key algorithms.
|
||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate protoc --go_out=. --go_opt=Mpb/crypto.proto=./pb pb/crypto.proto
|
||||||
|
|
||||||
|
const (
|
||||||
|
// RSA is an enum for the supported RSA key type
|
||||||
|
RSA = iota
|
||||||
|
// Ed25519 is an enum for the supported Ed25519 key type
|
||||||
|
Ed25519
|
||||||
|
// Secp256k1 is an enum for the supported Secp256k1 key type
|
||||||
|
Secp256k1
|
||||||
|
// ECDSA is an enum for the supported ECDSA key type
|
||||||
|
ECDSA
|
||||||
|
// Ed448 is an enum for the supported Ed448 key type
|
||||||
|
Ed448
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrBadKeyType is returned when a key is not supported
|
||||||
|
ErrBadKeyType = errors.New("invalid or unsupported key type")
|
||||||
|
// KeyTypes is a list of supported keys
|
||||||
|
KeyTypes = []int{
|
||||||
|
RSA,
|
||||||
|
Ed25519,
|
||||||
|
Secp256k1,
|
||||||
|
ECDSA,
|
||||||
|
Ed448,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// PubKeyUnmarshaller is a func that creates a PubKey from a given slice of bytes
|
||||||
|
type PubKeyUnmarshaller func(data []byte) (PubKey, error)
|
||||||
|
|
||||||
|
// PrivKeyUnmarshaller is a func that creates a PrivKey from a given slice of bytes
|
||||||
|
type PrivKeyUnmarshaller func(data []byte) (PrivKey, error)
|
||||||
|
|
||||||
|
// PubKeyUnmarshallers is a map of unmarshallers by key type
|
||||||
|
var PubKeyUnmarshallers = map[pb.KeyType]PubKeyUnmarshaller{
|
||||||
|
pb.KeyType_RSA: UnmarshalRsaPublicKey,
|
||||||
|
pb.KeyType_Ed25519: UnmarshalEd25519PublicKey,
|
||||||
|
pb.KeyType_Secp256k1: UnmarshalSecp256k1PublicKey,
|
||||||
|
pb.KeyType_ECDSA: UnmarshalECDSAPublicKey,
|
||||||
|
pb.KeyType_Ed448: UnmarshalEd448PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivKeyUnmarshallers is a map of unmarshallers by key type
|
||||||
|
var PrivKeyUnmarshallers = map[pb.KeyType]PrivKeyUnmarshaller{
|
||||||
|
pb.KeyType_RSA: UnmarshalRsaPrivateKey,
|
||||||
|
pb.KeyType_Ed25519: UnmarshalEd25519PrivateKey,
|
||||||
|
pb.KeyType_Secp256k1: UnmarshalSecp256k1PrivateKey,
|
||||||
|
pb.KeyType_ECDSA: UnmarshalECDSAPrivateKey,
|
||||||
|
pb.KeyType_Ed448: UnmarshalEd448PrivateKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key represents a crypto key that can be compared to another key
|
||||||
|
type Key interface {
|
||||||
|
// Equals checks whether two PubKeys are the same
|
||||||
|
Equals(Key) bool
|
||||||
|
|
||||||
|
// Raw returns the raw bytes of the key (not wrapped in the
|
||||||
|
// libp2p-crypto protobuf).
|
||||||
|
//
|
||||||
|
// This function is the inverse of {Priv,Pub}KeyUnmarshaler.
|
||||||
|
Raw() ([]byte, error)
|
||||||
|
|
||||||
|
// Type returns the protobuf key type.
|
||||||
|
Type() pb.KeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivKey represents a private key that can be used to generate a public key and sign data
|
||||||
|
type PrivKey interface {
|
||||||
|
Key
|
||||||
|
|
||||||
|
// Cryptographically sign the given bytes
|
||||||
|
Sign([]byte) ([]byte, error)
|
||||||
|
|
||||||
|
// Return a public key paired with this private key
|
||||||
|
GetPublic() PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// PubKey is a public key that can be used to verify data signed with the corresponding private key
|
||||||
|
type PubKey interface {
|
||||||
|
Key
|
||||||
|
|
||||||
|
// Verify that 'sig' is the signed hash of 'data'
|
||||||
|
Verify(data []byte, sig []byte) (bool, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenSharedKey generates the shared key from a given private key
|
||||||
|
type GenSharedKey func([]byte) ([]byte, error)
|
||||||
|
|
||||||
|
// GenerateKeyPair generates a private and public key
|
||||||
|
func GenerateKeyPair(typ, bits int) (PrivKey, PubKey, error) {
|
||||||
|
return GenerateKeyPairWithReader(typ, bits, rand.Reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKeyPairWithReader returns a keypair of the given type and bit-size
|
||||||
|
func GenerateKeyPairWithReader(typ, bits int, src io.Reader) (PrivKey, PubKey, error) {
|
||||||
|
switch typ {
|
||||||
|
case RSA:
|
||||||
|
return GenerateRSAKeyPair(bits, src)
|
||||||
|
case Ed25519:
|
||||||
|
return GenerateEd25519Key(src)
|
||||||
|
case Secp256k1:
|
||||||
|
return GenerateSecp256k1Key(src)
|
||||||
|
case ECDSA:
|
||||||
|
return GenerateECDSAKeyPair(src)
|
||||||
|
case Ed448:
|
||||||
|
return GenerateEd448Key(src)
|
||||||
|
default:
|
||||||
|
return nil, nil, ErrBadKeyType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateEKeyPair returns an ephemeral public key and returns a function that will compute
|
||||||
|
// the shared secret key. Used in the identify module.
|
||||||
|
//
|
||||||
|
// Focuses only on ECDH now, but can be made more general in the future.
|
||||||
|
func GenerateEKeyPair(curveName string) ([]byte, GenSharedKey, error) {
|
||||||
|
var curve elliptic.Curve
|
||||||
|
|
||||||
|
switch curveName {
|
||||||
|
case "P-256":
|
||||||
|
curve = elliptic.P256()
|
||||||
|
case "P-384":
|
||||||
|
curve = elliptic.P384()
|
||||||
|
case "P-521":
|
||||||
|
curve = elliptic.P521()
|
||||||
|
default:
|
||||||
|
return nil, nil, fmt.Errorf("unknown curve name")
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, x, y, err := elliptic.GenerateKey(curve, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey := elliptic.Marshal(curve, x, y)
|
||||||
|
|
||||||
|
done := func(theirPub []byte) ([]byte, error) {
|
||||||
|
// Verify and unpack node's public key.
|
||||||
|
x, y := elliptic.Unmarshal(curve, theirPub)
|
||||||
|
if x == nil {
|
||||||
|
return nil, fmt.Errorf("malformed public key: %d %v", len(theirPub), theirPub)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !curve.IsOnCurve(x, y) {
|
||||||
|
return nil, errors.New("invalid public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate shared secret.
|
||||||
|
secret, _ := curve.ScalarMult(x, y, priv)
|
||||||
|
|
||||||
|
return secret.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return pubKey, done, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalPublicKey converts a protobuf serialized public key into its
|
||||||
|
// representative object
|
||||||
|
func UnmarshalPublicKey(data []byte) (PubKey, error) {
|
||||||
|
pmes := new(pb.PublicKey)
|
||||||
|
err := proto.Unmarshal(data, pmes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return PublicKeyFromProto(pmes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyFromProto converts an unserialized protobuf PublicKey message
|
||||||
|
// into its representative object.
|
||||||
|
func PublicKeyFromProto(pmes *pb.PublicKey) (PubKey, error) {
|
||||||
|
um, ok := PubKeyUnmarshallers[pmes.GetType()]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrBadKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
data := pmes.GetData()
|
||||||
|
|
||||||
|
pk, err := um(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tpk := pk.(type) {
|
||||||
|
case *RsaPublicKey:
|
||||||
|
tpk.cached, _ = proto.Marshal(pmes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pk, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalPublicKey converts a public key object into a protobuf serialized
|
||||||
|
// public key
|
||||||
|
func MarshalPublicKey(k PubKey) ([]byte, error) {
|
||||||
|
pbmes, err := PublicKeyToProto(k)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return proto.Marshal(pbmes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyToProto converts a public key object into an unserialized
|
||||||
|
// protobuf PublicKey message.
|
||||||
|
func PublicKeyToProto(k PubKey) (*pb.PublicKey, error) {
|
||||||
|
data, err := k.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pb.PublicKey{
|
||||||
|
Type: k.Type().Enum(),
|
||||||
|
Data: data,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalPrivateKey converts a protobuf serialized private key into its
|
||||||
|
// representative object
|
||||||
|
func UnmarshalPrivateKey(data []byte) (PrivKey, error) {
|
||||||
|
pmes := new(pb.PrivateKey)
|
||||||
|
err := proto.Unmarshal(data, pmes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
um, ok := PrivKeyUnmarshallers[pmes.GetType()]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrBadKeyType
|
||||||
|
}
|
||||||
|
|
||||||
|
return um(pmes.GetData())
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalPrivateKey converts a key object into its protobuf serialized form.
|
||||||
|
func MarshalPrivateKey(k PrivKey) ([]byte, error) {
|
||||||
|
data, err := k.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proto.Marshal(&pb.PrivateKey{
|
||||||
|
Type: k.Type().Enum(),
|
||||||
|
Data: data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigDecodeKey decodes from b64 (for config file) to a byte array that can be unmarshalled.
|
||||||
|
func ConfigDecodeKey(b string) ([]byte, error) {
|
||||||
|
return base64.StdEncoding.DecodeString(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConfigEncodeKey encodes a marshalled key to b64 (for config file).
|
||||||
|
func ConfigEncodeKey(b []byte) string {
|
||||||
|
return base64.StdEncoding.EncodeToString(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyEqual checks whether two Keys are equivalent (have identical byte representations).
|
||||||
|
func KeyEqual(k1, k2 Key) bool {
|
||||||
|
if k1 == k2 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return k1.Equals(k2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func basicEquals(k1, k2 Key) bool {
|
||||||
|
if k1.Type() != k2.Type() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := k1.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
b, err := k2.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return subtle.ConstantTimeCompare(a, b) == 1
|
||||||
|
}
|
321
go-libp2p/core/crypto/key_test.go
Normal file
321
go-libp2p/core/crypto/key_test.go
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
package crypto_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/sign/ed448"
|
||||||
|
. "github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
"github.com/libp2p/go-libp2p/core/test"
|
||||||
|
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
secp256k1ecdsa "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
|
||||||
|
"github.com/minio/sha256-simd"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKeys(t *testing.T) {
|
||||||
|
for _, typ := range KeyTypes {
|
||||||
|
testKeyType(typ, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestKeyPairFromKey(t *testing.T) {
|
||||||
|
var (
|
||||||
|
data = []byte(`hello world`)
|
||||||
|
hashed = sha256.Sum256(data)
|
||||||
|
)
|
||||||
|
|
||||||
|
privk, err := secp256k1.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err generating btcec priv key:\n%v", err)
|
||||||
|
}
|
||||||
|
sigK := secp256k1ecdsa.Sign(privk, hashed[:])
|
||||||
|
|
||||||
|
eKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err generating ecdsa priv key:\n%v", err)
|
||||||
|
}
|
||||||
|
sigE, err := eKey.Sign(rand.Reader, hashed[:], crypto.SHA256)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err generating ecdsa sig:\n%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err generating rsa priv key:\n%v", err)
|
||||||
|
}
|
||||||
|
sigR, err := rKey.Sign(rand.Reader, hashed[:], crypto.SHA256)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err generating rsa sig:\n%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, edKey, err := ed25519.GenerateKey(rand.Reader)
|
||||||
|
sigEd := ed25519.Sign(edKey, data[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err generating ed25519 sig:\n%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ed448Key, err := ed448.GenerateKey(rand.Reader)
|
||||||
|
sigEd448 := ed448.Sign(ed448Key, data[:], "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err generating ed448 sig:\n%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range []struct {
|
||||||
|
in crypto.PrivateKey
|
||||||
|
typ pb.KeyType
|
||||||
|
sig []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
eKey,
|
||||||
|
ECDSA,
|
||||||
|
sigE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
privk,
|
||||||
|
Secp256k1,
|
||||||
|
sigK.Serialize(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rKey,
|
||||||
|
RSA,
|
||||||
|
sigR,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&edKey,
|
||||||
|
Ed25519,
|
||||||
|
sigEd,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&ed448Key,
|
||||||
|
Ed448,
|
||||||
|
sigEd448,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(fmt.Sprintf("%v", i), func(t *testing.T) {
|
||||||
|
priv, pub, err := KeyPairFromStdKey(tt.in)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if priv == nil || pub == nil {
|
||||||
|
t.Errorf("received nil private key or public key: %v, %v", priv, pub)
|
||||||
|
}
|
||||||
|
|
||||||
|
if priv == nil || priv.Type() != tt.typ {
|
||||||
|
t.Errorf("want %v; got %v", tt.typ, priv.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := pub.Verify(data[:], tt.sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !v {
|
||||||
|
t.Error("signature was not verified")
|
||||||
|
}
|
||||||
|
|
||||||
|
stdPub, err := PubKeyToStdKey(pub)
|
||||||
|
if stdPub == nil {
|
||||||
|
t.Errorf("err getting std public key from key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdPubBytes []byte
|
||||||
|
|
||||||
|
switch p := stdPub.(type) {
|
||||||
|
case *Secp256k1PublicKey:
|
||||||
|
stdPubBytes, err = p.Raw()
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
stdPubBytes = []byte(p)
|
||||||
|
case ed448.PublicKey:
|
||||||
|
stdPubBytes = []byte(p)
|
||||||
|
default:
|
||||||
|
stdPubBytes, err = x509.MarshalPKIXPublicKey(stdPub)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error while marshaling %v key: %v", reflect.TypeOf(stdPub), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubBytes, err := pub.Raw()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err getting raw bytes for %v key: %v", reflect.TypeOf(pub), err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(stdPubBytes, pubBytes) {
|
||||||
|
t.Errorf("err roundtripping %v key", reflect.TypeOf(pub))
|
||||||
|
}
|
||||||
|
|
||||||
|
stdPriv, err := PrivKeyToStdKey(priv)
|
||||||
|
if stdPub == nil {
|
||||||
|
t.Errorf("err getting std private key from key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdPrivBytes []byte
|
||||||
|
|
||||||
|
switch p := stdPriv.(type) {
|
||||||
|
case *Secp256k1PrivateKey:
|
||||||
|
stdPrivBytes, err = p.Raw()
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
stdPrivBytes, err = x509.MarshalECPrivateKey(p)
|
||||||
|
case *ed25519.PrivateKey:
|
||||||
|
stdPrivBytes = *p
|
||||||
|
case *ed448.PrivateKey:
|
||||||
|
stdPrivBytes = *p
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
stdPrivBytes = x509.MarshalPKCS1PrivateKey(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err marshaling %v key: %v", reflect.TypeOf(stdPriv), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privBytes, err := priv.Raw()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("err getting raw bytes for %v key: %v", reflect.TypeOf(priv), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(stdPrivBytes, privBytes) {
|
||||||
|
t.Errorf("err roundtripping %v key", reflect.TypeOf(priv))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testKeyType(typ int, t *testing.T) {
|
||||||
|
bits := 512
|
||||||
|
if typ == RSA {
|
||||||
|
bits = 2048
|
||||||
|
}
|
||||||
|
sk, pk, err := test.RandTestKeyPair(typ, bits)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
testKeySignature(t, sk)
|
||||||
|
testKeyEncoding(t, sk)
|
||||||
|
testKeyEquals(t, sk)
|
||||||
|
testKeyEquals(t, pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testKeySignature(t *testing.T, sk PrivKey) {
|
||||||
|
pk := sk.GetPublic()
|
||||||
|
|
||||||
|
text := make([]byte, 16)
|
||||||
|
if _, err := rand.Read(text); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := sk.Sign(text)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
valid, err := pk.Verify(text, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
t.Fatal("Invalid signature.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testKeyEncoding(t *testing.T, sk PrivKey) {
|
||||||
|
skbm, err := MarshalPrivateKey(sk)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sk2, err := UnmarshalPrivateKey(skbm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sk.Equals(sk2) {
|
||||||
|
t.Error("Unmarshaled private key didn't match original.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
skbm2, err := MarshalPrivateKey(sk2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(skbm, skbm2) {
|
||||||
|
t.Error("skb -> marshal -> unmarshal -> skb failed.\n", skbm, "\n", skbm2)
|
||||||
|
}
|
||||||
|
|
||||||
|
pk := sk.GetPublic()
|
||||||
|
pkbm, err := MarshalPublicKey(pk)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pk2, err := UnmarshalPublicKey(pkbm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pk.Equals(pk2) {
|
||||||
|
t.Error("Unmarshaled public key didn't match original.\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
pkbm2, err := MarshalPublicKey(pk)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(pkbm, pkbm2) {
|
||||||
|
t.Error("skb -> marshal -> unmarshal -> skb failed.\n", pkbm, "\n", pkbm2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testKeyEquals(t *testing.T, k Key) {
|
||||||
|
// kb, err := k.Raw()
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
if !KeyEqual(k, k) {
|
||||||
|
t.Fatal("Key not equal to itself.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// bad test, relies on deep internals..
|
||||||
|
// if !KeyEqual(k, testkey(kb)) {
|
||||||
|
// t.Fatal("Key not equal to key with same bytes.")
|
||||||
|
// }
|
||||||
|
|
||||||
|
sk, pk, err := test.RandTestKeyPair(RSA, 2048)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if KeyEqual(k, sk) {
|
||||||
|
t.Fatal("Keys should not equal.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if KeyEqual(k, pk) {
|
||||||
|
t.Fatal("Keys should not equal.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnknownCurveErrors(t *testing.T) {
|
||||||
|
_, _, err := GenerateEKeyPair("P-256")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _, err = GenerateEKeyPair("error-please")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected invalid key type to error")
|
||||||
|
}
|
||||||
|
}
|
88
go-libp2p/core/crypto/key_to_stdlib.go
Normal file
88
go-libp2p/core/crypto/key_to_stdlib.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rsa"
|
||||||
|
|
||||||
|
"github.com/cloudflare/circl/sign/ed448"
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyPairFromStdKey wraps standard library (and secp256k1) private keys in libp2p/go-libp2p/core/crypto keys
|
||||||
|
func KeyPairFromStdKey(priv crypto.PrivateKey) (PrivKey, PubKey, error) {
|
||||||
|
if priv == nil {
|
||||||
|
return nil, nil, ErrNilPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p := priv.(type) {
|
||||||
|
case *rsa.PrivateKey:
|
||||||
|
return &RsaPrivateKey{*p}, &RsaPublicKey{k: p.PublicKey}, nil
|
||||||
|
|
||||||
|
case *ecdsa.PrivateKey:
|
||||||
|
return &ECDSAPrivateKey{p}, &ECDSAPublicKey{&p.PublicKey}, nil
|
||||||
|
|
||||||
|
case *ed25519.PrivateKey:
|
||||||
|
pubIfc := p.Public()
|
||||||
|
pub, _ := pubIfc.(ed25519.PublicKey)
|
||||||
|
return &Ed25519PrivateKey{*p}, &Ed25519PublicKey{pub}, nil
|
||||||
|
|
||||||
|
case *secp256k1.PrivateKey:
|
||||||
|
sPriv := Secp256k1PrivateKey(*p)
|
||||||
|
sPub := Secp256k1PublicKey(*p.PubKey())
|
||||||
|
return &sPriv, &sPub, nil
|
||||||
|
|
||||||
|
case *ed448.PrivateKey:
|
||||||
|
pubIfc := p.Public()
|
||||||
|
pub, _ := pubIfc.(ed448.PublicKey)
|
||||||
|
return &Ed448PrivateKey{*p}, &Ed448PublicKey{pub}, nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, nil, ErrBadKeyType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivKeyToStdKey converts libp2p/go-libp2p/core/crypto private keys to standard library (and secp256k1) private keys
|
||||||
|
func PrivKeyToStdKey(priv PrivKey) (crypto.PrivateKey, error) {
|
||||||
|
if priv == nil {
|
||||||
|
return nil, ErrNilPrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p := priv.(type) {
|
||||||
|
case *RsaPrivateKey:
|
||||||
|
return &p.sk, nil
|
||||||
|
case *ECDSAPrivateKey:
|
||||||
|
return p.priv, nil
|
||||||
|
case *Ed25519PrivateKey:
|
||||||
|
return &p.k, nil
|
||||||
|
case *Secp256k1PrivateKey:
|
||||||
|
return p, nil
|
||||||
|
case *Ed448PrivateKey:
|
||||||
|
return &p.k, nil
|
||||||
|
default:
|
||||||
|
return nil, ErrBadKeyType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PubKeyToStdKey converts libp2p/go-libp2p/core/crypto private keys to standard library (and secp256k1) public keys
|
||||||
|
func PubKeyToStdKey(pub PubKey) (crypto.PublicKey, error) {
|
||||||
|
if pub == nil {
|
||||||
|
return nil, ErrNilPublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p := pub.(type) {
|
||||||
|
case *RsaPublicKey:
|
||||||
|
return &p.k, nil
|
||||||
|
case *ECDSAPublicKey:
|
||||||
|
return p.pub, nil
|
||||||
|
case *Ed25519PublicKey:
|
||||||
|
return p.k, nil
|
||||||
|
case *Secp256k1PublicKey:
|
||||||
|
return p, nil
|
||||||
|
case *Ed448PublicKey:
|
||||||
|
return p.k, nil
|
||||||
|
default:
|
||||||
|
return nil, ErrBadKeyType
|
||||||
|
}
|
||||||
|
}
|
301
go-libp2p/core/crypto/pb/crypto.pb.go
Normal file
301
go-libp2p/core/crypto/pb/crypto.pb.go
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// protoc-gen-go v1.30.0
|
||||||
|
// protoc v3.21.12
|
||||||
|
// source: pb/crypto.proto
|
||||||
|
|
||||||
|
package pb
|
||||||
|
|
||||||
|
import (
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
|
reflect "reflect"
|
||||||
|
sync "sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Verify that this generated code is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||||
|
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||||
|
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyType_RSA KeyType = 0
|
||||||
|
KeyType_Ed25519 KeyType = 1
|
||||||
|
KeyType_Secp256k1 KeyType = 2
|
||||||
|
KeyType_ECDSA KeyType = 3
|
||||||
|
KeyType_Ed448 KeyType = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enum value maps for KeyType.
|
||||||
|
var (
|
||||||
|
KeyType_name = map[int32]string{
|
||||||
|
0: "RSA",
|
||||||
|
1: "Ed25519",
|
||||||
|
2: "Secp256k1",
|
||||||
|
3: "ECDSA",
|
||||||
|
4: "Ed448",
|
||||||
|
}
|
||||||
|
KeyType_value = map[string]int32{
|
||||||
|
"RSA": 0,
|
||||||
|
"Ed25519": 1,
|
||||||
|
"Secp256k1": 2,
|
||||||
|
"ECDSA": 3,
|
||||||
|
"Ed448": 4,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (x KeyType) Enum() *KeyType {
|
||||||
|
p := new(KeyType)
|
||||||
|
*p = x
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x KeyType) String() string {
|
||||||
|
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (KeyType) Descriptor() protoreflect.EnumDescriptor {
|
||||||
|
return file_pb_crypto_proto_enumTypes[0].Descriptor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (KeyType) Type() protoreflect.EnumType {
|
||||||
|
return &file_pb_crypto_proto_enumTypes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x KeyType) Number() protoreflect.EnumNumber {
|
||||||
|
return protoreflect.EnumNumber(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Do not use.
|
||||||
|
func (x *KeyType) UnmarshalJSON(b []byte) error {
|
||||||
|
num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*x = KeyType(num)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use KeyType.Descriptor instead.
|
||||||
|
func (KeyType) EnumDescriptor() ([]byte, []int) {
|
||||||
|
return file_pb_crypto_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PublicKey struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Type *KeyType `protobuf:"varint,1,req,name=Type,enum=crypto.pb.KeyType" json:"Type,omitempty"`
|
||||||
|
Data []byte `protobuf:"bytes,2,req,name=Data" json:"Data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PublicKey) Reset() {
|
||||||
|
*x = PublicKey{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_pb_crypto_proto_msgTypes[0]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PublicKey) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PublicKey) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PublicKey) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_pb_crypto_proto_msgTypes[0]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PublicKey.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PublicKey) Descriptor() ([]byte, []int) {
|
||||||
|
return file_pb_crypto_proto_rawDescGZIP(), []int{0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PublicKey) GetType() KeyType {
|
||||||
|
if x != nil && x.Type != nil {
|
||||||
|
return *x.Type
|
||||||
|
}
|
||||||
|
return KeyType_RSA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PublicKey) GetData() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrivateKey struct {
|
||||||
|
state protoimpl.MessageState
|
||||||
|
sizeCache protoimpl.SizeCache
|
||||||
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
|
Type *KeyType `protobuf:"varint,1,req,name=Type,enum=crypto.pb.KeyType" json:"Type,omitempty"`
|
||||||
|
Data []byte `protobuf:"bytes,2,req,name=Data" json:"Data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivateKey) Reset() {
|
||||||
|
*x = PrivateKey{}
|
||||||
|
if protoimpl.UnsafeEnabled {
|
||||||
|
mi := &file_pb_crypto_proto_msgTypes[1]
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivateKey) String() string {
|
||||||
|
return protoimpl.X.MessageStringOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*PrivateKey) ProtoMessage() {}
|
||||||
|
|
||||||
|
func (x *PrivateKey) ProtoReflect() protoreflect.Message {
|
||||||
|
mi := &file_pb_crypto_proto_msgTypes[1]
|
||||||
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
ms.StoreMessageInfo(mi)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
|
}
|
||||||
|
return mi.MessageOf(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: Use PrivateKey.ProtoReflect.Descriptor instead.
|
||||||
|
func (*PrivateKey) Descriptor() ([]byte, []int) {
|
||||||
|
return file_pb_crypto_proto_rawDescGZIP(), []int{1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivateKey) GetType() KeyType {
|
||||||
|
if x != nil && x.Type != nil {
|
||||||
|
return *x.Type
|
||||||
|
}
|
||||||
|
return KeyType_RSA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *PrivateKey) GetData() []byte {
|
||||||
|
if x != nil {
|
||||||
|
return x.Data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var File_pb_crypto_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
|
var file_pb_crypto_proto_rawDesc = []byte{
|
||||||
|
0x0a, 0x0f, 0x70, 0x62, 0x2f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||||
|
0x6f, 0x12, 0x09, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x62, 0x22, 0x47, 0x0a, 0x09,
|
||||||
|
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x04, 0x54, 0x79, 0x70,
|
||||||
|
0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f,
|
||||||
|
0x2e, 0x70, 0x62, 0x2e, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70,
|
||||||
|
0x65, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x02, 0x28, 0x0c, 0x52,
|
||||||
|
0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0x48, 0x0a, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65,
|
||||||
|
0x4b, 0x65, 0x79, 0x12, 0x26, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28,
|
||||||
|
0x0e, 0x32, 0x12, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x2e, 0x70, 0x62, 0x2e, 0x4b, 0x65,
|
||||||
|
0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x44,
|
||||||
|
0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x02, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x2a,
|
||||||
|
0x44, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x52, 0x53,
|
||||||
|
0x41, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x10, 0x01,
|
||||||
|
0x12, 0x0d, 0x0a, 0x09, 0x53, 0x65, 0x63, 0x70, 0x32, 0x35, 0x36, 0x6b, 0x31, 0x10, 0x02, 0x12,
|
||||||
|
0x09, 0x0a, 0x05, 0x45, 0x43, 0x44, 0x53, 0x41, 0x10, 0x03, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x64,
|
||||||
|
0x34, 0x34, 0x38, 0x10, 0x04, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||||
|
0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x62, 0x70, 0x32, 0x70, 0x2f, 0x67, 0x6f, 0x2d, 0x6c, 0x69,
|
||||||
|
0x62, 0x70, 0x32, 0x70, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f,
|
||||||
|
0x2f, 0x70, 0x62,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
file_pb_crypto_proto_rawDescOnce sync.Once
|
||||||
|
file_pb_crypto_proto_rawDescData = file_pb_crypto_proto_rawDesc
|
||||||
|
)
|
||||||
|
|
||||||
|
func file_pb_crypto_proto_rawDescGZIP() []byte {
|
||||||
|
file_pb_crypto_proto_rawDescOnce.Do(func() {
|
||||||
|
file_pb_crypto_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_crypto_proto_rawDescData)
|
||||||
|
})
|
||||||
|
return file_pb_crypto_proto_rawDescData
|
||||||
|
}
|
||||||
|
|
||||||
|
var file_pb_crypto_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||||
|
var file_pb_crypto_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||||
|
var file_pb_crypto_proto_goTypes = []interface{}{
|
||||||
|
(KeyType)(0), // 0: crypto.pb.KeyType
|
||||||
|
(*PublicKey)(nil), // 1: crypto.pb.PublicKey
|
||||||
|
(*PrivateKey)(nil), // 2: crypto.pb.PrivateKey
|
||||||
|
}
|
||||||
|
var file_pb_crypto_proto_depIdxs = []int32{
|
||||||
|
0, // 0: crypto.pb.PublicKey.Type:type_name -> crypto.pb.KeyType
|
||||||
|
0, // 1: crypto.pb.PrivateKey.Type:type_name -> crypto.pb.KeyType
|
||||||
|
2, // [2:2] is the sub-list for method output_type
|
||||||
|
2, // [2:2] is the sub-list for method input_type
|
||||||
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
|
2, // [2:2] is the sub-list for extension extendee
|
||||||
|
0, // [0:2] is the sub-list for field type_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { file_pb_crypto_proto_init() }
|
||||||
|
func file_pb_crypto_proto_init() {
|
||||||
|
if File_pb_crypto_proto != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !protoimpl.UnsafeEnabled {
|
||||||
|
file_pb_crypto_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*PublicKey); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file_pb_crypto_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||||
|
switch v := v.(*PrivateKey); i {
|
||||||
|
case 0:
|
||||||
|
return &v.state
|
||||||
|
case 1:
|
||||||
|
return &v.sizeCache
|
||||||
|
case 2:
|
||||||
|
return &v.unknownFields
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type x struct{}
|
||||||
|
out := protoimpl.TypeBuilder{
|
||||||
|
File: protoimpl.DescBuilder{
|
||||||
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
|
RawDescriptor: file_pb_crypto_proto_rawDesc,
|
||||||
|
NumEnums: 1,
|
||||||
|
NumMessages: 2,
|
||||||
|
NumExtensions: 0,
|
||||||
|
NumServices: 0,
|
||||||
|
},
|
||||||
|
GoTypes: file_pb_crypto_proto_goTypes,
|
||||||
|
DependencyIndexes: file_pb_crypto_proto_depIdxs,
|
||||||
|
EnumInfos: file_pb_crypto_proto_enumTypes,
|
||||||
|
MessageInfos: file_pb_crypto_proto_msgTypes,
|
||||||
|
}.Build()
|
||||||
|
File_pb_crypto_proto = out.File
|
||||||
|
file_pb_crypto_proto_rawDesc = nil
|
||||||
|
file_pb_crypto_proto_goTypes = nil
|
||||||
|
file_pb_crypto_proto_depIdxs = nil
|
||||||
|
}
|
23
go-libp2p/core/crypto/pb/crypto.proto
Normal file
23
go-libp2p/core/crypto/pb/crypto.proto
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
syntax = "proto2";
|
||||||
|
|
||||||
|
package crypto.pb;
|
||||||
|
|
||||||
|
option go_package = "github.com/libp2p/go-libp2p/core/crypto/pb";
|
||||||
|
|
||||||
|
enum KeyType {
|
||||||
|
RSA = 0;
|
||||||
|
Ed25519 = 1;
|
||||||
|
Secp256k1 = 2;
|
||||||
|
ECDSA = 3;
|
||||||
|
Ed448 = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PublicKey {
|
||||||
|
required KeyType Type = 1;
|
||||||
|
required bytes Data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PrivateKey {
|
||||||
|
required KeyType Type = 1;
|
||||||
|
required bytes Data = 2;
|
||||||
|
}
|
28
go-libp2p/core/crypto/rsa_common.go
Normal file
28
go-libp2p/core/crypto/rsa_common.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WeakRsaKeyEnv is an environment variable which, when set, lowers the
|
||||||
|
// minimum required bits of RSA keys to 512. This should be used exclusively in
|
||||||
|
// test situations.
|
||||||
|
const WeakRsaKeyEnv = "LIBP2P_ALLOW_WEAK_RSA_KEYS"
|
||||||
|
|
||||||
|
var MinRsaKeyBits = 2048
|
||||||
|
|
||||||
|
var maxRsaKeyBits = 8192
|
||||||
|
|
||||||
|
// ErrRsaKeyTooSmall is returned when trying to generate or parse an RSA key
|
||||||
|
// that's smaller than MinRsaKeyBits bits. In test
|
||||||
|
var ErrRsaKeyTooSmall error
|
||||||
|
var ErrRsaKeyTooBig error = fmt.Errorf("rsa keys must be <= %d bits", maxRsaKeyBits)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if _, ok := os.LookupEnv(WeakRsaKeyEnv); ok {
|
||||||
|
MinRsaKeyBits = 512
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrRsaKeyTooSmall = fmt.Errorf("rsa keys must be >= %d bits to be useful", MinRsaKeyBits)
|
||||||
|
}
|
155
go-libp2p/core/crypto/rsa_go.go
Normal file
155
go-libp2p/core/crypto/rsa_go.go
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
"github.com/libp2p/go-libp2p/core/internal/catch"
|
||||||
|
|
||||||
|
"github.com/minio/sha256-simd"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RsaPrivateKey is a rsa private key
|
||||||
|
type RsaPrivateKey struct {
|
||||||
|
sk rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// RsaPublicKey is a rsa public key
|
||||||
|
type RsaPublicKey struct {
|
||||||
|
k rsa.PublicKey
|
||||||
|
|
||||||
|
cached []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateRSAKeyPair generates a new rsa private and public key
|
||||||
|
func GenerateRSAKeyPair(bits int, src io.Reader) (PrivKey, PubKey, error) {
|
||||||
|
if bits < MinRsaKeyBits {
|
||||||
|
return nil, nil, ErrRsaKeyTooSmall
|
||||||
|
}
|
||||||
|
if bits > maxRsaKeyBits {
|
||||||
|
return nil, nil, ErrRsaKeyTooBig
|
||||||
|
}
|
||||||
|
priv, err := rsa.GenerateKey(src, bits)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
pk := priv.PublicKey
|
||||||
|
return &RsaPrivateKey{sk: *priv}, &RsaPublicKey{k: pk}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify compares a signature against input data
|
||||||
|
func (pk *RsaPublicKey) Verify(data, sig []byte) (success bool, err error) {
|
||||||
|
defer func() {
|
||||||
|
catch.HandlePanic(recover(), &err, "RSA signature verification")
|
||||||
|
|
||||||
|
// To be safe
|
||||||
|
if err != nil {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
hashed := sha256.Sum256(data)
|
||||||
|
err = rsa.VerifyPKCS1v15(&pk.k, crypto.SHA256, hashed[:], sig)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk *RsaPublicKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_RSA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pk *RsaPublicKey) Raw() (res []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "RSA public-key marshaling") }()
|
||||||
|
return x509.MarshalPKIXPublicKey(&pk.k)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals checks whether this key is equal to another
|
||||||
|
func (pk *RsaPublicKey) Equals(k Key) bool {
|
||||||
|
// make sure this is a rsa public key
|
||||||
|
other, ok := (k).(*RsaPublicKey)
|
||||||
|
if !ok {
|
||||||
|
return basicEquals(pk, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pk.k.N.Cmp(other.k.N) == 0 && pk.k.E == other.k.E
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns a signature of the input data
|
||||||
|
func (sk *RsaPrivateKey) Sign(message []byte) (sig []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "RSA signing") }()
|
||||||
|
hashed := sha256.Sum256(message)
|
||||||
|
return rsa.SignPKCS1v15(rand.Reader, &sk.sk, crypto.SHA256, hashed[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublic returns a public key
|
||||||
|
func (sk *RsaPrivateKey) GetPublic() PubKey {
|
||||||
|
return &RsaPublicKey{k: sk.sk.PublicKey}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sk *RsaPrivateKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_RSA
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sk *RsaPrivateKey) Raw() (res []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "RSA private-key marshaling") }()
|
||||||
|
b := x509.MarshalPKCS1PrivateKey(&sk.sk)
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals checks whether this key is equal to another
|
||||||
|
func (sk *RsaPrivateKey) Equals(k Key) bool {
|
||||||
|
// make sure this is a rsa public key
|
||||||
|
other, ok := (k).(*RsaPrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return basicEquals(sk, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
a := sk.sk
|
||||||
|
b := other.sk
|
||||||
|
|
||||||
|
// Don't care about constant time. We're only comparing the public half.
|
||||||
|
return a.PublicKey.N.Cmp(b.PublicKey.N) == 0 && a.PublicKey.E == b.PublicKey.E
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalRsaPrivateKey returns a private key from the input x509 bytes
|
||||||
|
func UnmarshalRsaPrivateKey(b []byte) (key PrivKey, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "RSA private-key unmarshaling") }()
|
||||||
|
sk, err := x509.ParsePKCS1PrivateKey(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if sk.N.BitLen() < MinRsaKeyBits {
|
||||||
|
return nil, ErrRsaKeyTooSmall
|
||||||
|
}
|
||||||
|
if sk.N.BitLen() > maxRsaKeyBits {
|
||||||
|
return nil, ErrRsaKeyTooBig
|
||||||
|
}
|
||||||
|
return &RsaPrivateKey{sk: *sk}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalRsaPublicKey returns a public key from the input x509 bytes
|
||||||
|
func UnmarshalRsaPublicKey(b []byte) (key PubKey, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "RSA public-key unmarshaling") }()
|
||||||
|
pub, err := x509.ParsePKIXPublicKey(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pk, ok := pub.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("not actually an rsa public key")
|
||||||
|
}
|
||||||
|
if pk.N.BitLen() < MinRsaKeyBits {
|
||||||
|
return nil, ErrRsaKeyTooSmall
|
||||||
|
}
|
||||||
|
if pk.N.BitLen() > maxRsaKeyBits {
|
||||||
|
return nil, ErrRsaKeyTooBig
|
||||||
|
}
|
||||||
|
|
||||||
|
return &RsaPublicKey{k: *pk}, nil
|
||||||
|
}
|
162
go-libp2p/core/crypto/rsa_test.go
Normal file
162
go-libp2p/core/crypto/rsa_test.go
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRSABasicSignAndVerify(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateRSAKeyPair(2048, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []byte("hello! and welcome to some awesome crypto primitives")
|
||||||
|
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didnt match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// change data
|
||||||
|
data[0] = ^data[0]
|
||||||
|
ok, err = pub.Verify(data, sig)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("should have produced a verification error")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Fatal("signature matched and shouldn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSASmallKey(t *testing.T) {
|
||||||
|
_, _, err := GenerateRSAKeyPair(MinRsaKeyBits/2, rand.Reader)
|
||||||
|
if err != ErrRsaKeyTooSmall {
|
||||||
|
t.Fatal("should have refused to create small RSA key")
|
||||||
|
}
|
||||||
|
MinRsaKeyBits /= 2
|
||||||
|
badPriv, badPub, err := GenerateRSAKeyPair(MinRsaKeyBits, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should have succeeded, got: %s", err)
|
||||||
|
}
|
||||||
|
pubBytes, err := MarshalPublicKey(badPub)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
privBytes, err := MarshalPrivateKey(badPriv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
MinRsaKeyBits *= 2
|
||||||
|
_, err = UnmarshalPublicKey(pubBytes)
|
||||||
|
if err != ErrRsaKeyTooSmall {
|
||||||
|
t.Fatal("should have refused to unmarshal a weak key")
|
||||||
|
}
|
||||||
|
_, err = UnmarshalPrivateKey(privBytes)
|
||||||
|
if err != ErrRsaKeyTooSmall {
|
||||||
|
t.Fatal("should have refused to unmarshal a weak key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSABigKeyFailsToGenerate(t *testing.T) {
|
||||||
|
_, _, err := GenerateRSAKeyPair(maxRsaKeyBits*2, rand.Reader)
|
||||||
|
if err != ErrRsaKeyTooBig {
|
||||||
|
t.Fatal("should have refused to create too big RSA key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSABigKey(t *testing.T) {
|
||||||
|
// Make the global limit smaller for this test to run faster.
|
||||||
|
// Note we also change the limit below, but this is different
|
||||||
|
origSize := maxRsaKeyBits
|
||||||
|
maxRsaKeyBits = 2048
|
||||||
|
defer func() { maxRsaKeyBits = origSize }() //
|
||||||
|
|
||||||
|
maxRsaKeyBits *= 2
|
||||||
|
badPriv, badPub, err := GenerateRSAKeyPair(maxRsaKeyBits, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("should have succeeded, got: %s", err)
|
||||||
|
}
|
||||||
|
pubBytes, err := MarshalPublicKey(badPub)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
privBytes, err := MarshalPrivateKey(badPriv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
maxRsaKeyBits /= 2
|
||||||
|
_, err = UnmarshalPublicKey(pubBytes)
|
||||||
|
if err != ErrRsaKeyTooBig {
|
||||||
|
t.Fatal("should have refused to unmarshal a too big key")
|
||||||
|
}
|
||||||
|
_, err = UnmarshalPrivateKey(privBytes)
|
||||||
|
if err != ErrRsaKeyTooBig {
|
||||||
|
t.Fatal("should have refused to unmarshal a too big key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSASignZero(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateRSAKeyPair(2048, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, 0)
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRSAMarshalLoop(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateRSAKeyPair(2048, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privB, err := MarshalPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privNew, err := UnmarshalPrivateKey(privB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !priv.Equals(privNew) || !privNew.Equals(priv) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubB, err := MarshalPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pubNew, err := UnmarshalPublicKey(pubB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pub.Equals(pubNew) || !pubNew.Equals(pub) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
}
|
127
go-libp2p/core/crypto/secp256k1.go
Normal file
127
go-libp2p/core/crypto/secp256k1.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
pb "github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
"github.com/libp2p/go-libp2p/core/internal/catch"
|
||||||
|
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa"
|
||||||
|
"github.com/minio/sha256-simd"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Secp256k1PrivateKey is a Secp256k1 private key
|
||||||
|
type Secp256k1PrivateKey secp256k1.PrivateKey
|
||||||
|
|
||||||
|
// Secp256k1PublicKey is a Secp256k1 public key
|
||||||
|
type Secp256k1PublicKey secp256k1.PublicKey
|
||||||
|
|
||||||
|
// GenerateSecp256k1Key generates a new Secp256k1 private and public key pair
|
||||||
|
func GenerateSecp256k1Key(src io.Reader) (PrivKey, PubKey, error) {
|
||||||
|
privk, err := secp256k1.GeneratePrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
k := (*Secp256k1PrivateKey)(privk)
|
||||||
|
return k, k.GetPublic(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalSecp256k1PrivateKey returns a private key from bytes
|
||||||
|
func UnmarshalSecp256k1PrivateKey(data []byte) (k PrivKey, err error) {
|
||||||
|
if len(data) != secp256k1.PrivKeyBytesLen {
|
||||||
|
return nil, fmt.Errorf("expected secp256k1 data size to be %d", secp256k1.PrivKeyBytesLen)
|
||||||
|
}
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "secp256k1 private-key unmarshal") }()
|
||||||
|
|
||||||
|
privk := secp256k1.PrivKeyFromBytes(data)
|
||||||
|
return (*Secp256k1PrivateKey)(privk), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalSecp256k1PublicKey returns a public key from bytes
|
||||||
|
func UnmarshalSecp256k1PublicKey(data []byte) (_k PubKey, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "secp256k1 public-key unmarshal") }()
|
||||||
|
k, err := secp256k1.ParsePubKey(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*Secp256k1PublicKey)(k), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the private key type
|
||||||
|
func (k *Secp256k1PrivateKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_Secp256k1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw returns the bytes of the key
|
||||||
|
func (k *Secp256k1PrivateKey) Raw() ([]byte, error) {
|
||||||
|
return (*secp256k1.PrivateKey)(k).Serialize(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares two private keys
|
||||||
|
func (k *Secp256k1PrivateKey) Equals(o Key) bool {
|
||||||
|
sk, ok := o.(*Secp256k1PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return basicEquals(k, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return k.GetPublic().Equals(sk.GetPublic())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign returns a signature from input data
|
||||||
|
func (k *Secp256k1PrivateKey) Sign(data []byte) (_sig []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "secp256k1 signing") }()
|
||||||
|
key := (*secp256k1.PrivateKey)(k)
|
||||||
|
hash := sha256.Sum256(data)
|
||||||
|
sig := ecdsa.Sign(key, hash[:])
|
||||||
|
|
||||||
|
return sig.Serialize(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPublic returns a public key
|
||||||
|
func (k *Secp256k1PrivateKey) GetPublic() PubKey {
|
||||||
|
return (*Secp256k1PublicKey)((*secp256k1.PrivateKey)(k).PubKey())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type returns the public key type
|
||||||
|
func (k *Secp256k1PublicKey) Type() pb.KeyType {
|
||||||
|
return pb.KeyType_Secp256k1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Raw returns the bytes of the key
|
||||||
|
func (k *Secp256k1PublicKey) Raw() (res []byte, err error) {
|
||||||
|
defer func() { catch.HandlePanic(recover(), &err, "secp256k1 public key marshaling") }()
|
||||||
|
return (*secp256k1.PublicKey)(k).SerializeCompressed(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals compares two public keys
|
||||||
|
func (k *Secp256k1PublicKey) Equals(o Key) bool {
|
||||||
|
sk, ok := o.(*Secp256k1PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return basicEquals(k, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (*secp256k1.PublicKey)(k).IsEqual((*secp256k1.PublicKey)(sk))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify compares a signature against the input data
|
||||||
|
func (k *Secp256k1PublicKey) Verify(data []byte, sigStr []byte) (success bool, err error) {
|
||||||
|
defer func() {
|
||||||
|
catch.HandlePanic(recover(), &err, "secp256k1 signature verification")
|
||||||
|
|
||||||
|
// To be extra safe.
|
||||||
|
if err != nil {
|
||||||
|
success = false
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
sig, err := ecdsa.ParseDERSignature(sigStr)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.Sum256(data)
|
||||||
|
return sig.Verify(hash[:], (*secp256k1.PublicKey)(k)), nil
|
||||||
|
}
|
96
go-libp2p/core/crypto/secp256k1_test.go
Normal file
96
go-libp2p/core/crypto/secp256k1_test.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSecp256k1BasicSignAndVerify(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateSecp256k1Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := []byte("hello! and welcome to some awesome crypto primitives")
|
||||||
|
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didnt match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// change data
|
||||||
|
data[0] = ^data[0]
|
||||||
|
ok, err = pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Fatal("signature matched and shouldn't")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecp256k1SignZero(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateSecp256k1Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, 0)
|
||||||
|
sig, err := priv.Sign(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := pub.Verify(data, sig)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("signature didn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecp256k1MarshalLoop(t *testing.T) {
|
||||||
|
priv, pub, err := GenerateSecp256k1Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privB, err := MarshalPrivateKey(priv)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
privNew, err := UnmarshalPrivateKey(privB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !priv.Equals(privNew) || !privNew.Equals(priv) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubB, err := MarshalPublicKey(pub)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
pubNew, err := UnmarshalPublicKey(pubB)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pub.Equals(pubNew) || !pubNew.Equals(pub) {
|
||||||
|
t.Fatal("keys are not equal")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
go-libp2p/core/crypto/test_data/0.priv
Normal file
BIN
go-libp2p/core/crypto/test_data/0.priv
Normal file
Binary file not shown.
BIN
go-libp2p/core/crypto/test_data/0.pub
Normal file
BIN
go-libp2p/core/crypto/test_data/0.pub
Normal file
Binary file not shown.
BIN
go-libp2p/core/crypto/test_data/0.sig
Normal file
BIN
go-libp2p/core/crypto/test_data/0.sig
Normal file
Binary file not shown.
1
go-libp2p/core/crypto/test_data/2.priv
Normal file
1
go-libp2p/core/crypto/test_data/2.priv
Normal file
@ -0,0 +1 @@
|
|||||||
|
1A½`jPLDò4”ØóNò[¹µ-ªÐX¾ƒ¶àF±X
|
1
go-libp2p/core/crypto/test_data/2.pub
Normal file
1
go-libp2p/core/crypto/test_data/2.pub
Normal file
@ -0,0 +1 @@
|
|||||||
|
!5@„*ψ¤5Q©Mƒƒ¥U©’&Pk<50>ωS<CF89>磡³ΦΆ
|
1
go-libp2p/core/crypto/test_data/2.sig
Normal file
1
go-libp2p/core/crypto/test_data/2.sig
Normal file
@ -0,0 +1 @@
|
|||||||
|
0D 1§Ö3ÂóäZŤCuú¨Ü›˘@’ĹőłĘŇČţLó<4C>ň IęŤ!źE†<EFBFBD>ŇGuŐCꏲ<EFBFBD>pCGű5I<@;ÂÂY˛ťž
|
BIN
go-libp2p/core/crypto/test_data/3.priv
Normal file
BIN
go-libp2p/core/crypto/test_data/3.priv
Normal file
Binary file not shown.
BIN
go-libp2p/core/crypto/test_data/3.pub
Normal file
BIN
go-libp2p/core/crypto/test_data/3.pub
Normal file
Binary file not shown.
BIN
go-libp2p/core/crypto/test_data/3.sig
Normal file
BIN
go-libp2p/core/crypto/test_data/3.sig
Normal file
Binary file not shown.
27
go-libp2p/core/discovery/discovery.go
Normal file
27
go-libp2p/core/discovery/discovery.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Package discovery provides service advertisement and peer discovery interfaces for libp2p.
|
||||||
|
package discovery
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Advertiser is an interface for advertising services
|
||||||
|
type Advertiser interface {
|
||||||
|
// Advertise advertises a service
|
||||||
|
Advertise(ctx context.Context, ns string, opts ...Option) (time.Duration, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discoverer is an interface for peer discovery
|
||||||
|
type Discoverer interface {
|
||||||
|
// FindPeers discovers peers providing a service
|
||||||
|
FindPeers(ctx context.Context, ns string, opts ...Option) (<-chan peer.AddrInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discovery is an interface that combines service advertisement and peer discovery
|
||||||
|
type Discovery interface {
|
||||||
|
Advertiser
|
||||||
|
Discoverer
|
||||||
|
}
|
41
go-libp2p/core/discovery/options.go
Normal file
41
go-libp2p/core/discovery/options.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package discovery
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// DiscoveryOpt is a single discovery option.
|
||||||
|
type Option func(opts *Options) error
|
||||||
|
|
||||||
|
// DiscoveryOpts is a set of discovery options.
|
||||||
|
type Options struct {
|
||||||
|
Ttl time.Duration
|
||||||
|
Limit int
|
||||||
|
|
||||||
|
// Other (implementation-specific) options
|
||||||
|
Other map[interface{}]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply applies the given options to this DiscoveryOpts
|
||||||
|
func (opts *Options) Apply(options ...Option) error {
|
||||||
|
for _, o := range options {
|
||||||
|
if err := o(opts); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TTL is an option that provides a hint for the duration of an advertisement
|
||||||
|
func TTL(ttl time.Duration) Option {
|
||||||
|
return func(opts *Options) error {
|
||||||
|
opts.Ttl = ttl
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit is an option that provides an upper bound on the peer count for discovery
|
||||||
|
func Limit(limit int) Option {
|
||||||
|
return func(opts *Options) error {
|
||||||
|
opts.Limit = limit
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
83
go-libp2p/core/event/addrs.go
Normal file
83
go-libp2p/core/event/addrs.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/libp2p/go-libp2p/core/record"
|
||||||
|
|
||||||
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddrAction represents an action taken on one of a Host's listen addresses.
|
||||||
|
// It is used to add context to address change events in EvtLocalAddressesUpdated.
|
||||||
|
type AddrAction int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Unknown means that the event producer was unable to determine why the address
|
||||||
|
// is in the current state.
|
||||||
|
Unknown AddrAction = iota
|
||||||
|
|
||||||
|
// Added means that the address is new and was not present prior to the event.
|
||||||
|
Added
|
||||||
|
|
||||||
|
// Maintained means that the address was not altered between the current and
|
||||||
|
// previous states.
|
||||||
|
Maintained
|
||||||
|
|
||||||
|
// Removed means that the address was removed from the Host.
|
||||||
|
Removed
|
||||||
|
)
|
||||||
|
|
||||||
|
// UpdatedAddress is used in the EvtLocalAddressesUpdated event to convey
|
||||||
|
// address change information.
|
||||||
|
type UpdatedAddress struct {
|
||||||
|
// Address contains the address that was updated.
|
||||||
|
Address ma.Multiaddr
|
||||||
|
|
||||||
|
// Action indicates what action was taken on the address during the
|
||||||
|
// event. May be Unknown if the event producer cannot produce diffs.
|
||||||
|
Action AddrAction
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvtLocalAddressesUpdated should be emitted when the set of listen addresses for
|
||||||
|
// the local host changes. This may happen for a number of reasons. For example,
|
||||||
|
// we may have opened a new relay connection, established a new NAT mapping via
|
||||||
|
// UPnP, or been informed of our observed address by another peer.
|
||||||
|
//
|
||||||
|
// EvtLocalAddressesUpdated contains a snapshot of the current listen addresses,
|
||||||
|
// and may also contain a diff between the current state and the previous state.
|
||||||
|
// If the event producer is capable of creating a diff, the Diffs field will be
|
||||||
|
// true, and event consumers can inspect the Action field of each UpdatedAddress
|
||||||
|
// to see how each address was modified.
|
||||||
|
//
|
||||||
|
// For example, the Action will tell you whether an address in
|
||||||
|
// the Current list was Added by the event producer, or was Maintained without
|
||||||
|
// changes. Addresses that were removed from the Host will have the AddrAction
|
||||||
|
// of Removed, and will be in the Removed list.
|
||||||
|
//
|
||||||
|
// If the event producer is not capable or producing diffs, the Diffs field will
|
||||||
|
// be false, the Removed list will always be empty, and the Action for each
|
||||||
|
// UpdatedAddress in the Current list will be Unknown.
|
||||||
|
//
|
||||||
|
// In addition to the above, EvtLocalAddressesUpdated also contains the updated peer.PeerRecord
|
||||||
|
// for the Current set of listen addresses, wrapped in a record.Envelope and signed by the Host's private key.
|
||||||
|
// This record can be shared with other peers to inform them of what we believe are our diallable addresses
|
||||||
|
// a secure and authenticated way.
|
||||||
|
type EvtLocalAddressesUpdated struct {
|
||||||
|
|
||||||
|
// Diffs indicates whether this event contains a diff of the Host's previous
|
||||||
|
// address set.
|
||||||
|
Diffs bool
|
||||||
|
|
||||||
|
// Current contains all current listen addresses for the Host.
|
||||||
|
// If Diffs == true, the Action field of each UpdatedAddress will tell
|
||||||
|
// you whether an address was Added, or was Maintained from the previous
|
||||||
|
// state.
|
||||||
|
Current []UpdatedAddress
|
||||||
|
|
||||||
|
// Removed contains addresses that were removed from the Host.
|
||||||
|
// This field is only set when Diffs == true.
|
||||||
|
Removed []UpdatedAddress
|
||||||
|
|
||||||
|
// SignedPeerRecord contains our own updated peer.PeerRecord, listing the addresses enumerated in Current.
|
||||||
|
// wrapped in a record.Envelope and signed by the Host's private key.
|
||||||
|
SignedPeerRecord *record.Envelope
|
||||||
|
}
|
100
go-libp2p/core/event/bus.go
Normal file
100
go-libp2p/core/event/bus.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SubscriptionOpt represents a subscriber option. Use the options exposed by the implementation of choice.
|
||||||
|
type SubscriptionOpt = func(interface{}) error
|
||||||
|
|
||||||
|
// EmitterOpt represents an emitter option. Use the options exposed by the implementation of choice.
|
||||||
|
type EmitterOpt = func(interface{}) error
|
||||||
|
|
||||||
|
// CancelFunc closes a subscriber.
|
||||||
|
type CancelFunc = func()
|
||||||
|
|
||||||
|
// wildcardSubscriptionType is a virtual type to represent wildcard
|
||||||
|
// subscriptions.
|
||||||
|
type wildcardSubscriptionType interface{}
|
||||||
|
|
||||||
|
// WildcardSubscription is the type to subscribe to receive all events
|
||||||
|
// emitted in the eventbus.
|
||||||
|
var WildcardSubscription = new(wildcardSubscriptionType)
|
||||||
|
|
||||||
|
// Emitter represents an actor that emits events onto the eventbus.
|
||||||
|
type Emitter interface {
|
||||||
|
io.Closer
|
||||||
|
|
||||||
|
// Emit emits an event onto the eventbus. If any channel subscribed to the topic is blocked,
|
||||||
|
// calls to Emit will block.
|
||||||
|
//
|
||||||
|
// Calling this function with wrong event type will cause a panic.
|
||||||
|
Emit(evt interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subscription represents a subscription to one or multiple event types.
|
||||||
|
type Subscription interface {
|
||||||
|
io.Closer
|
||||||
|
|
||||||
|
// Out returns the channel from which to consume events.
|
||||||
|
Out() <-chan interface{}
|
||||||
|
|
||||||
|
// Name returns the name for the subscription
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bus is an interface for a type-based event delivery system.
|
||||||
|
type Bus interface {
|
||||||
|
// Subscribe creates a new Subscription.
|
||||||
|
//
|
||||||
|
// eventType can be either a pointer to a single event type, or a slice of pointers to
|
||||||
|
// subscribe to multiple event types at once, under a single subscription (and channel).
|
||||||
|
//
|
||||||
|
// Failing to drain the channel may cause publishers to block.
|
||||||
|
//
|
||||||
|
// If you want to subscribe to ALL events emitted in the bus, use
|
||||||
|
// `WildcardSubscription` as the `eventType`:
|
||||||
|
//
|
||||||
|
// eventbus.Subscribe(WildcardSubscription)
|
||||||
|
//
|
||||||
|
// Simple example
|
||||||
|
//
|
||||||
|
// sub, err := eventbus.Subscribe(new(EventType))
|
||||||
|
// defer sub.Close()
|
||||||
|
// for e := range sub.Out() {
|
||||||
|
// event := e.(EventType) // guaranteed safe
|
||||||
|
// [...]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Multi-type example
|
||||||
|
//
|
||||||
|
// sub, err := eventbus.Subscribe([]interface{}{new(EventA), new(EventB)})
|
||||||
|
// defer sub.Close()
|
||||||
|
// for e := range sub.Out() {
|
||||||
|
// select e.(type):
|
||||||
|
// case EventA:
|
||||||
|
// [...]
|
||||||
|
// case EventB:
|
||||||
|
// [...]
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
Subscribe(eventType interface{}, opts ...SubscriptionOpt) (Subscription, error)
|
||||||
|
|
||||||
|
// Emitter creates a new event emitter.
|
||||||
|
//
|
||||||
|
// eventType accepts typed nil pointers, and uses the type information for wiring purposes.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// em, err := eventbus.Emitter(new(EventT))
|
||||||
|
// defer em.Close() // MUST call this after being done with the emitter
|
||||||
|
// em.Emit(EventT{})
|
||||||
|
Emitter(eventType interface{}, opts ...EmitterOpt) (Emitter, error)
|
||||||
|
|
||||||
|
// GetAllEventTypes returns all the event types that this bus knows about
|
||||||
|
// (having emitters and subscribers). It omits the WildcardSubscription.
|
||||||
|
//
|
||||||
|
// The caller is guaranteed that this function will only return value types;
|
||||||
|
// no pointer types will be returned.
|
||||||
|
GetAllEventTypes() []reflect.Type
|
||||||
|
}
|
21
go-libp2p/core/event/dht.go
Normal file
21
go-libp2p/core/event/dht.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
// RawJSON is a type that contains a raw JSON string.
|
||||||
|
type RawJSON string
|
||||||
|
|
||||||
|
// GenericDHTEvent is a type that encapsulates an actual DHT event by carrying
|
||||||
|
// its raw JSON.
|
||||||
|
//
|
||||||
|
// Context: the DHT event system is rather bespoke and a bit messy at the time,
|
||||||
|
// so until we unify/clean that up, this event bridges the gap. It should only
|
||||||
|
// be consumed for informational purposes.
|
||||||
|
//
|
||||||
|
// EXPERIMENTAL: this will likely be removed if/when the DHT event types are
|
||||||
|
// hoisted to core, and the DHT event system is reconciled with the eventbus.
|
||||||
|
type GenericDHTEvent struct {
|
||||||
|
// Type is the type of the DHT event that occurred.
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// Raw is the raw JSON representation of the event payload.
|
||||||
|
Raw RawJSON
|
||||||
|
}
|
11
go-libp2p/core/event/doc.go
Normal file
11
go-libp2p/core/event/doc.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Package event contains the abstractions for a local event bus, along with the standard events
|
||||||
|
// that libp2p subsystems may emit.
|
||||||
|
//
|
||||||
|
// Source code is arranged as follows:
|
||||||
|
// - doc.go: this file.
|
||||||
|
// - bus.go: abstractions for the event bus.
|
||||||
|
// - rest: event structs, sensibly categorised in files by entity, and following this naming convention:
|
||||||
|
// Evt[Entity (noun)][Event (verb past tense / gerund)]
|
||||||
|
// The past tense is used to convey that something happened, whereas the gerund form of the verb (-ing)
|
||||||
|
// expresses that a process is in progress. Examples: EvtConnEstablishing, EvtConnEstablished.
|
||||||
|
package event
|
17
go-libp2p/core/event/identify.go
Normal file
17
go-libp2p/core/event/identify.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import "github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
|
||||||
|
// EvtPeerIdentificationCompleted is emitted when the initial identification round for a peer is completed.
|
||||||
|
type EvtPeerIdentificationCompleted struct {
|
||||||
|
// Peer is the ID of the peer whose identification succeeded.
|
||||||
|
Peer peer.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvtPeerIdentificationFailed is emitted when the initial identification round for a peer failed.
|
||||||
|
type EvtPeerIdentificationFailed struct {
|
||||||
|
// Peer is the ID of the peer whose identification failed.
|
||||||
|
Peer peer.ID
|
||||||
|
// Reason is the reason why identification failed.
|
||||||
|
Reason error
|
||||||
|
}
|
18
go-libp2p/core/event/nattype.go
Normal file
18
go-libp2p/core/event/nattype.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import "github.com/libp2p/go-libp2p/core/network"
|
||||||
|
|
||||||
|
// EvtNATDeviceTypeChanged is an event struct to be emitted when the type of the NAT device changes for a Transport Protocol.
|
||||||
|
//
|
||||||
|
// Note: This event is meaningful ONLY if the AutoNAT Reachability is Private.
|
||||||
|
// Consumers of this event should ALSO consume the `EvtLocalReachabilityChanged` event and interpret
|
||||||
|
// this event ONLY if the Reachability on the `EvtLocalReachabilityChanged` is Private.
|
||||||
|
type EvtNATDeviceTypeChanged struct {
|
||||||
|
// TransportProtocol is the Transport Protocol for which the NAT Device Type has been determined.
|
||||||
|
TransportProtocol network.NATTransportProtocol
|
||||||
|
// NatDeviceType indicates the type of the NAT Device for the Transport Protocol.
|
||||||
|
// Currently, it can be either a `Cone NAT` or a `Symmetric NAT`. Please see the detailed documentation
|
||||||
|
// on `network.NATDeviceType` enumerations for a better understanding of what these types mean and
|
||||||
|
// how they impact Connectivity and Hole Punching.
|
||||||
|
NatDeviceType network.NATDeviceType
|
||||||
|
}
|
55
go-libp2p/core/event/network.go
Normal file
55
go-libp2p/core/event/network.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EvtPeerConnectednessChanged should be emitted every time the "connectedness" to a
|
||||||
|
// given peer changes. Specifically, this event is emitted in the following
|
||||||
|
// cases:
|
||||||
|
//
|
||||||
|
// - Connectedness = Connected: Every time we transition from having no
|
||||||
|
// connections to a peer to having at least one connection to the peer.
|
||||||
|
// - Connectedness = NotConnected: Every time we transition from having at least
|
||||||
|
// one connection to a peer to having no connections to the peer.
|
||||||
|
//
|
||||||
|
// Additional connectedness states may be added in the future. This list should
|
||||||
|
// not be considered exhaustive.
|
||||||
|
//
|
||||||
|
// Take note:
|
||||||
|
//
|
||||||
|
// - It's possible to have _multiple_ connections to a given peer.
|
||||||
|
// - Both libp2p and networks are asynchronous.
|
||||||
|
//
|
||||||
|
// This means that all the following situations are possible:
|
||||||
|
//
|
||||||
|
// A connection is cut and is re-established:
|
||||||
|
//
|
||||||
|
// - Peer A observes a transition from Connected -> NotConnected -> Connected
|
||||||
|
// - Peer B observes a transition from Connected -> NotConnected -> Connected
|
||||||
|
//
|
||||||
|
// Explanation: Both peers observe the connection die. This is the "nice" case.
|
||||||
|
//
|
||||||
|
// A connection is cut and is re-established.
|
||||||
|
//
|
||||||
|
// - Peer A observes a transition from Connected -> NotConnected -> Connected.
|
||||||
|
// - Peer B observes no transition.
|
||||||
|
//
|
||||||
|
// Explanation: Peer A re-establishes the dead connection. Peer B observes the
|
||||||
|
// new connection form before it observes the old connection die.
|
||||||
|
//
|
||||||
|
// A connection is cut:
|
||||||
|
//
|
||||||
|
// - Peer A observes no transition.
|
||||||
|
// - Peer B observes no transition.
|
||||||
|
//
|
||||||
|
// Explanation: There were two connections and one was cut. This connection
|
||||||
|
// might have been in active use but neither peer will observe a change in
|
||||||
|
// "connectedness". Peers should always make sure to retry network requests.
|
||||||
|
type EvtPeerConnectednessChanged struct {
|
||||||
|
// Peer is the remote peer whose connectedness has changed.
|
||||||
|
Peer peer.ID
|
||||||
|
// Connectedness is the new connectedness state.
|
||||||
|
Connectedness network.Connectedness
|
||||||
|
}
|
26
go-libp2p/core/event/protocol.go
Normal file
26
go-libp2p/core/event/protocol.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import (
|
||||||
|
peer "github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
protocol "github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EvtPeerProtocolsUpdated should be emitted when a peer we're connected to adds or removes protocols from their stack.
|
||||||
|
type EvtPeerProtocolsUpdated struct {
|
||||||
|
// Peer is the peer whose protocols were updated.
|
||||||
|
Peer peer.ID
|
||||||
|
// Added enumerates the protocols that were added by this peer.
|
||||||
|
Added []protocol.ID
|
||||||
|
// Removed enumerates the protocols that were removed by this peer.
|
||||||
|
Removed []protocol.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// EvtLocalProtocolsUpdated should be emitted when stream handlers are attached or detached from the local host.
|
||||||
|
// For handlers attached with a matcher predicate (host.SetStreamHandlerMatch()), only the protocol ID will be
|
||||||
|
// included in this event.
|
||||||
|
type EvtLocalProtocolsUpdated struct {
|
||||||
|
// Added enumerates the protocols that were added locally.
|
||||||
|
Added []protocol.ID
|
||||||
|
// Removed enumerates the protocols that were removed locally.
|
||||||
|
Removed []protocol.ID
|
||||||
|
}
|
13
go-libp2p/core/event/reachability.go
Normal file
13
go-libp2p/core/event/reachability.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EvtLocalReachabilityChanged is an event struct to be emitted when the local's
|
||||||
|
// node reachability changes state.
|
||||||
|
//
|
||||||
|
// This event is usually emitted by the AutoNAT subsystem.
|
||||||
|
type EvtLocalReachabilityChanged struct {
|
||||||
|
Reachability network.Reachability
|
||||||
|
}
|
11
go-libp2p/core/host/helpers.go
Normal file
11
go-libp2p/core/host/helpers.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package host
|
||||||
|
|
||||||
|
import "github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
|
||||||
|
// InfoFromHost returns a peer.AddrInfo struct with the Host's ID and all of its Addrs.
|
||||||
|
func InfoFromHost(h Host) *peer.AddrInfo {
|
||||||
|
return &peer.AddrInfo{
|
||||||
|
ID: h.ID(),
|
||||||
|
Addrs: h.Addrs(),
|
||||||
|
}
|
||||||
|
}
|
75
go-libp2p/core/host/host.go
Normal file
75
go-libp2p/core/host/host.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// Package host provides the core Host interface for libp2p.
|
||||||
|
//
|
||||||
|
// Host represents a single libp2p node in a peer-to-peer network.
|
||||||
|
package host
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/connmgr"
|
||||||
|
"github.com/libp2p/go-libp2p/core/event"
|
||||||
|
"github.com/libp2p/go-libp2p/core/network"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peerstore"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
|
||||||
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Host is an object participating in a p2p network, which
|
||||||
|
// implements protocols or provides services. It handles
|
||||||
|
// requests like a Server, and issues requests like a Client.
|
||||||
|
// It is called Host because it is both Server and Client (and Peer
|
||||||
|
// may be confusing).
|
||||||
|
type Host interface {
|
||||||
|
// ID returns the (local) peer.ID associated with this Host
|
||||||
|
ID() peer.ID
|
||||||
|
|
||||||
|
// Peerstore returns the Host's repository of Peer Addresses and Keys.
|
||||||
|
Peerstore() peerstore.Peerstore
|
||||||
|
|
||||||
|
// Returns the listen addresses of the Host
|
||||||
|
Addrs() []ma.Multiaddr
|
||||||
|
|
||||||
|
// Networks returns the Network interface of the Host
|
||||||
|
Network() network.Network
|
||||||
|
|
||||||
|
// Mux returns the Mux multiplexing incoming streams to protocol handlers
|
||||||
|
Mux() protocol.Switch
|
||||||
|
|
||||||
|
// Connect ensures there is a connection between this host and the peer with
|
||||||
|
// given peer.ID. Connect will absorb the addresses in pi into its internal
|
||||||
|
// peerstore. If there is not an active connection, Connect will issue a
|
||||||
|
// h.Network.Dial, and block until a connection is open, or an error is
|
||||||
|
// returned. // TODO: Relay + NAT.
|
||||||
|
Connect(ctx context.Context, pi peer.AddrInfo) error
|
||||||
|
|
||||||
|
// SetStreamHandler sets the protocol handler on the Host's Mux.
|
||||||
|
// This is equivalent to:
|
||||||
|
// host.Mux().SetHandler(proto, handler)
|
||||||
|
// (Thread-safe)
|
||||||
|
SetStreamHandler(pid protocol.ID, handler network.StreamHandler)
|
||||||
|
|
||||||
|
// SetStreamHandlerMatch sets the protocol handler on the Host's Mux
|
||||||
|
// using a matching function for protocol selection.
|
||||||
|
SetStreamHandlerMatch(protocol.ID, func(protocol.ID) bool, network.StreamHandler)
|
||||||
|
|
||||||
|
// RemoveStreamHandler removes a handler on the mux that was set by
|
||||||
|
// SetStreamHandler
|
||||||
|
RemoveStreamHandler(pid protocol.ID)
|
||||||
|
|
||||||
|
// NewStream opens a new stream to given peer p, and writes a p2p/protocol
|
||||||
|
// header with given ProtocolID. If there is no connection to p, attempts
|
||||||
|
// to create one. If ProtocolID is "", writes no header.
|
||||||
|
// (Thread-safe)
|
||||||
|
NewStream(ctx context.Context, p peer.ID, pids ...protocol.ID) (network.Stream, error)
|
||||||
|
|
||||||
|
// Close shuts down the host, its Network, and services.
|
||||||
|
Close() error
|
||||||
|
|
||||||
|
// ConnManager returns this hosts connection manager
|
||||||
|
ConnManager() connmgr.ConnManager
|
||||||
|
|
||||||
|
// EventBus returns the hosts eventbus
|
||||||
|
EventBus() event.Bus
|
||||||
|
}
|
18
go-libp2p/core/internal/catch/catch.go
Normal file
18
go-libp2p/core/internal/catch/catch.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package catch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"runtime/debug"
|
||||||
|
)
|
||||||
|
|
||||||
|
var panicWriter io.Writer = os.Stderr
|
||||||
|
|
||||||
|
// HandlePanic handles and logs panics.
|
||||||
|
func HandlePanic(rerr interface{}, err *error, where string) {
|
||||||
|
if rerr != nil {
|
||||||
|
fmt.Fprintf(panicWriter, "caught panic: %s\n%s\n", rerr, debug.Stack())
|
||||||
|
*err = fmt.Errorf("panic in %s: %s", where, rerr)
|
||||||
|
}
|
||||||
|
}
|
28
go-libp2p/core/internal/catch/catch_test.go
Normal file
28
go-libp2p/core/internal/catch/catch_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package catch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCatch(t *testing.T) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
oldPanicWriter := panicWriter
|
||||||
|
t.Cleanup(func() { panicWriter = oldPanicWriter })
|
||||||
|
panicWriter = buf
|
||||||
|
|
||||||
|
panicAndCatch := func() (err error) {
|
||||||
|
defer func() { HandlePanic(recover(), &err, "somewhere") }()
|
||||||
|
|
||||||
|
panic("here")
|
||||||
|
}
|
||||||
|
|
||||||
|
err := panicAndCatch()
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), "panic in somewhere: here")
|
||||||
|
|
||||||
|
require.Contains(t, buf.String(), "caught panic: here")
|
||||||
|
}
|
176
go-libp2p/core/metrics/bandwidth.go
Normal file
176
go-libp2p/core/metrics/bandwidth.go
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
// Package metrics provides metrics collection and reporting interfaces for libp2p.
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-flow-metrics"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BandwidthCounter tracks incoming and outgoing data transferred by the local peer.
|
||||||
|
// Metrics are available for total bandwidth across all peers / protocols, as well
|
||||||
|
// as segmented by remote peer ID and protocol ID.
|
||||||
|
type BandwidthCounter struct {
|
||||||
|
totalIn flow.Meter
|
||||||
|
totalOut flow.Meter
|
||||||
|
|
||||||
|
protocolIn flow.MeterRegistry
|
||||||
|
protocolOut flow.MeterRegistry
|
||||||
|
|
||||||
|
peerIn flow.MeterRegistry
|
||||||
|
peerOut flow.MeterRegistry
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBandwidthCounter creates a new BandwidthCounter.
|
||||||
|
func NewBandwidthCounter() *BandwidthCounter {
|
||||||
|
return new(BandwidthCounter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogSentMessage records the size of an outgoing message
|
||||||
|
// without associating the bandwidth to a specific peer or protocol.
|
||||||
|
func (bwc *BandwidthCounter) LogSentMessage(size int64) {
|
||||||
|
bwc.totalOut.Mark(uint64(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogRecvMessage records the size of an incoming message
|
||||||
|
// without associating the bandwidth to a specific peer or protocol.
|
||||||
|
func (bwc *BandwidthCounter) LogRecvMessage(size int64) {
|
||||||
|
bwc.totalIn.Mark(uint64(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogSentMessageStream records the size of an outgoing message over a single logical stream.
|
||||||
|
// Bandwidth is associated with the given protocol.ID and peer.ID.
|
||||||
|
func (bwc *BandwidthCounter) LogSentMessageStream(size int64, proto protocol.ID, p peer.ID) {
|
||||||
|
bwc.protocolOut.Get(string(proto)).Mark(uint64(size))
|
||||||
|
bwc.peerOut.Get(string(p)).Mark(uint64(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogRecvMessageStream records the size of an incoming message over a single logical stream.
|
||||||
|
// Bandwidth is associated with the given protocol.ID and peer.ID.
|
||||||
|
func (bwc *BandwidthCounter) LogRecvMessageStream(size int64, proto protocol.ID, p peer.ID) {
|
||||||
|
bwc.protocolIn.Get(string(proto)).Mark(uint64(size))
|
||||||
|
bwc.peerIn.Get(string(p)).Mark(uint64(size))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBandwidthForPeer returns a Stats struct with bandwidth metrics associated with the given peer.ID.
|
||||||
|
// The metrics returned include all traffic sent / received for the peer, regardless of protocol.
|
||||||
|
func (bwc *BandwidthCounter) GetBandwidthForPeer(p peer.ID) (out Stats) {
|
||||||
|
inSnap := bwc.peerIn.Get(string(p)).Snapshot()
|
||||||
|
outSnap := bwc.peerOut.Get(string(p)).Snapshot()
|
||||||
|
|
||||||
|
return Stats{
|
||||||
|
TotalIn: int64(inSnap.Total),
|
||||||
|
TotalOut: int64(outSnap.Total),
|
||||||
|
RateIn: inSnap.Rate,
|
||||||
|
RateOut: outSnap.Rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBandwidthForProtocol returns a Stats struct with bandwidth metrics associated with the given protocol.ID.
|
||||||
|
// The metrics returned include all traffic sent / received for the protocol, regardless of which peers were
|
||||||
|
// involved.
|
||||||
|
func (bwc *BandwidthCounter) GetBandwidthForProtocol(proto protocol.ID) (out Stats) {
|
||||||
|
inSnap := bwc.protocolIn.Get(string(proto)).Snapshot()
|
||||||
|
outSnap := bwc.protocolOut.Get(string(proto)).Snapshot()
|
||||||
|
|
||||||
|
return Stats{
|
||||||
|
TotalIn: int64(inSnap.Total),
|
||||||
|
TotalOut: int64(outSnap.Total),
|
||||||
|
RateIn: inSnap.Rate,
|
||||||
|
RateOut: outSnap.Rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBandwidthTotals returns a Stats struct with bandwidth metrics for all data sent / received by the
|
||||||
|
// local peer, regardless of protocol or remote peer IDs.
|
||||||
|
func (bwc *BandwidthCounter) GetBandwidthTotals() (out Stats) {
|
||||||
|
inSnap := bwc.totalIn.Snapshot()
|
||||||
|
outSnap := bwc.totalOut.Snapshot()
|
||||||
|
|
||||||
|
return Stats{
|
||||||
|
TotalIn: int64(inSnap.Total),
|
||||||
|
TotalOut: int64(outSnap.Total),
|
||||||
|
RateIn: inSnap.Rate,
|
||||||
|
RateOut: outSnap.Rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBandwidthByPeer returns a map of all remembered peers and the bandwidth
|
||||||
|
// metrics with respect to each. This method may be very expensive.
|
||||||
|
func (bwc *BandwidthCounter) GetBandwidthByPeer() map[peer.ID]Stats {
|
||||||
|
peers := make(map[peer.ID]Stats)
|
||||||
|
|
||||||
|
bwc.peerIn.ForEach(func(p string, meter *flow.Meter) {
|
||||||
|
id := peer.ID(p)
|
||||||
|
snap := meter.Snapshot()
|
||||||
|
|
||||||
|
stat := peers[id]
|
||||||
|
stat.TotalIn = int64(snap.Total)
|
||||||
|
stat.RateIn = snap.Rate
|
||||||
|
peers[id] = stat
|
||||||
|
})
|
||||||
|
|
||||||
|
bwc.peerOut.ForEach(func(p string, meter *flow.Meter) {
|
||||||
|
id := peer.ID(p)
|
||||||
|
snap := meter.Snapshot()
|
||||||
|
|
||||||
|
stat := peers[id]
|
||||||
|
stat.TotalOut = int64(snap.Total)
|
||||||
|
stat.RateOut = snap.Rate
|
||||||
|
peers[id] = stat
|
||||||
|
})
|
||||||
|
|
||||||
|
return peers
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBandwidthByProtocol returns a map of all remembered protocols and
|
||||||
|
// the bandwidth metrics with respect to each. This method may be moderately
|
||||||
|
// expensive.
|
||||||
|
func (bwc *BandwidthCounter) GetBandwidthByProtocol() map[protocol.ID]Stats {
|
||||||
|
protocols := make(map[protocol.ID]Stats)
|
||||||
|
|
||||||
|
bwc.protocolIn.ForEach(func(p string, meter *flow.Meter) {
|
||||||
|
id := protocol.ID(p)
|
||||||
|
snap := meter.Snapshot()
|
||||||
|
|
||||||
|
stat := protocols[id]
|
||||||
|
stat.TotalIn = int64(snap.Total)
|
||||||
|
stat.RateIn = snap.Rate
|
||||||
|
protocols[id] = stat
|
||||||
|
})
|
||||||
|
|
||||||
|
bwc.protocolOut.ForEach(func(p string, meter *flow.Meter) {
|
||||||
|
id := protocol.ID(p)
|
||||||
|
snap := meter.Snapshot()
|
||||||
|
|
||||||
|
stat := protocols[id]
|
||||||
|
stat.TotalOut = int64(snap.Total)
|
||||||
|
stat.RateOut = snap.Rate
|
||||||
|
protocols[id] = stat
|
||||||
|
})
|
||||||
|
|
||||||
|
return protocols
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset clears all stats.
|
||||||
|
func (bwc *BandwidthCounter) Reset() {
|
||||||
|
bwc.totalIn.Reset()
|
||||||
|
bwc.totalOut.Reset()
|
||||||
|
|
||||||
|
bwc.protocolIn.Clear()
|
||||||
|
bwc.protocolOut.Clear()
|
||||||
|
|
||||||
|
bwc.peerIn.Clear()
|
||||||
|
bwc.peerOut.Clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimIdle trims all timers idle since the given time.
|
||||||
|
func (bwc *BandwidthCounter) TrimIdle(since time.Time) {
|
||||||
|
bwc.peerIn.TrimIdle(since)
|
||||||
|
bwc.peerOut.TrimIdle(since)
|
||||||
|
bwc.protocolIn.TrimIdle(since)
|
||||||
|
bwc.protocolOut.TrimIdle(since)
|
||||||
|
}
|
170
go-libp2p/core/metrics/bandwidth_test.go
Normal file
170
go-libp2p/core/metrics/bandwidth_test.go
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-flow-metrics"
|
||||||
|
|
||||||
|
"github.com/benbjohnson/clock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cl = clock.NewMock()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
flow.SetClock(cl)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkBandwidthCounter(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
bwc := NewBandwidthCounter()
|
||||||
|
round(bwc, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func round(bwc *BandwidthCounter, b *testing.B) {
|
||||||
|
start := make(chan struct{})
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(10000)
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
p := peer.ID(fmt.Sprintf("peer-%d", i))
|
||||||
|
for j := 0; j < 10; j++ {
|
||||||
|
proto := protocol.ID(fmt.Sprintf("bitswap-%d", j))
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
<-start
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
bwc.LogSentMessage(100)
|
||||||
|
bwc.LogSentMessageStream(100, proto, p)
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.StartTimer()
|
||||||
|
close(start)
|
||||||
|
wg.Wait()
|
||||||
|
b.StopTimer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBandwidthCounter(t *testing.T) {
|
||||||
|
bwc := NewBandwidthCounter()
|
||||||
|
for i := 0; i < 40; i++ {
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
p := peer.ID(fmt.Sprintf("peer-%d", i))
|
||||||
|
for j := 0; j < 2; j++ {
|
||||||
|
proto := protocol.ID(fmt.Sprintf("proto-%d", j))
|
||||||
|
|
||||||
|
// make sure the bandwidth counters are active
|
||||||
|
bwc.LogSentMessage(100)
|
||||||
|
bwc.LogRecvMessage(50)
|
||||||
|
bwc.LogSentMessageStream(100, proto, p)
|
||||||
|
bwc.LogRecvMessageStream(50, proto, p)
|
||||||
|
|
||||||
|
// <-start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cl.Add(100 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertProtocols := func(check func(Stats)) {
|
||||||
|
byProtocol := bwc.GetBandwidthByProtocol()
|
||||||
|
require.Len(t, byProtocol, 2, "expected 2 protocols")
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
p := protocol.ID(fmt.Sprintf("proto-%d", i))
|
||||||
|
for _, stats := range [...]Stats{bwc.GetBandwidthForProtocol(p), byProtocol[p]} {
|
||||||
|
check(stats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertPeers := func(check func(Stats)) {
|
||||||
|
byPeer := bwc.GetBandwidthByPeer()
|
||||||
|
require.Len(t, byPeer, 100, "expected 100 peers")
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
p := peer.ID(fmt.Sprintf("peer-%d", i))
|
||||||
|
for _, stats := range [...]Stats{bwc.GetBandwidthForPeer(p), byPeer[p]} {
|
||||||
|
check(stats)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertPeers(func(stats Stats) {
|
||||||
|
require.Equal(t, int64(8000), stats.TotalOut)
|
||||||
|
require.Equal(t, int64(4000), stats.TotalIn)
|
||||||
|
})
|
||||||
|
|
||||||
|
assertProtocols(func(stats Stats) {
|
||||||
|
require.Equal(t, int64(400000), stats.TotalOut)
|
||||||
|
require.Equal(t, int64(200000), stats.TotalIn)
|
||||||
|
})
|
||||||
|
|
||||||
|
stats := bwc.GetBandwidthTotals()
|
||||||
|
require.Equal(t, int64(800000), stats.TotalOut)
|
||||||
|
require.Equal(t, int64(400000), stats.TotalIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResetBandwidthCounter(t *testing.T) {
|
||||||
|
bwc := NewBandwidthCounter()
|
||||||
|
|
||||||
|
p := peer.ID("peer-0")
|
||||||
|
proto := protocol.ID("proto-0")
|
||||||
|
|
||||||
|
// We don't calculate bandwidth till we've been active for a second.
|
||||||
|
bwc.LogSentMessage(42)
|
||||||
|
bwc.LogRecvMessage(24)
|
||||||
|
bwc.LogSentMessageStream(100, proto, p)
|
||||||
|
bwc.LogRecvMessageStream(50, proto, p)
|
||||||
|
|
||||||
|
time.Sleep(200 * time.Millisecond) // make sure the meters are registered with the sweeper
|
||||||
|
cl.Add(time.Second)
|
||||||
|
|
||||||
|
bwc.LogSentMessage(42)
|
||||||
|
bwc.LogRecvMessage(24)
|
||||||
|
bwc.LogSentMessageStream(100, proto, p)
|
||||||
|
bwc.LogRecvMessageStream(50, proto, p)
|
||||||
|
|
||||||
|
cl.Add(time.Second)
|
||||||
|
|
||||||
|
{
|
||||||
|
stats := bwc.GetBandwidthTotals()
|
||||||
|
require.Equal(t, int64(84), stats.TotalOut)
|
||||||
|
require.Equal(t, int64(48), stats.TotalIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stats := bwc.GetBandwidthByProtocol()
|
||||||
|
require.Len(t, stats, 1)
|
||||||
|
stat := stats[proto]
|
||||||
|
require.Equal(t, float64(100), stat.RateOut)
|
||||||
|
require.Equal(t, float64(50), stat.RateIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
stats := bwc.GetBandwidthByPeer()
|
||||||
|
require.Len(t, stats, 1)
|
||||||
|
stat := stats[p]
|
||||||
|
require.Equal(t, float64(100), stat.RateOut)
|
||||||
|
require.Equal(t, float64(50), stat.RateIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
bwc.Reset()
|
||||||
|
{
|
||||||
|
stats := bwc.GetBandwidthTotals()
|
||||||
|
require.Zero(t, stats.TotalOut)
|
||||||
|
require.Zero(t, stats.TotalIn)
|
||||||
|
require.Empty(t, bwc.GetBandwidthByProtocol(), "expected 0 protocols")
|
||||||
|
require.Empty(t, bwc.GetBandwidthByPeer(), "expected 0 peers")
|
||||||
|
}
|
||||||
|
}
|
31
go-libp2p/core/metrics/reporter.go
Normal file
31
go-libp2p/core/metrics/reporter.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Package metrics provides metrics collection and reporting interfaces for libp2p.
|
||||||
|
package metrics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stats represents a point-in-time snapshot of bandwidth metrics.
|
||||||
|
//
|
||||||
|
// The TotalIn and TotalOut fields record cumulative bytes sent / received.
|
||||||
|
// The RateIn and RateOut fields record bytes sent / received per second.
|
||||||
|
type Stats struct {
|
||||||
|
TotalIn int64
|
||||||
|
TotalOut int64
|
||||||
|
RateIn float64
|
||||||
|
RateOut float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reporter provides methods for logging and retrieving metrics.
|
||||||
|
type Reporter interface {
|
||||||
|
LogSentMessage(int64)
|
||||||
|
LogRecvMessage(int64)
|
||||||
|
LogSentMessageStream(int64, protocol.ID, peer.ID)
|
||||||
|
LogRecvMessageStream(int64, protocol.ID, peer.ID)
|
||||||
|
GetBandwidthForPeer(peer.ID) Stats
|
||||||
|
GetBandwidthForProtocol(protocol.ID) Stats
|
||||||
|
GetBandwidthTotals() Stats
|
||||||
|
GetBandwidthByPeer() map[peer.ID]Stats
|
||||||
|
GetBandwidthByProtocol() map[protocol.ID]Stats
|
||||||
|
}
|
93
go-libp2p/core/network/conn.go
Normal file
93
go-libp2p/core/network/conn.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
ic "github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
"github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
|
||||||
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Conn is a connection to a remote peer. It multiplexes streams.
|
||||||
|
// Usually there is no need to use a Conn directly, but it may
|
||||||
|
// be useful to get information about the peer on the other side:
|
||||||
|
//
|
||||||
|
// stream.Conn().RemotePeer()
|
||||||
|
type Conn interface {
|
||||||
|
io.Closer
|
||||||
|
|
||||||
|
ConnSecurity
|
||||||
|
ConnMultiaddrs
|
||||||
|
ConnStat
|
||||||
|
ConnScoper
|
||||||
|
|
||||||
|
// ID returns an identifier that uniquely identifies this Conn within this
|
||||||
|
// host, during this run. Connection IDs may repeat across restarts.
|
||||||
|
ID() string
|
||||||
|
|
||||||
|
// NewStream constructs a new Stream over this conn.
|
||||||
|
NewStream(context.Context) (Stream, error)
|
||||||
|
|
||||||
|
// GetStreams returns all open streams over this conn.
|
||||||
|
GetStreams() []Stream
|
||||||
|
|
||||||
|
// IsClosed returns whether a connection is fully closed, so it can
|
||||||
|
// be garbage collected.
|
||||||
|
IsClosed() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectionState holds information about the connection.
|
||||||
|
type ConnectionState struct {
|
||||||
|
// The stream multiplexer used on this connection (if any). For example: /yamux/1.0.0
|
||||||
|
StreamMultiplexer protocol.ID
|
||||||
|
// The security protocol used on this connection (if any). For example: /tls/1.0.0
|
||||||
|
Security protocol.ID
|
||||||
|
// the transport used on this connection. For example: tcp
|
||||||
|
Transport string
|
||||||
|
// indicates whether StreamMultiplexer was selected using inlined muxer negotiation
|
||||||
|
UsedEarlyMuxerNegotiation bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnSecurity is the interface that one can mix into a connection interface to
|
||||||
|
// give it the security methods.
|
||||||
|
type ConnSecurity interface {
|
||||||
|
// LocalPeer returns our peer ID
|
||||||
|
LocalPeer() peer.ID
|
||||||
|
|
||||||
|
// RemotePeer returns the peer ID of the remote peer.
|
||||||
|
RemotePeer() peer.ID
|
||||||
|
|
||||||
|
// RemotePublicKey returns the public key of the remote peer.
|
||||||
|
RemotePublicKey() ic.PubKey
|
||||||
|
|
||||||
|
// ConnState returns information about the connection state.
|
||||||
|
ConnState() ConnectionState
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnMultiaddrs is an interface mixin for connection types that provide multiaddr
|
||||||
|
// addresses for the endpoints.
|
||||||
|
type ConnMultiaddrs interface {
|
||||||
|
// LocalMultiaddr returns the local Multiaddr associated
|
||||||
|
// with this connection
|
||||||
|
LocalMultiaddr() ma.Multiaddr
|
||||||
|
|
||||||
|
// RemoteMultiaddr returns the remote Multiaddr associated
|
||||||
|
// with this connection
|
||||||
|
RemoteMultiaddr() ma.Multiaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnStat is an interface mixin for connection types that provide connection statistics.
|
||||||
|
type ConnStat interface {
|
||||||
|
// Stat stores metadata pertaining to this conn.
|
||||||
|
Stat() ConnStats
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnScoper is the interface that one can mix into a connection interface to give it a resource
|
||||||
|
// management scope
|
||||||
|
type ConnScoper interface {
|
||||||
|
// Scope returns the user view of this connection's resource scope
|
||||||
|
Scope() ConnScope
|
||||||
|
}
|
110
go-libp2p/core/network/context.go
Normal file
110
go-libp2p/core/network/context.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DialPeerTimeout is the default timeout for a single call to `DialPeer`. When
|
||||||
|
// there are multiple concurrent calls to `DialPeer`, this timeout will apply to
|
||||||
|
// each independently.
|
||||||
|
var DialPeerTimeout = 60 * time.Second
|
||||||
|
|
||||||
|
type noDialCtxKey struct{}
|
||||||
|
type dialPeerTimeoutCtxKey struct{}
|
||||||
|
type forceDirectDialCtxKey struct{}
|
||||||
|
type useTransientCtxKey struct{}
|
||||||
|
type simConnectCtxKey struct{ isClient bool }
|
||||||
|
|
||||||
|
var noDial = noDialCtxKey{}
|
||||||
|
var forceDirectDial = forceDirectDialCtxKey{}
|
||||||
|
var useTransient = useTransientCtxKey{}
|
||||||
|
var simConnectIsServer = simConnectCtxKey{}
|
||||||
|
var simConnectIsClient = simConnectCtxKey{isClient: true}
|
||||||
|
|
||||||
|
// EXPERIMENTAL
|
||||||
|
// WithForceDirectDial constructs a new context with an option that instructs the network
|
||||||
|
// to attempt to force a direct connection to a peer via a dial even if a proxied connection to it already exists.
|
||||||
|
func WithForceDirectDial(ctx context.Context, reason string) context.Context {
|
||||||
|
return context.WithValue(ctx, forceDirectDial, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPERIMENTAL
|
||||||
|
// GetForceDirectDial returns true if the force direct dial option is set in the context.
|
||||||
|
func GetForceDirectDial(ctx context.Context) (forceDirect bool, reason string) {
|
||||||
|
v := ctx.Value(forceDirectDial)
|
||||||
|
if v != nil {
|
||||||
|
return true, v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSimultaneousConnect constructs a new context with an option that instructs the transport
|
||||||
|
// to apply hole punching logic where applicable.
|
||||||
|
// EXPERIMENTAL
|
||||||
|
func WithSimultaneousConnect(ctx context.Context, isClient bool, reason string) context.Context {
|
||||||
|
if isClient {
|
||||||
|
return context.WithValue(ctx, simConnectIsClient, reason)
|
||||||
|
}
|
||||||
|
return context.WithValue(ctx, simConnectIsServer, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSimultaneousConnect returns true if the simultaneous connect option is set in the context.
|
||||||
|
// EXPERIMENTAL
|
||||||
|
func GetSimultaneousConnect(ctx context.Context) (simconnect bool, isClient bool, reason string) {
|
||||||
|
if v := ctx.Value(simConnectIsClient); v != nil {
|
||||||
|
return true, true, v.(string)
|
||||||
|
}
|
||||||
|
if v := ctx.Value(simConnectIsServer); v != nil {
|
||||||
|
return true, false, v.(string)
|
||||||
|
}
|
||||||
|
return false, false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNoDial constructs a new context with an option that instructs the network
|
||||||
|
// to not attempt a new dial when opening a stream.
|
||||||
|
func WithNoDial(ctx context.Context, reason string) context.Context {
|
||||||
|
return context.WithValue(ctx, noDial, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNoDial returns true if the no dial option is set in the context.
|
||||||
|
func GetNoDial(ctx context.Context) (nodial bool, reason string) {
|
||||||
|
v := ctx.Value(noDial)
|
||||||
|
if v != nil {
|
||||||
|
return true, v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDialPeerTimeout returns the current DialPeer timeout (or the default).
|
||||||
|
func GetDialPeerTimeout(ctx context.Context) time.Duration {
|
||||||
|
if to, ok := ctx.Value(dialPeerTimeoutCtxKey{}).(time.Duration); ok {
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
return DialPeerTimeout
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithDialPeerTimeout returns a new context with the DialPeer timeout applied.
|
||||||
|
//
|
||||||
|
// This timeout overrides the default DialPeerTimeout and applies per-dial
|
||||||
|
// independently.
|
||||||
|
func WithDialPeerTimeout(ctx context.Context, timeout time.Duration) context.Context {
|
||||||
|
return context.WithValue(ctx, dialPeerTimeoutCtxKey{}, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithUseTransient constructs a new context with an option that instructs the network
|
||||||
|
// that it is acceptable to use a transient connection when opening a new stream.
|
||||||
|
func WithUseTransient(ctx context.Context, reason string) context.Context {
|
||||||
|
return context.WithValue(ctx, useTransient, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUseTransient returns true if the use transient option is set in the context.
|
||||||
|
func GetUseTransient(ctx context.Context) (usetransient bool, reason string) {
|
||||||
|
v := ctx.Value(useTransient)
|
||||||
|
if v != nil {
|
||||||
|
return true, v.(string)
|
||||||
|
}
|
||||||
|
return false, ""
|
||||||
|
}
|
59
go-libp2p/core/network/context_test.go
Normal file
59
go-libp2p/core/network/context_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaultTimeout(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
dur := GetDialPeerTimeout(ctx)
|
||||||
|
if dur != DialPeerTimeout {
|
||||||
|
t.Fatal("expected default peer timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNonDefaultTimeout(t *testing.T) {
|
||||||
|
customTimeout := time.Duration(1)
|
||||||
|
ctx := context.WithValue(
|
||||||
|
context.Background(),
|
||||||
|
dialPeerTimeoutCtxKey{},
|
||||||
|
customTimeout,
|
||||||
|
)
|
||||||
|
dur := GetDialPeerTimeout(ctx)
|
||||||
|
if dur != customTimeout {
|
||||||
|
t.Fatal("peer timeout doesn't match set timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSettingTimeout(t *testing.T) {
|
||||||
|
customTimeout := time.Duration(1)
|
||||||
|
ctx := WithDialPeerTimeout(
|
||||||
|
context.Background(),
|
||||||
|
customTimeout,
|
||||||
|
)
|
||||||
|
dur := GetDialPeerTimeout(ctx)
|
||||||
|
if dur != customTimeout {
|
||||||
|
t.Fatal("peer timeout doesn't match set timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSimultaneousConnect(t *testing.T) {
|
||||||
|
t.Run("for the server", func(t *testing.T) {
|
||||||
|
serverCtx := WithSimultaneousConnect(context.Background(), false, "foobar")
|
||||||
|
ok, isClient, reason := GetSimultaneousConnect(serverCtx)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.False(t, isClient)
|
||||||
|
require.Equal(t, reason, "foobar")
|
||||||
|
})
|
||||||
|
t.Run("for the client", func(t *testing.T) {
|
||||||
|
serverCtx := WithSimultaneousConnect(context.Background(), true, "foo")
|
||||||
|
ok, isClient, reason := GetSimultaneousConnect(serverCtx)
|
||||||
|
require.True(t, ok)
|
||||||
|
require.True(t, isClient)
|
||||||
|
require.Equal(t, reason, "foo")
|
||||||
|
})
|
||||||
|
}
|
33
go-libp2p/core/network/errors.go
Normal file
33
go-libp2p/core/network/errors.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package network
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type temporaryError string
|
||||||
|
|
||||||
|
func (e temporaryError) Error() string { return string(e) }
|
||||||
|
func (e temporaryError) Temporary() bool { return true }
|
||||||
|
func (e temporaryError) Timeout() bool { return false }
|
||||||
|
|
||||||
|
var _ net.Error = temporaryError("")
|
||||||
|
|
||||||
|
// ErrNoRemoteAddrs is returned when there are no addresses associated with a peer during a dial.
|
||||||
|
var ErrNoRemoteAddrs = errors.New("no remote addresses")
|
||||||
|
|
||||||
|
// ErrNoConn is returned when attempting to open a stream to a peer with the NoDial
|
||||||
|
// option and no usable connection is available.
|
||||||
|
var ErrNoConn = errors.New("no usable connection to peer")
|
||||||
|
|
||||||
|
// ErrTransientConn is returned when attempting to open a stream to a peer with only a transient
|
||||||
|
// connection, without specifying the UseTransient option.
|
||||||
|
var ErrTransientConn = errors.New("transient connection to peer")
|
||||||
|
|
||||||
|
// ErrResourceLimitExceeded is returned when attempting to perform an operation that would
|
||||||
|
// exceed system resource limits.
|
||||||
|
var ErrResourceLimitExceeded = temporaryError("resource limit exceeded")
|
||||||
|
|
||||||
|
// ErrResourceScopeClosed is returned when attempting to reserve resources in a closed resource
|
||||||
|
// scope.
|
||||||
|
var ErrResourceScopeClosed = errors.New("resource scope closed")
|
131
go-libp2p/core/network/mocks/mock_conn_management_scope.go
Normal file
131
go-libp2p/core/network/mocks/mock_conn_management_scope.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/libp2p/go-libp2p/core/network (interfaces: ConnManagementScope)
|
||||||
|
|
||||||
|
// Package mocknetwork is a generated GoMock package.
|
||||||
|
package mocknetwork
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
network "github.com/libp2p/go-libp2p/core/network"
|
||||||
|
peer "github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockConnManagementScope is a mock of ConnManagementScope interface.
|
||||||
|
type MockConnManagementScope struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockConnManagementScopeMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockConnManagementScopeMockRecorder is the mock recorder for MockConnManagementScope.
|
||||||
|
type MockConnManagementScopeMockRecorder struct {
|
||||||
|
mock *MockConnManagementScope
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockConnManagementScope creates a new mock instance.
|
||||||
|
func NewMockConnManagementScope(ctrl *gomock.Controller) *MockConnManagementScope {
|
||||||
|
mock := &MockConnManagementScope{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockConnManagementScopeMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockConnManagementScope) EXPECT() *MockConnManagementScopeMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginSpan mocks base method.
|
||||||
|
func (m *MockConnManagementScope) BeginSpan() (network.ResourceScopeSpan, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "BeginSpan")
|
||||||
|
ret0, _ := ret[0].(network.ResourceScopeSpan)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginSpan indicates an expected call of BeginSpan.
|
||||||
|
func (mr *MockConnManagementScopeMockRecorder) BeginSpan() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginSpan", reflect.TypeOf((*MockConnManagementScope)(nil).BeginSpan))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done mocks base method.
|
||||||
|
func (m *MockConnManagementScope) Done() {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "Done")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done indicates an expected call of Done.
|
||||||
|
func (mr *MockConnManagementScopeMockRecorder) Done() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Done", reflect.TypeOf((*MockConnManagementScope)(nil).Done))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerScope mocks base method.
|
||||||
|
func (m *MockConnManagementScope) PeerScope() network.PeerScope {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "PeerScope")
|
||||||
|
ret0, _ := ret[0].(network.PeerScope)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerScope indicates an expected call of PeerScope.
|
||||||
|
func (mr *MockConnManagementScopeMockRecorder) PeerScope() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PeerScope", reflect.TypeOf((*MockConnManagementScope)(nil).PeerScope))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseMemory mocks base method.
|
||||||
|
func (m *MockConnManagementScope) ReleaseMemory(arg0 int) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "ReleaseMemory", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseMemory indicates an expected call of ReleaseMemory.
|
||||||
|
func (mr *MockConnManagementScopeMockRecorder) ReleaseMemory(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReleaseMemory", reflect.TypeOf((*MockConnManagementScope)(nil).ReleaseMemory), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReserveMemory mocks base method.
|
||||||
|
func (m *MockConnManagementScope) ReserveMemory(arg0 int, arg1 byte) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ReserveMemory", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReserveMemory indicates an expected call of ReserveMemory.
|
||||||
|
func (mr *MockConnManagementScopeMockRecorder) ReserveMemory(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReserveMemory", reflect.TypeOf((*MockConnManagementScope)(nil).ReserveMemory), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPeer mocks base method.
|
||||||
|
func (m *MockConnManagementScope) SetPeer(arg0 peer.ID) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "SetPeer", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPeer indicates an expected call of SetPeer.
|
||||||
|
func (mr *MockConnManagementScopeMockRecorder) SetPeer(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetPeer", reflect.TypeOf((*MockConnManagementScope)(nil).SetPeer), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat mocks base method.
|
||||||
|
func (m *MockConnManagementScope) Stat() network.ScopeStat {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Stat")
|
||||||
|
ret0, _ := ret[0].(network.ScopeStat)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat indicates an expected call of Stat.
|
||||||
|
func (mr *MockConnManagementScopeMockRecorder) Stat() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockConnManagementScope)(nil).Stat))
|
||||||
|
}
|
105
go-libp2p/core/network/mocks/mock_peer_scope.go
Normal file
105
go-libp2p/core/network/mocks/mock_peer_scope.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/libp2p/go-libp2p/core/network (interfaces: PeerScope)
|
||||||
|
|
||||||
|
// Package mocknetwork is a generated GoMock package.
|
||||||
|
package mocknetwork
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
network "github.com/libp2p/go-libp2p/core/network"
|
||||||
|
peer "github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockPeerScope is a mock of PeerScope interface.
|
||||||
|
type MockPeerScope struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockPeerScopeMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockPeerScopeMockRecorder is the mock recorder for MockPeerScope.
|
||||||
|
type MockPeerScopeMockRecorder struct {
|
||||||
|
mock *MockPeerScope
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockPeerScope creates a new mock instance.
|
||||||
|
func NewMockPeerScope(ctrl *gomock.Controller) *MockPeerScope {
|
||||||
|
mock := &MockPeerScope{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockPeerScopeMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockPeerScope) EXPECT() *MockPeerScopeMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginSpan mocks base method.
|
||||||
|
func (m *MockPeerScope) BeginSpan() (network.ResourceScopeSpan, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "BeginSpan")
|
||||||
|
ret0, _ := ret[0].(network.ResourceScopeSpan)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginSpan indicates an expected call of BeginSpan.
|
||||||
|
func (mr *MockPeerScopeMockRecorder) BeginSpan() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginSpan", reflect.TypeOf((*MockPeerScope)(nil).BeginSpan))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peer mocks base method.
|
||||||
|
func (m *MockPeerScope) Peer() peer.ID {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Peer")
|
||||||
|
ret0, _ := ret[0].(peer.ID)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peer indicates an expected call of Peer.
|
||||||
|
func (mr *MockPeerScopeMockRecorder) Peer() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Peer", reflect.TypeOf((*MockPeerScope)(nil).Peer))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseMemory mocks base method.
|
||||||
|
func (m *MockPeerScope) ReleaseMemory(arg0 int) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "ReleaseMemory", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseMemory indicates an expected call of ReleaseMemory.
|
||||||
|
func (mr *MockPeerScopeMockRecorder) ReleaseMemory(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReleaseMemory", reflect.TypeOf((*MockPeerScope)(nil).ReleaseMemory), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReserveMemory mocks base method.
|
||||||
|
func (m *MockPeerScope) ReserveMemory(arg0 int, arg1 byte) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ReserveMemory", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReserveMemory indicates an expected call of ReserveMemory.
|
||||||
|
func (mr *MockPeerScopeMockRecorder) ReserveMemory(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReserveMemory", reflect.TypeOf((*MockPeerScope)(nil).ReserveMemory), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat mocks base method.
|
||||||
|
func (m *MockPeerScope) Stat() network.ScopeStat {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Stat")
|
||||||
|
ret0, _ := ret[0].(network.ScopeStat)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat indicates an expected call of Stat.
|
||||||
|
func (mr *MockPeerScopeMockRecorder) Stat() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockPeerScope)(nil).Stat))
|
||||||
|
}
|
105
go-libp2p/core/network/mocks/mock_protocol_scope.go
Normal file
105
go-libp2p/core/network/mocks/mock_protocol_scope.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/libp2p/go-libp2p/core/network (interfaces: ProtocolScope)
|
||||||
|
|
||||||
|
// Package mocknetwork is a generated GoMock package.
|
||||||
|
package mocknetwork
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
network "github.com/libp2p/go-libp2p/core/network"
|
||||||
|
protocol "github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockProtocolScope is a mock of ProtocolScope interface.
|
||||||
|
type MockProtocolScope struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockProtocolScopeMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockProtocolScopeMockRecorder is the mock recorder for MockProtocolScope.
|
||||||
|
type MockProtocolScopeMockRecorder struct {
|
||||||
|
mock *MockProtocolScope
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockProtocolScope creates a new mock instance.
|
||||||
|
func NewMockProtocolScope(ctrl *gomock.Controller) *MockProtocolScope {
|
||||||
|
mock := &MockProtocolScope{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockProtocolScopeMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockProtocolScope) EXPECT() *MockProtocolScopeMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginSpan mocks base method.
|
||||||
|
func (m *MockProtocolScope) BeginSpan() (network.ResourceScopeSpan, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "BeginSpan")
|
||||||
|
ret0, _ := ret[0].(network.ResourceScopeSpan)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginSpan indicates an expected call of BeginSpan.
|
||||||
|
func (mr *MockProtocolScopeMockRecorder) BeginSpan() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "BeginSpan", reflect.TypeOf((*MockProtocolScope)(nil).BeginSpan))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol mocks base method.
|
||||||
|
func (m *MockProtocolScope) Protocol() protocol.ID {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Protocol")
|
||||||
|
ret0, _ := ret[0].(protocol.ID)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol indicates an expected call of Protocol.
|
||||||
|
func (mr *MockProtocolScopeMockRecorder) Protocol() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Protocol", reflect.TypeOf((*MockProtocolScope)(nil).Protocol))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseMemory mocks base method.
|
||||||
|
func (m *MockProtocolScope) ReleaseMemory(arg0 int) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
m.ctrl.Call(m, "ReleaseMemory", arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseMemory indicates an expected call of ReleaseMemory.
|
||||||
|
func (mr *MockProtocolScopeMockRecorder) ReleaseMemory(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReleaseMemory", reflect.TypeOf((*MockProtocolScope)(nil).ReleaseMemory), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReserveMemory mocks base method.
|
||||||
|
func (m *MockProtocolScope) ReserveMemory(arg0 int, arg1 byte) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ReserveMemory", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReserveMemory indicates an expected call of ReserveMemory.
|
||||||
|
func (mr *MockProtocolScopeMockRecorder) ReserveMemory(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReserveMemory", reflect.TypeOf((*MockProtocolScope)(nil).ReserveMemory), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat mocks base method.
|
||||||
|
func (m *MockProtocolScope) Stat() network.ScopeStat {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Stat")
|
||||||
|
ret0, _ := ret[0].(network.ScopeStat)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat indicates an expected call of Stat.
|
||||||
|
func (mr *MockProtocolScopeMockRecorder) Stat() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stat", reflect.TypeOf((*MockProtocolScope)(nil).Stat))
|
||||||
|
}
|
152
go-libp2p/core/network/mocks/mock_resource_manager.go
Normal file
152
go-libp2p/core/network/mocks/mock_resource_manager.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// Code generated by MockGen. DO NOT EDIT.
|
||||||
|
// Source: github.com/libp2p/go-libp2p/core/network (interfaces: ResourceManager)
|
||||||
|
|
||||||
|
// Package mocknetwork is a generated GoMock package.
|
||||||
|
package mocknetwork
|
||||||
|
|
||||||
|
import (
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
|
gomock "github.com/golang/mock/gomock"
|
||||||
|
network "github.com/libp2p/go-libp2p/core/network"
|
||||||
|
peer "github.com/libp2p/go-libp2p/core/peer"
|
||||||
|
protocol "github.com/libp2p/go-libp2p/core/protocol"
|
||||||
|
multiaddr "github.com/multiformats/go-multiaddr"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockResourceManager is a mock of ResourceManager interface.
|
||||||
|
type MockResourceManager struct {
|
||||||
|
ctrl *gomock.Controller
|
||||||
|
recorder *MockResourceManagerMockRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// MockResourceManagerMockRecorder is the mock recorder for MockResourceManager.
|
||||||
|
type MockResourceManagerMockRecorder struct {
|
||||||
|
mock *MockResourceManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMockResourceManager creates a new mock instance.
|
||||||
|
func NewMockResourceManager(ctrl *gomock.Controller) *MockResourceManager {
|
||||||
|
mock := &MockResourceManager{ctrl: ctrl}
|
||||||
|
mock.recorder = &MockResourceManagerMockRecorder{mock}
|
||||||
|
return mock
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||||
|
func (m *MockResourceManager) EXPECT() *MockResourceManagerMockRecorder {
|
||||||
|
return m.recorder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close mocks base method.
|
||||||
|
func (m *MockResourceManager) Close() error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "Close")
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close indicates an expected call of Close.
|
||||||
|
func (mr *MockResourceManagerMockRecorder) Close() *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockResourceManager)(nil).Close))
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenConnection mocks base method.
|
||||||
|
func (m *MockResourceManager) OpenConnection(arg0 network.Direction, arg1 bool, arg2 multiaddr.Multiaddr) (network.ConnManagementScope, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "OpenConnection", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(network.ConnManagementScope)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenConnection indicates an expected call of OpenConnection.
|
||||||
|
func (mr *MockResourceManagerMockRecorder) OpenConnection(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenConnection", reflect.TypeOf((*MockResourceManager)(nil).OpenConnection), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenStream mocks base method.
|
||||||
|
func (m *MockResourceManager) OpenStream(arg0 peer.ID, arg1 network.Direction) (network.StreamManagementScope, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "OpenStream", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(network.StreamManagementScope)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenStream indicates an expected call of OpenStream.
|
||||||
|
func (mr *MockResourceManagerMockRecorder) OpenStream(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OpenStream", reflect.TypeOf((*MockResourceManager)(nil).OpenStream), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewPeer mocks base method.
|
||||||
|
func (m *MockResourceManager) ViewPeer(arg0 peer.ID, arg1 func(network.PeerScope) error) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ViewPeer", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewPeer indicates an expected call of ViewPeer.
|
||||||
|
func (mr *MockResourceManagerMockRecorder) ViewPeer(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewPeer", reflect.TypeOf((*MockResourceManager)(nil).ViewPeer), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewProtocol mocks base method.
|
||||||
|
func (m *MockResourceManager) ViewProtocol(arg0 protocol.ID, arg1 func(network.ProtocolScope) error) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ViewProtocol", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewProtocol indicates an expected call of ViewProtocol.
|
||||||
|
func (mr *MockResourceManagerMockRecorder) ViewProtocol(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewProtocol", reflect.TypeOf((*MockResourceManager)(nil).ViewProtocol), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewService mocks base method.
|
||||||
|
func (m *MockResourceManager) ViewService(arg0 string, arg1 func(network.ServiceScope) error) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ViewService", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewService indicates an expected call of ViewService.
|
||||||
|
func (mr *MockResourceManagerMockRecorder) ViewService(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewService", reflect.TypeOf((*MockResourceManager)(nil).ViewService), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewSystem mocks base method.
|
||||||
|
func (m *MockResourceManager) ViewSystem(arg0 func(network.ResourceScope) error) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ViewSystem", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewSystem indicates an expected call of ViewSystem.
|
||||||
|
func (mr *MockResourceManagerMockRecorder) ViewSystem(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewSystem", reflect.TypeOf((*MockResourceManager)(nil).ViewSystem), arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewTransient mocks base method.
|
||||||
|
func (m *MockResourceManager) ViewTransient(arg0 func(network.ResourceScope) error) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "ViewTransient", arg0)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewTransient indicates an expected call of ViewTransient.
|
||||||
|
func (mr *MockResourceManagerMockRecorder) ViewTransient(arg0 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewTransient", reflect.TypeOf((*MockResourceManager)(nil).ViewTransient), arg0)
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user