From 1134f3fd403b7ef7fe6229f0fa07670cb039be22 Mon Sep 17 00:00:00 2001 From: Luca Moser <moser.luca@gmail.com> Date: Mon, 24 Feb 2020 15:27:41 +0100 Subject: [PATCH] Adds changes for v0.1.2 (#266) * New binary tangle using atomic transactions (#239) * Feat: started porting the new binary stuff * Refactor: removed unnecessary folder * Refactor: cleaned up go.mod files * Fix: removed objectsdb files * Feat: added transactionrequester package * Adds the transactionParser as a fliter and pre-processing mechanism of the tangle. (#242) * Feat: started porting the new binary stuff * Refactor: removed unnecessary folder * Refactor: cleaned up go.mod files * Fix: removed objectsdb files * Feat: added transactionrequester package * Feat: added new transactionparser as the filter for the tangle * Feat: Use hive.go autopeering (#250) * use autopeering from hive.go * update hive.go * update hive.go * Adds the TransactionRequester and some refactors (#256) * Feat: started porting the new binary stuff * Refactor: removed unnecessary folder * Refactor: cleaned up go.mod files * Fix: removed objectsdb files * Feat: added transactionrequester package * Feat: added new transactionparser as the filter for the tangle * Refactor: removed duplicate code * Fix: Log dropping packets every 1000 drops (#255) * Fix: Log dropping packets every 1000 drops * use an atomic counter for dropped message because Write() could be called concurrently * removes redundant zero * Add external check in test Co-authored-by: Luca Moser <moser.luca@gmail.com> * Fix: Update pprof port (#244) Since both Hornet and Goshimmer might be running on the same machine, update the port so that we can get debug information for both. * Remove docker specific config (#245) * :arrow_up: upgrades hive.go * :bug: checks meta_tx size * :heavy_minus_sign: removes dependencies * :sparkles: enables local testing * :sparkles: adds config plugin * Extend remote log message (#260) * Bump up Logstash and ElasticSearch memory #251 * Add version and if available GIT information to remote log message #251 * :bug: fixes flag parsing before loading config file * Feat: Add --version to cli (#264) * Adds changelog entry for v0.1.2 and bumps version number (#265) * updates changelog for v0.1.2 release * bumps version number to v0.1.2 Co-authored-by: Hans Moog <hm@mkjc.net> Co-authored-by: Wolfgang Welz <welzwo@gmail.com> Co-authored-by: jkrvivian <jkrvivian@gmail.com> Co-authored-by: Dave <44786846+centercirclesolutions@users.noreply.github.com> Co-authored-by: Angelo Capossele <angelocapossele@gmail.com> Co-authored-by: Jonas Theis <mail@jonastheis.de> --- .gitignore | 1 + CHANGELOG.md | 13 + Dockerfile | 3 +- docker.config.json | 34 -- go.mod | 17 +- go.sum | 59 ++- main.go | 3 +- packages/autopeering/discover/common.go | 65 --- packages/autopeering/discover/events.go | 35 -- packages/autopeering/discover/manager.go | 336 ------------ packages/autopeering/discover/manager_test.go | 168 ------ packages/autopeering/discover/mpeer.go | 95 ---- packages/autopeering/discover/mpeer_test.go | 189 ------- .../autopeering/discover/proto/message.go | 39 -- .../autopeering/discover/proto/message.pb.go | 280 ---------- .../autopeering/discover/proto/message.proto | 42 -- packages/autopeering/discover/protocol.go | 492 ------------------ .../autopeering/discover/protocol_test.go | 293 ----------- packages/autopeering/discover/query_strat.go | 94 ---- packages/autopeering/distance/consts.go | 5 - packages/autopeering/distance/distance.go | 33 -- .../autopeering/distance/distance_test.go | 38 -- packages/autopeering/peer/id.go | 40 -- packages/autopeering/peer/local.go | 126 ----- packages/autopeering/peer/local_test.go | 72 --- packages/autopeering/peer/peer.go | 143 ----- packages/autopeering/peer/peer_test.go | 82 --- packages/autopeering/peer/peerdb.go | 246 --------- packages/autopeering/peer/peerdb_test.go | 90 ---- .../autopeering/peer/peertest/peertest.go | 35 -- packages/autopeering/peer/proto/peer.pb.go | 94 ---- packages/autopeering/peer/proto/peer.proto | 15 - .../peer/service/proto/service.pb.go | 137 ----- .../peer/service/proto/service.proto | 17 - packages/autopeering/peer/service/record.go | 118 ----- packages/autopeering/peer/service/service.go | 26 - packages/autopeering/peer/sort.go | 38 -- packages/autopeering/peer/sort_test.go | 45 -- packages/autopeering/salt/proto/salt.pb.go | 89 ---- packages/autopeering/salt/proto/salt.proto | 12 - packages/autopeering/salt/salt.go | 86 --- packages/autopeering/salt/salt_test.go | 73 --- packages/autopeering/selection/common.go | 86 --- packages/autopeering/selection/events.go | 55 -- packages/autopeering/selection/manager.go | 371 ------------- .../autopeering/selection/manager_test.go | 216 -------- .../autopeering/selection/neighborhood.go | 132 ----- .../selection/neighborhood_test.go | 254 --------- .../autopeering/selection/proto/message.go | 35 -- .../autopeering/selection/proto/message.pb.go | 207 -------- .../autopeering/selection/proto/message.proto | 30 -- packages/autopeering/selection/protocol.go | 370 ------------- .../autopeering/selection/protocol_test.go | 185 ------- packages/autopeering/selection/selection.go | 72 --- .../autopeering/selection/selection_test.go | 165 ------ packages/autopeering/server/common.go | 38 -- packages/autopeering/server/errors.go | 17 - .../autopeering/server/proto/packet.pb.go | 97 ---- .../autopeering/server/proto/packet.proto | 11 - packages/autopeering/server/protocol.go | 32 -- packages/autopeering/server/server.go | 315 ----------- packages/autopeering/server/server_test.go | 216 -------- packages/autopeering/transport/chan.go | 116 ----- packages/autopeering/transport/chan_test.go | 78 --- packages/autopeering/transport/conn.go | 58 --- packages/autopeering/transport/conn_test.go | 41 -- packages/autopeering/transport/const.go | 5 - packages/autopeering/transport/errors.go | 8 - packages/autopeering/transport/p2p.go | 93 ---- packages/autopeering/transport/p2p_test.go | 34 -- packages/autopeering/transport/transport.go | 37 -- packages/binary/identity/constants.go | 7 + packages/binary/identity/identity.go | 48 ++ packages/binary/identity/identity_test.go | 41 ++ packages/binary/identity/type.go | 8 + packages/binary/signature/ed25119/ed25119.go | 18 + packages/binary/signature/ed25119/key_pair.go | 6 + .../binary/signature/ed25119/private_key.go | 15 + .../binary/signature/ed25119/public_key.go | 25 + .../binary/signature/ed25119/signature.go | 19 + .../binary/storageprefix/storageprefix.go | 17 + packages/binary/tangle/events.go | 39 ++ .../binary/tangle/model/approver/approver.go | 59 +++ .../tangle/model/approver/cached_approver.go | 33 ++ .../cached_missingtransaction.go | 21 + .../missingtransaction/missingtransaction.go | 53 ++ .../model/transaction/cached_transaction.go | 31 ++ .../binary/tangle/model/transaction/id.go | 34 ++ .../binary/tangle/model/transaction/init.go | 10 + .../model/transaction/payload/data/data.go | 52 ++ .../model/transaction/payload/data/init.go | 9 + .../tangle/model/transaction/payload/id.go | 5 + .../model/transaction/payload/payload.go | 12 + .../tangle/model/transaction/payload/type.go | 3 + .../transaction/payload/type_register.go | 36 ++ .../transaction/test/transaction_test.go | 77 +++ .../tangle/model/transaction/transaction.go | 277 ++++++++++ .../cached_transactionmetadata.go | 25 + .../transactionmetadata.go | 94 ++++ packages/binary/tangle/tangle.go | 323 ++++++++++++ packages/binary/tangle/tangle_test.go | 95 ++++ .../recently_seen_bytes_filter.go | 68 +++ .../transaction_signature_filter.go | 66 +++ .../tangle/transactionparser/bytes_filter.go | 8 + .../binary/tangle/transactionparser/events.go | 9 + .../transactionparser/transaction_filter.go | 12 + .../transactionparser/transactionparser.go | 137 +++++ .../transactionparser_test.go | 56 ++ .../tangle/transactionrequester/constants.go | 10 + .../tangle/transactionrequester/events.go | 9 + .../tangle/transactionrequester/options.go | 37 ++ .../transactionrequester.go | 74 +++ packages/gossip/common.go | 4 +- packages/gossip/events.go | 2 +- packages/gossip/manager.go | 2 +- packages/gossip/manager_test.go | 4 +- packages/gossip/neighbor.go | 19 +- packages/gossip/neighbor_test.go | 12 +- packages/gossip/server/handshake.go | 2 +- packages/gossip/server/server.go | 6 +- packages/gossip/server/server_test.go | 4 +- .../meta_transaction/meta_transaction.go | 5 +- .../meta_transaction/meta_transaction_test.go | 4 +- plugins/analysis/client/plugin.go | 4 +- plugins/autopeering/autopeering.go | 12 +- plugins/autopeering/local/local.go | 6 +- plugins/autopeering/plugin.go | 6 +- plugins/cli/plugin.go | 13 +- plugins/config/config.go | 64 +++ plugins/config/plugin.go | 12 + plugins/gossip/gossip.go | 4 +- plugins/gossip/plugin.go | 4 +- plugins/remotelog/plugin.go | 72 ++- plugins/remotelog/server/docker-compose.yml | 4 +- plugins/spa/plugin.go | 2 +- plugins/tangle/solidifier.go | 8 +- plugins/tangle/solidifier_test.go | 6 +- plugins/webapi/getNeighbors/plugin.go | 4 +- 138 files changed, 2289 insertions(+), 7266 deletions(-) delete mode 100644 docker.config.json delete mode 100644 packages/autopeering/discover/common.go delete mode 100644 packages/autopeering/discover/events.go delete mode 100644 packages/autopeering/discover/manager.go delete mode 100644 packages/autopeering/discover/manager_test.go delete mode 100644 packages/autopeering/discover/mpeer.go delete mode 100644 packages/autopeering/discover/mpeer_test.go delete mode 100644 packages/autopeering/discover/proto/message.go delete mode 100644 packages/autopeering/discover/proto/message.pb.go delete mode 100644 packages/autopeering/discover/proto/message.proto delete mode 100644 packages/autopeering/discover/protocol.go delete mode 100644 packages/autopeering/discover/protocol_test.go delete mode 100644 packages/autopeering/discover/query_strat.go delete mode 100644 packages/autopeering/distance/consts.go delete mode 100644 packages/autopeering/distance/distance.go delete mode 100644 packages/autopeering/distance/distance_test.go delete mode 100644 packages/autopeering/peer/id.go delete mode 100644 packages/autopeering/peer/local.go delete mode 100644 packages/autopeering/peer/local_test.go delete mode 100644 packages/autopeering/peer/peer.go delete mode 100644 packages/autopeering/peer/peer_test.go delete mode 100644 packages/autopeering/peer/peerdb.go delete mode 100644 packages/autopeering/peer/peerdb_test.go delete mode 100644 packages/autopeering/peer/peertest/peertest.go delete mode 100644 packages/autopeering/peer/proto/peer.pb.go delete mode 100644 packages/autopeering/peer/proto/peer.proto delete mode 100644 packages/autopeering/peer/service/proto/service.pb.go delete mode 100644 packages/autopeering/peer/service/proto/service.proto delete mode 100644 packages/autopeering/peer/service/record.go delete mode 100644 packages/autopeering/peer/service/service.go delete mode 100644 packages/autopeering/peer/sort.go delete mode 100644 packages/autopeering/peer/sort_test.go delete mode 100644 packages/autopeering/salt/proto/salt.pb.go delete mode 100644 packages/autopeering/salt/proto/salt.proto delete mode 100644 packages/autopeering/salt/salt.go delete mode 100644 packages/autopeering/salt/salt_test.go delete mode 100644 packages/autopeering/selection/common.go delete mode 100644 packages/autopeering/selection/events.go delete mode 100644 packages/autopeering/selection/manager.go delete mode 100644 packages/autopeering/selection/manager_test.go delete mode 100644 packages/autopeering/selection/neighborhood.go delete mode 100644 packages/autopeering/selection/neighborhood_test.go delete mode 100644 packages/autopeering/selection/proto/message.go delete mode 100644 packages/autopeering/selection/proto/message.pb.go delete mode 100644 packages/autopeering/selection/proto/message.proto delete mode 100644 packages/autopeering/selection/protocol.go delete mode 100644 packages/autopeering/selection/protocol_test.go delete mode 100644 packages/autopeering/selection/selection.go delete mode 100644 packages/autopeering/selection/selection_test.go delete mode 100644 packages/autopeering/server/common.go delete mode 100644 packages/autopeering/server/errors.go delete mode 100644 packages/autopeering/server/proto/packet.pb.go delete mode 100644 packages/autopeering/server/proto/packet.proto delete mode 100644 packages/autopeering/server/protocol.go delete mode 100644 packages/autopeering/server/server.go delete mode 100644 packages/autopeering/server/server_test.go delete mode 100644 packages/autopeering/transport/chan.go delete mode 100644 packages/autopeering/transport/chan_test.go delete mode 100644 packages/autopeering/transport/conn.go delete mode 100644 packages/autopeering/transport/conn_test.go delete mode 100644 packages/autopeering/transport/const.go delete mode 100644 packages/autopeering/transport/errors.go delete mode 100644 packages/autopeering/transport/p2p.go delete mode 100644 packages/autopeering/transport/p2p_test.go delete mode 100644 packages/autopeering/transport/transport.go create mode 100644 packages/binary/identity/constants.go create mode 100644 packages/binary/identity/identity.go create mode 100644 packages/binary/identity/identity_test.go create mode 100644 packages/binary/identity/type.go create mode 100644 packages/binary/signature/ed25119/ed25119.go create mode 100644 packages/binary/signature/ed25119/key_pair.go create mode 100644 packages/binary/signature/ed25119/private_key.go create mode 100644 packages/binary/signature/ed25119/public_key.go create mode 100644 packages/binary/signature/ed25119/signature.go create mode 100644 packages/binary/storageprefix/storageprefix.go create mode 100644 packages/binary/tangle/events.go create mode 100644 packages/binary/tangle/model/approver/approver.go create mode 100644 packages/binary/tangle/model/approver/cached_approver.go create mode 100644 packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go create mode 100644 packages/binary/tangle/model/missingtransaction/missingtransaction.go create mode 100644 packages/binary/tangle/model/transaction/cached_transaction.go create mode 100644 packages/binary/tangle/model/transaction/id.go create mode 100644 packages/binary/tangle/model/transaction/init.go create mode 100644 packages/binary/tangle/model/transaction/payload/data/data.go create mode 100644 packages/binary/tangle/model/transaction/payload/data/init.go create mode 100644 packages/binary/tangle/model/transaction/payload/id.go create mode 100644 packages/binary/tangle/model/transaction/payload/payload.go create mode 100644 packages/binary/tangle/model/transaction/payload/type.go create mode 100644 packages/binary/tangle/model/transaction/payload/type_register.go create mode 100644 packages/binary/tangle/model/transaction/test/transaction_test.go create mode 100644 packages/binary/tangle/model/transaction/transaction.go create mode 100644 packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go create mode 100644 packages/binary/tangle/model/transactionmetadata/transactionmetadata.go create mode 100644 packages/binary/tangle/tangle.go create mode 100644 packages/binary/tangle/tangle_test.go create mode 100644 packages/binary/tangle/transactionparser/builtinfilters/recently_seen_bytes_filter.go create mode 100644 packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go create mode 100644 packages/binary/tangle/transactionparser/bytes_filter.go create mode 100644 packages/binary/tangle/transactionparser/events.go create mode 100644 packages/binary/tangle/transactionparser/transaction_filter.go create mode 100644 packages/binary/tangle/transactionparser/transactionparser.go create mode 100644 packages/binary/tangle/transactionparser/transactionparser_test.go create mode 100644 packages/binary/tangle/transactionrequester/constants.go create mode 100644 packages/binary/tangle/transactionrequester/events.go create mode 100644 packages/binary/tangle/transactionrequester/options.go create mode 100644 packages/binary/tangle/transactionrequester/transactionrequester.go create mode 100644 plugins/config/config.go create mode 100644 plugins/config/plugin.go diff --git a/.gitignore b/.gitignore index 52422ce9..b376e693 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ testNodes/* # Database directory mainnetdb/ +objectsdb/ # OSX related files .DS_Store diff --git a/CHANGELOG.md b/CHANGELOG.md index b47ac3d5..cff14c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# v0.1.2 - 2020-02-24 + +* Adds `--version` flag to retrieve the GoShimmer version +* Adds the version and commit hash to the remote log logging +* Replaces the autopeering module with the one from hive.go +* Changed the pprof listen port to `6061` to avoid conflict with Hornet +* Fixes `invalid stored peer` messages +* Fixes masternodes getting removed if they were offline +* Fixes `-c` and `-d` to define config file/dir +* Fixes drop messages about full queues appearing too many times +* Fixes crash due to incopatible transaction size +* Changed the salt lifetime to 2 hours from 30 minutes + # v0.1.1 - 2020-02-07 This release contains a series of fixes: diff --git a/Dockerfile b/Dockerfile index 381f8824..fda2f888 100644 --- a/Dockerfile +++ b/Dockerfile @@ -27,11 +27,10 @@ VOLUME /app/mainnetdb EXPOSE 14666/tcp EXPOSE 14626/udp -EXPOSE 14626/tcp # Copy the Pre-built binary file from the previous stage COPY --from=build /go/bin/goshimmer . # Copy the docker config -COPY docker.config.json config.json +COPY config.json config.json ENTRYPOINT ["./goshimmer"] diff --git a/docker.config.json b/docker.config.json deleted file mode 100644 index ff931575..00000000 --- a/docker.config.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "analysis": { - "serveraddress": "ressims.iota.cafe:188", - "serverport": 0 - }, - "autopeering": { - "address": "0.0.0.0", - "entrynodes": [ - "V8LYtWWcPYYDTTXLeIEFjJEuWlsjDiI0+Pq/Cx9ai6g=@116.202.49.178:14626" - ], - "port": 14626 - }, - "database": { - "directory": "mainnetdb" - }, - "gossip": { - "port": 14666 - }, - "logger": { - "Level": "info", - "DisableCaller": true, - "DisableStacktrace": false, - "Encoding": "console", - "OutputPaths": [ - "stdout", - "goshimmer.log" - ], - "DisableEvents": true - }, - "node": { - "disablePlugins": [], - "enablePlugins": [] - } -} diff --git a/go.mod b/go.mod index af0a116b..88641721 100644 --- a/go.mod +++ b/go.mod @@ -13,31 +13,36 @@ require ( github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2 github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0 github.com/gorilla/websocket v1.4.1 - github.com/iotaledger/hive.go v0.0.0-20200207144536-27b18f10f09e + github.com/iotaledger/hive.go v0.0.0-20200219224037-2d5f5238c0de github.com/iotaledger/iota.go v1.0.0-beta.14 + github.com/kr/pretty v0.2.0 // indirect + github.com/kr/text v0.2.0 // indirect github.com/labstack/echo v3.3.10+incompatible github.com/labstack/gommon v0.3.0 // indirect github.com/magiconair/properties v1.8.1 github.com/mattn/go-colorable v0.1.4 // indirect github.com/mattn/go-isatty v0.0.11 // indirect + github.com/mr-tron/base58 v1.1.3 + github.com/oasislabs/ed25519 v0.0.0-20200206134218-2893bee822a3 + github.com/panjf2000/ants/v2 v2.2.2 github.com/pelletier/go-toml v1.6.0 // indirect github.com/pkg/errors v0.9.1 github.com/rogpeppe/go-internal v1.5.2 // indirect + github.com/sergi/go-diff v1.1.0 // indirect github.com/spf13/afero v1.2.2 // indirect github.com/spf13/cast v1.3.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.6.1 - github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.4.0 github.com/valyala/fasttemplate v1.1.0 // indirect go.uber.org/atomic v1.5.1 go.uber.org/zap v1.13.0 - golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d // indirect - golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 - golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 // indirect - golang.org/x/text v0.3.2 // indirect + golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 + golang.org/x/net v0.0.0-20200202094626-16171245cfb2 + golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7 // indirect gopkg.in/ini.v1 v1.51.1 // indirect + gopkg.in/src-d/go-git.v4 v4.13.1 gopkg.in/yaml.v2 v2.2.7 // indirect ) diff --git a/go.sum b/go.sum index a943eb06..c1a5c4ac 100644 --- a/go.sum +++ b/go.sum @@ -13,10 +13,13 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/beevik/ntp v0.2.0/go.mod h1:hIHWr+l3+/clUnF44zdK+CWW7fO8dR5cIylAQ76NRpg= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -32,6 +35,8 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -50,11 +55,14 @@ github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b/go.mod h1:SqUrOPUn github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -90,6 +98,7 @@ github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -123,18 +132,24 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/iotaledger/hive.go v0.0.0-20200207144536-27b18f10f09e h1:eM0/mEG3ZPflZRJxOn0cc+/8ZZIpirEAyhzF4rzTKjM= -github.com/iotaledger/hive.go v0.0.0-20200207144536-27b18f10f09e/go.mod h1:wj3bFHlcX0NiEOWu5+WOg/MI/5N7PKCFnyaziaylB64= +github.com/iotaledger/hive.go v0.0.0-20200219224037-2d5f5238c0de h1:J9G9YWM5q7r3DObMIx/Qc8CUjrpD+c90EPVKjsBrR+E= +github.com/iotaledger/hive.go v0.0.0-20200219224037-2d5f5238c0de/go.mod h1:wj3bFHlcX0NiEOWu5+WOg/MI/5N7PKCFnyaziaylB64= github.com/iotaledger/iota.go v1.0.0-beta.9/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= github.com/iotaledger/iota.go v1.0.0-beta.14 h1:Oeb28MfBuJEeXcGrLhTCJFtbsnc8y1u7xidsAmiOD5A= github.com/iotaledger/iota.go v1.0.0-beta.14/go.mod h1:F6WBmYd98mVjAmmPVYhnxg8NNIWCjjH8VWT9qvv3Rc8= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -143,10 +158,15 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= @@ -163,13 +183,18 @@ github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGe github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= +github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/oasislabs/ed25519 v0.0.0-20200206134218-2893bee822a3 h1:xhsvlWpWPdHXHt8i5eaXf2WbAxHLciGqrfup6zAPjVQ= +github.com/oasislabs/ed25519 v0.0.0-20200206134218-2893bee822a3/go.mod h1:xIpCyrK2ouGA4QBGbiNbkoONrvJ00u9P3QOkXSOAC0c= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w= @@ -177,7 +202,9 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/panjf2000/ants/v2 v2.2.2 h1:TWzusBjq/IflXhy+/S6u5wmMLCBdJnB9tPIx9Zmhvok= github.com/panjf2000/ants/v2 v2.2.2/go.mod h1:1GFm8bV8nyCQvU5K4WvBCTG1/YBFOD2VzjffD8fV55A= +github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= @@ -213,7 +240,10 @@ github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y= github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= @@ -268,6 +298,8 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spf13/viper v1.5.0/go.mod h1:AkYRkVJF8TkSG/xet6PzXX+l39KhhXa2pdqVSxnTcn4= github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk= github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k= +github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= +github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= @@ -288,6 +320,8 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= @@ -314,14 +348,19 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191119213627-4f8c1d86b1ba/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U= golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg= +golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -343,8 +382,11 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -363,16 +405,20 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -389,6 +435,7 @@ golang.org/x/tools v0.0.0-20190214204934-8dcb7bc8c7fe/go.mod h1:E6PF97AdD6v0s+fP golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -431,8 +478,16 @@ gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1 h1:GyboHr4UqMiLUybYjd22ZjQIKEJEpgtLXtuGbR21Oho= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= +gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg= +gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= +gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= +gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 2f947172..b45501ba 100644 --- a/main.go +++ b/main.go @@ -29,9 +29,10 @@ import ( ) func main() { + cli.PrintVersion() cli.LoadConfig() - go http.ListenAndServe("localhost:6060", nil) // pprof Server for Debbuging Mutexes + go http.ListenAndServe("localhost:6061", nil) // pprof Server for Debbuging Mutexes node.Run( node.Plugins( diff --git a/packages/autopeering/discover/common.go b/packages/autopeering/discover/common.go deleted file mode 100644 index 99b092ea..00000000 --- a/packages/autopeering/discover/common.go +++ /dev/null @@ -1,65 +0,0 @@ -package discover - -import ( - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/hive.go/logger" -) - -// Default values for the global parameters -const ( - DefaultReverifyInterval = 10 * time.Second - DefaultQueryInterval = 60 * time.Second - DefaultMaxManaged = 1000 - DefaultMaxReplacements = 10 -) - -var ( - reverifyInterval = DefaultReverifyInterval // time interval after which the next peer is reverified - queryInterval = DefaultQueryInterval // time interval after which peers are queried for new peers - maxManaged = DefaultMaxManaged // maximum number of peers that can be managed - maxReplacements = DefaultMaxReplacements // maximum number of peers kept in the replacement list -) - -// Config holds discovery related settings. -type Config struct { - // These settings are required and configure the listener: - Log *logger.Logger - - // These settings are optional: - MasterPeers []*peer.Peer // list of master peers used for bootstrapping -} - -// Parameters holds the parameters that can be configured. -type Parameters struct { - ReverifyInterval time.Duration // time interval after which the next peer is reverified - QueryInterval time.Duration // time interval after which peers are queried for new peers - MaxManaged int // maximum number of peers that can be managed - MaxReplacements int // maximum number of peers kept in the replacement list -} - -// SetParameters sets the global parameters for this package. -// This function cannot be used concurrently. -func SetParameter(param Parameters) { - if param.ReverifyInterval > 0 { - reverifyInterval = param.ReverifyInterval - } else { - reverifyInterval = DefaultReverifyInterval - } - if param.QueryInterval > 0 { - queryInterval = param.QueryInterval - } else { - queryInterval = DefaultQueryInterval - } - if param.MaxManaged > 0 { - maxManaged = param.MaxManaged - } else { - maxManaged = DefaultMaxManaged - } - if param.MaxReplacements > 0 { - maxReplacements = param.MaxReplacements - } else { - maxReplacements = DefaultMaxReplacements - } -} diff --git a/packages/autopeering/discover/events.go b/packages/autopeering/discover/events.go deleted file mode 100644 index 177c6be8..00000000 --- a/packages/autopeering/discover/events.go +++ /dev/null @@ -1,35 +0,0 @@ -package discover - -import ( - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/hive.go/events" -) - -// Events contains all the events that are triggered during the peer discovery. -var Events = struct { - // A PeerDiscovered event is triggered, when a new peer has been discovered and verified. - PeerDiscovered *events.Event - // A PeerDeleted event is triggered, when a discovered and verified peer could not be reverified. - PeerDeleted *events.Event -}{ - PeerDiscovered: events.NewEvent(peerDiscovered), - PeerDeleted: events.NewEvent(peerDeleted), -} - -// DiscoveredEvent bundles the information of the discovered peer. -type DiscoveredEvent struct { - Peer *peer.Peer // discovered peer -} - -// DeletedEvent bundles the information of the deleted peer. -type DeletedEvent struct { - Peer *peer.Peer // deleted peer -} - -func peerDiscovered(handler interface{}, params ...interface{}) { - handler.(func(*DiscoveredEvent))(params[0].(*DiscoveredEvent)) -} - -func peerDeleted(handler interface{}, params ...interface{}) { - handler.(func(*DeletedEvent))(params[0].(*DeletedEvent)) -} diff --git a/packages/autopeering/discover/manager.go b/packages/autopeering/discover/manager.go deleted file mode 100644 index 3bd36f06..00000000 --- a/packages/autopeering/discover/manager.go +++ /dev/null @@ -1,336 +0,0 @@ -package discover - -import ( - "math/rand" - "sync" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/server" - "github.com/iotaledger/hive.go/logger" -) - -const ( - // PingExpiration is the time until a peer verification expires. - PingExpiration = 12 * time.Hour - // MaxPeersInResponse is the maximum number of peers returned in DiscoveryResponse. - MaxPeersInResponse = 6 - // MaxServices is the maximum number of services a peer can support. - MaxServices = 5 - - // VersionNum specifies the expected version number for this Protocol. - VersionNum = 0 -) - -type network interface { - local() *peer.Local - - Ping(*peer.Peer) error - DiscoveryRequest(*peer.Peer) ([]*peer.Peer, error) -} - -type manager struct { - mutex sync.Mutex // protects active and replacement - active []*mpeer - replacements []*mpeer - - net network - log *logger.Logger - - wg sync.WaitGroup - closing chan struct{} -} - -func newManager(net network, masters []*peer.Peer, log *logger.Logger) *manager { - m := &manager{ - active: make([]*mpeer, 0, maxManaged), - replacements: make([]*mpeer, 0, maxReplacements), - net: net, - log: log, - closing: make(chan struct{}), - } - m.loadInitialPeers(masters) - - return m -} - -func (m *manager) start() { - m.wg.Add(1) - go m.loop() -} - -func (m *manager) self() peer.ID { - return m.net.local().ID() -} - -func (m *manager) close() { - close(m.closing) - m.wg.Wait() -} - -func (m *manager) loop() { - defer m.wg.Done() - - var ( - reverify = time.NewTimer(0) // setting this to 0 will cause a trigger right away - reverifyDone chan struct{} - - query = time.NewTimer(server.ResponseTimeout) // trigger the first query after the reverify - queryNext chan time.Duration - ) - defer reverify.Stop() - defer query.Stop() - -Loop: - for { - select { - // start verification, if not yet running - case <-reverify.C: - // if there is no reverifyDone, this means doReverify is not running - if reverifyDone == nil { - reverifyDone = make(chan struct{}) - go m.doReverify(reverifyDone) - } - - // reset verification - case <-reverifyDone: - reverifyDone = nil - reverify.Reset(reverifyInterval) // reverify again after the given interval - - // start requesting new peers, if no yet running - case <-query.C: - if queryNext == nil { - queryNext = make(chan time.Duration) - go m.doQuery(queryNext) - } - - // on query done, reset time to given duration - case d := <-queryNext: - queryNext = nil - query.Reset(d) - - // on close, exit the loop - case <-m.closing: - break Loop - } - } - - // wait for spawned goroutines to finish - if reverifyDone != nil { - <-reverifyDone - } - if queryNext != nil { - <-queryNext - } -} - -// doReverify pings the oldest active peer. -func (m *manager) doReverify(done chan<- struct{}) { - defer close(done) - - p := m.peerToReverify() - if p == nil { - return // nothing can be reverified - } - m.log.Debugw("reverifying", - "id", p.ID(), - "addr", p.Address(), - ) - - // could not verify the peer - if m.net.Ping(unwrapPeer(p)) != nil { - m.mutex.Lock() - defer m.mutex.Unlock() - - m.active, _ = deletePeerByID(m.active, p.ID()) - m.log.Debugw("remove dead", - "peer", p, - ) - Events.PeerDeleted.Trigger(&DeletedEvent{Peer: unwrapPeer(p)}) - - // add a random replacement, if available - if len(m.replacements) > 0 { - var r *mpeer - m.replacements, r = deletePeer(m.replacements, rand.Intn(len(m.replacements))) - m.active = pushPeer(m.active, r, maxManaged) - } - return - } - - // no need to do anything here, as the peer is bumped when handling the pong -} - -// peerToReverify returns the oldest peer, or nil if empty. -func (m *manager) peerToReverify() *mpeer { - m.mutex.Lock() - defer m.mutex.Unlock() - - if len(m.active) == 0 { - return nil - } - // the last peer is the oldest - return m.active[len(m.active)-1] -} - -// updatePeer moves the peer with the given ID to the front of the list of managed peers. -// It returns 0 if there was no peer with that id, otherwise the verifiedCount of the updated peer is returned. -func (m *manager) updatePeer(update *peer.Peer) uint { - id := update.ID() - for i, p := range m.active { - if p.ID() == id { - if i > 0 { - // move i-th peer to the front - copy(m.active[1:], m.active[:i]) - } - // replace first mpeer with a wrap of the updated peer - m.active[0] = &mpeer{ - Peer: *update, - verifiedCount: p.verifiedCount + 1, - lastNewPeers: p.lastNewPeers, - } - return p.verifiedCount + 1 - } - } - return 0 -} - -func (m *manager) addReplacement(p *mpeer) bool { - if containsPeer(m.replacements, p.ID()) { - return false // already in the list - } - m.replacements = unshiftPeer(m.replacements, p, maxReplacements) - return true -} - -func (m *manager) loadInitialPeers(masters []*peer.Peer) { - var peers []*peer.Peer - - db := m.net.local().Database() - if db != nil { - peers = db.SeedPeers() - } - - peers = append(peers, masters...) - for _, p := range peers { - m.addDiscoveredPeer(p) - } -} - -// addDiscoveredPeer adds a newly discovered peer that has never been verified or pinged yet. -// It returns true, if the given peer was new and added, false otherwise. -func (m *manager) addDiscoveredPeer(p *peer.Peer) bool { - // never add the local peer - if p.ID() == m.self() { - return false - } - - m.mutex.Lock() - defer m.mutex.Unlock() - - if containsPeer(m.active, p.ID()) { - return false - } - m.log.Debugw("discovered", - "peer", p, - ) - - mp := wrapPeer(p) - if len(m.active) >= maxManaged { - return m.addReplacement(mp) - } - - m.active = pushPeer(m.active, mp, maxManaged) - return true -} - -// addVerifiedPeer adds a new peer that has just been successfully pinged. -// It returns true, if the given peer was new and added, false otherwise. -func (m *manager) addVerifiedPeer(p *peer.Peer) bool { - // never add the local peer - if p.ID() == m.self() { - return false - } - - m.log.Debugw("verified", - "peer", p, - "services", p.Services(), - ) - - m.mutex.Lock() - defer m.mutex.Unlock() - - // if already in the list, move it to the front - if v := m.updatePeer(p); v > 0 { - // trigger the event only for the first time the peer is updated - if v == 1 { - Events.PeerDiscovered.Trigger(&DiscoveredEvent{Peer: p}) - } - return false - } - - mp := wrapPeer(p) - mp.verifiedCount = 1 - - if len(m.active) >= maxManaged { - return m.addReplacement(mp) - } - // trigger the event only when the peer is added to active - Events.PeerDiscovered.Trigger(&DiscoveredEvent{Peer: p}) - - // new nodes are added to the front - m.active = unshiftPeer(m.active, mp, maxManaged) - return true -} - -// getRandomPeers returns a list of randomly selected peers. -func (m *manager) getRandomPeers(n int, minVerified uint) []*peer.Peer { - m.mutex.Lock() - defer m.mutex.Unlock() - - if n > len(m.active) { - n = len(m.active) - } - - peers := make([]*peer.Peer, 0, n) - for _, i := range rand.Perm(len(m.active)) { - if len(peers) == n { - break - } - - mp := m.active[i] - if mp.verifiedCount < minVerified { - continue - } - peers = append(peers, unwrapPeer(mp)) - } - - return peers -} - -// getVerifiedPeers returns all the currently managed peers that have been verified at least once. -func (m *manager) getVerifiedPeers() []*mpeer { - m.mutex.Lock() - defer m.mutex.Unlock() - - peers := make([]*mpeer, 0, len(m.active)) - for _, mp := range m.active { - if mp.verifiedCount == 0 { - continue - } - peers = append(peers, mp) - } - - return peers -} - -// isKnown returns true if the manager is keeping track of that peer. -func (m *manager) isKnown(id peer.ID) bool { - if id == m.self() { - return true - } - - m.mutex.Lock() - defer m.mutex.Unlock() - - return containsPeer(m.active, id) || containsPeer(m.replacements, id) -} diff --git a/packages/autopeering/discover/manager_test.go b/packages/autopeering/discover/manager_test.go deleted file mode 100644 index 1ae1494d..00000000 --- a/packages/autopeering/discover/manager_test.go +++ /dev/null @@ -1,168 +0,0 @@ -package discover - -import ( - "fmt" - "testing" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/peertest" - "github.com/iotaledger/goshimmer/packages/autopeering/server" - "github.com/iotaledger/goshimmer/packages/database/mapdb" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -func TestMgrClose(t *testing.T) { - _, _, teardown := newManagerTest(t) - defer teardown() - - time.Sleep(graceTime) -} - -func TestMgrVerifyDiscoveredPeer(t *testing.T) { - mgr, m, teardown := newManagerTest(t) - defer teardown() - - p := peertest.NewPeer(testNetwork, "p") - - // expect Ping of peer p - m.On("Ping", p).Return(nil).Once() - // ignore DiscoveryRequest calls - m.On("DiscoveryRequest", mock.Anything).Return([]*peer.Peer{}, nil).Maybe() - - // let the manager initialize - time.Sleep(graceTime) - - mgr.addDiscoveredPeer(p) - - mgr.doReverify(make(chan struct{})) // manually trigger a verify - m.AssertExpectations(t) -} - -func TestMgrReverifyPeer(t *testing.T) { - mgr, m, teardown := newManagerTest(t) - defer teardown() - - p := peertest.NewPeer(testNetwork, "p") - - // expect Ping of peer p - m.On("Ping", p).Return(nil).Once() - // ignore DiscoveryRequest calls - m.On("DiscoveryRequest", mock.Anything).Return([]*peer.Peer{}, nil).Maybe() - - // let the manager initialize - time.Sleep(graceTime) - - mgr.addVerifiedPeer(p) - - mgr.doReverify(make(chan struct{})) // manually trigger a verify - m.AssertExpectations(t) -} - -func TestMgrRequestDiscoveredPeer(t *testing.T) { - mgr, m, teardown := newManagerTest(t) - defer teardown() - - p1 := peertest.NewPeer(testNetwork, "verified") - p2 := peertest.NewPeer(testNetwork, "discovered") - - // expect DiscoveryRequest on the discovered peer - m.On("DiscoveryRequest", p1).Return([]*peer.Peer{p2}, nil).Once() - // ignore any Ping - m.On("Ping", mock.Anything).Return(nil).Maybe() - - mgr.addVerifiedPeer(p1) - mgr.addDiscoveredPeer(p2) - - mgr.doQuery(make(chan time.Duration, 1)) // manually trigger a query - m.AssertExpectations(t) -} - -func TestMgrAddManyVerifiedPeers(t *testing.T) { - mgr, m, teardown := newManagerTest(t) - defer teardown() - - p := peertest.NewPeer(testNetwork, "p") - - // expect Ping of peer p - m.On("Ping", p).Return(nil).Once() - // ignore DiscoveryRequest calls - m.On("DiscoveryRequest", mock.Anything).Return([]*peer.Peer{}, nil).Maybe() - - // let the manager initialize - time.Sleep(graceTime) - - mgr.addVerifiedPeer(p) - for i := 0; i < maxManaged+maxReplacements; i++ { - mgr.addVerifiedPeer(peertest.NewPeer(testNetwork, fmt.Sprintf("p%d", i))) - } - - mgr.doReverify(make(chan struct{})) // manually trigger a verify - ps := unwrapPeers(mgr.getVerifiedPeers()) - - assert.Equal(t, maxManaged, len(ps)) - assert.Contains(t, ps, p) - - m.AssertExpectations(t) -} - -func TestMgrDeleteUnreachablePeer(t *testing.T) { - mgr, m, teardown := newManagerTest(t) - defer teardown() - - p := peertest.NewPeer(testNetwork, "p") - - // expect Ping of peer p, but return error - m.On("Ping", p).Return(server.ErrTimeout).Times(1) - // ignore DiscoveryRequest calls - m.On("DiscoveryRequest", mock.Anything).Return([]*peer.Peer{}, nil).Maybe() - - // let the manager initialize - time.Sleep(graceTime) - - mgr.addVerifiedPeer(p) - for i := 0; i < maxManaged; i++ { - mgr.addVerifiedPeer(peertest.NewPeer(testNetwork, fmt.Sprintf("p%d", i))) - } - - mgr.doReverify(make(chan struct{})) // manually trigger a verify - ps := unwrapPeers(mgr.getVerifiedPeers()) - - assert.Equal(t, maxManaged, len(ps)) - assert.NotContains(t, ps, p) - - m.AssertExpectations(t) -} - -type NetworkMock struct { - mock.Mock - - loc *peer.Local -} - -func newManagerTest(t require.TestingT) (*manager, *NetworkMock, func()) { - db, err := peer.NewDB(mapdb.NewMapDB()) - require.NoError(t, err) - local := peertest.NewLocal(testNetwork, testAddress, db) - networkMock := &NetworkMock{ - loc: local, - } - mgr := newManager(networkMock, nil, log) - return mgr, networkMock, mgr.close -} - -func (m *NetworkMock) local() *peer.Local { - return m.loc -} - -func (m *NetworkMock) Ping(p *peer.Peer) error { - args := m.Called(p) - return args.Error(0) -} - -func (m *NetworkMock) DiscoveryRequest(p *peer.Peer) ([]*peer.Peer, error) { - args := m.Called(p) - return args.Get(0).([]*peer.Peer), args.Error(1) -} diff --git a/packages/autopeering/discover/mpeer.go b/packages/autopeering/discover/mpeer.go deleted file mode 100644 index 7116ad85..00000000 --- a/packages/autopeering/discover/mpeer.go +++ /dev/null @@ -1,95 +0,0 @@ -package discover - -import ( - "fmt" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" -) - -// mpeer represents a discovered peer with additional data. -// The fields of Peer may not be modified. -type mpeer struct { - peer.Peer - - verifiedCount uint // how often that peer has been reverified - lastNewPeers uint // number of returned new peers when queried the last time -} - -func wrapPeer(p *peer.Peer) *mpeer { - return &mpeer{Peer: *p} -} - -func unwrapPeer(p *mpeer) *peer.Peer { - return &p.Peer -} - -func unwrapPeers(ps []*mpeer) []*peer.Peer { - result := make([]*peer.Peer, len(ps)) - for i, n := range ps { - result[i] = unwrapPeer(n) - } - return result -} - -// containsPeer returns true if a peer with the given ID is in the list. -func containsPeer(list []*mpeer, id peer.ID) bool { - for _, p := range list { - if p.ID() == id { - return true - } - } - return false -} - -// unshiftPeer adds a new peer to the front of the list. -// If the list already contains max peers, the last is discarded. -func unshiftPeer(list []*mpeer, p *mpeer, max int) []*mpeer { - if len(list) > max { - panic(fmt.Sprintf("mpeer: invalid max value %d", max)) - } - if len(list) < max { - list = append(list, nil) - } - copy(list[1:], list) - list[0] = p - - return list -} - -// deletePeer is a helper that deletes the peer with the given index from the list. -func deletePeer(list []*mpeer, i int) ([]*mpeer, *mpeer) { - if i >= len(list) { - panic("mpeer: invalid index or empty mpeer list") - } - p := list[i] - - copy(list[i:], list[i+1:]) - list[len(list)-1] = nil - - return list[:len(list)-1], p -} - -// deletePeerByID deletes the peer with the given ID from the list. -func deletePeerByID(list []*mpeer, id peer.ID) ([]*mpeer, *mpeer) { - for i, p := range list { - if p.ID() == id { - return deletePeer(list, i) - } - } - panic("mpeer: id not contained in list") -} - -// pushPeer adds the given peer to the pack of the list. -// If the list already contains max peers, the first is discarded. -func pushPeer(list []*mpeer, p *mpeer, max int) []*mpeer { - if len(list) > max { - panic(fmt.Sprintf("mpeer: invalid max value %d", max)) - } - if len(list) == max { - copy(list, list[1:]) - list[len(list)-1] = p - return list - } - - return append(list, p) -} diff --git a/packages/autopeering/discover/mpeer_test.go b/packages/autopeering/discover/mpeer_test.go deleted file mode 100644 index f917382d..00000000 --- a/packages/autopeering/discover/mpeer_test.go +++ /dev/null @@ -1,189 +0,0 @@ -package discover - -import ( - "fmt" - "testing" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/peertest" - "github.com/stretchr/testify/assert" -) - -func TestUnwrapPeers(t *testing.T) { - m := make([]*mpeer, 5) - p := make([]*peer.Peer, 5) - for i := range m { - p[i] = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - m[i] = &mpeer{Peer: *p[i]} - } - - unwrapP := unwrapPeers(m) - assert.Equal(t, p, unwrapP, "unwrapPeers") -} - -func TestContainsPeer(t *testing.T) { - m := make([]*mpeer, 5) - p := make([]*peer.Peer, 5) - k := peertest.NewPeer(testNetwork, "k") - for i := range m { - p[i] = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - m[i] = &mpeer{Peer: *p[i]} - } - - for i := range m { - assert.Equal(t, true, containsPeer(m, p[i].ID()), "Contains") - } - assert.Equal(t, false, containsPeer(m, k.ID()), "Contains") -} - -func TestUnshiftPeer(t *testing.T) { - m := make([]*mpeer, 5) - for i := range m { - m[i] = &mpeer{Peer: *peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i))} - } - - type testCase struct { - input []*mpeer - toAdd *mpeer - expected []*mpeer - } - - tests := []testCase{ - { - input: []*mpeer{}, - toAdd: m[0], - expected: []*mpeer{m[0]}, - }, - { - input: []*mpeer{m[0]}, - toAdd: m[1], - expected: []*mpeer{m[1], m[0]}, - }, - { - input: []*mpeer{m[0], m[1]}, - toAdd: m[2], - expected: []*mpeer{m[2], m[0], m[1]}, - }, - { - input: []*mpeer{m[0], m[1], m[2], m[3]}, - toAdd: m[4], - expected: []*mpeer{m[4], m[0], m[1], m[2]}, - }, - } - - for _, test := range tests { - test.input = unshiftPeer(test.input, test.toAdd, len(m)-1) - assert.Equal(t, test.expected, test.input, "unshiftPeer") - } -} - -func TestDeletePeer(t *testing.T) { - m := make([]*mpeer, 5) - for i := range m { - m[i] = &mpeer{Peer: *peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i))} - } - - type testCase struct { - input []*mpeer - toRemove int - expected []*mpeer - deleted *mpeer - } - - tests := []testCase{ - { - input: []*mpeer{m[0]}, - toRemove: 0, - expected: []*mpeer{}, - deleted: m[0], - }, - { - input: []*mpeer{m[0], m[1], m[2], m[3]}, - toRemove: 2, - expected: []*mpeer{m[0], m[1], m[3]}, - deleted: m[2], - }, - } - - for _, test := range tests { - var deleted *mpeer - test.input, deleted = deletePeer(test.input, test.toRemove) - assert.Equal(t, test.expected, test.input, "deletePeer_list") - assert.Equal(t, test.deleted, deleted, "deletePeer_peer") - } -} - -func TestDeletePeerByID(t *testing.T) { - m := make([]*mpeer, 5) - p := make([]*peer.Peer, 5) - for i := range m { - p[i] = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - m[i] = &mpeer{Peer: *p[i]} - } - - type testCase struct { - input []*mpeer - toRemove peer.ID - expected []*mpeer - deleted *mpeer - } - - tests := []testCase{ - { - input: []*mpeer{m[0]}, - toRemove: p[0].ID(), - expected: []*mpeer{}, - deleted: m[0], - }, - { - input: []*mpeer{m[0], m[1], m[2], m[3]}, - toRemove: p[2].ID(), - expected: []*mpeer{m[0], m[1], m[3]}, - deleted: m[2], - }, - } - - for _, test := range tests { - var deleted *mpeer - test.input, deleted = deletePeerByID(test.input, test.toRemove) - assert.Equal(t, test.expected, test.input, "deletePeerByID_list") - assert.Equal(t, test.deleted, deleted, "deletePeerByID_peer") - } -} - -func TestPushPeer(t *testing.T) { - m := make([]*mpeer, 5) - max := len(m) - 1 - for i := range m { - m[i] = &mpeer{Peer: *peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i))} - } - - type testCase struct { - input []*mpeer - toPush *mpeer - expected []*mpeer - } - - tests := []testCase{ - { - input: []*mpeer{}, - toPush: m[0], - expected: []*mpeer{m[0]}, - }, - { - input: []*mpeer{m[0], m[1]}, - toPush: m[2], - expected: []*mpeer{m[0], m[1], m[2]}, - }, - { - input: []*mpeer{m[0], m[1], m[2], m[3]}, - toPush: m[4], - expected: []*mpeer{m[1], m[2], m[3], m[4]}, - }, - } - - for _, test := range tests { - test.input = pushPeer(test.input, test.toPush, max) - assert.Equal(t, test.expected, test.input, "pushPeer") - } -} diff --git a/packages/autopeering/discover/proto/message.go b/packages/autopeering/discover/proto/message.go deleted file mode 100644 index d7516862..00000000 --- a/packages/autopeering/discover/proto/message.go +++ /dev/null @@ -1,39 +0,0 @@ -package proto - -import ( - "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/server" -) - -// MType is the type of message type enum. -type MType = server.MType - -// An enum for the different message types. -const ( - MPing MType = 10 + iota - MPong - MDiscoveryRequest - MDiscoveryResponse -) - -// Message extends the proto.Message interface with additional util functions. -type Message interface { - proto.Message - - // Name returns the name of the corresponding message type for debugging. - Name() string - // Type returns the type of the corresponding message as an enum. - Type() MType -} - -func (m *Ping) Name() string { return "PING" } -func (m *Ping) Type() MType { return MPing } - -func (m *Pong) Name() string { return "PONG" } -func (m *Pong) Type() MType { return MPong } - -func (m *DiscoveryRequest) Name() string { return "DISCOVERY_REQUEST" } -func (m *DiscoveryRequest) Type() MType { return MDiscoveryRequest } - -func (m *DiscoveryResponse) Name() string { return "DISCOVERY_RESPONSE" } -func (m *DiscoveryResponse) Type() MType { return MDiscoveryResponse } diff --git a/packages/autopeering/discover/proto/message.pb.go b/packages/autopeering/discover/proto/message.pb.go deleted file mode 100644 index f130a59e..00000000 --- a/packages/autopeering/discover/proto/message.pb.go +++ /dev/null @@ -1,280 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: discover/proto/message.proto - -package proto - -import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" - proto2 "github.com/iotaledger/goshimmer/packages/autopeering/peer/proto" - proto1 "github.com/iotaledger/goshimmer/packages/autopeering/peer/service/proto" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type Ping struct { - // protocol version number - Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - // string form of the return address (e.g. "192.0.2.1:25", "[2001:db8::1]:80") - From string `protobuf:"bytes,2,opt,name=from,proto3" json:"from,omitempty"` - // string form of the recipient address - To string `protobuf:"bytes,3,opt,name=to,proto3" json:"to,omitempty"` - // unix time - Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Ping) Reset() { *m = Ping{} } -func (m *Ping) String() string { return proto.CompactTextString(m) } -func (*Ping) ProtoMessage() {} -func (*Ping) Descriptor() ([]byte, []int) { - return fileDescriptor_43f14146485f66eb, []int{0} -} - -func (m *Ping) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Ping.Unmarshal(m, b) -} -func (m *Ping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Ping.Marshal(b, m, deterministic) -} -func (m *Ping) XXX_Merge(src proto.Message) { - xxx_messageInfo_Ping.Merge(m, src) -} -func (m *Ping) XXX_Size() int { - return xxx_messageInfo_Ping.Size(m) -} -func (m *Ping) XXX_DiscardUnknown() { - xxx_messageInfo_Ping.DiscardUnknown(m) -} - -var xxx_messageInfo_Ping proto.InternalMessageInfo - -func (m *Ping) GetVersion() uint32 { - if m != nil { - return m.Version - } - return 0 -} - -func (m *Ping) GetFrom() string { - if m != nil { - return m.From - } - return "" -} - -func (m *Ping) GetTo() string { - if m != nil { - return m.To - } - return "" -} - -func (m *Ping) GetTimestamp() int64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -type Pong struct { - // hash of the ping packet - PingHash []byte `protobuf:"bytes,1,opt,name=ping_hash,json=pingHash,proto3" json:"ping_hash,omitempty"` - // string form of the recipient address - To string `protobuf:"bytes,2,opt,name=to,proto3" json:"to,omitempty"` - // services supported by the sender - Services *proto1.ServiceMap `protobuf:"bytes,3,opt,name=services,proto3" json:"services,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Pong) Reset() { *m = Pong{} } -func (m *Pong) String() string { return proto.CompactTextString(m) } -func (*Pong) ProtoMessage() {} -func (*Pong) Descriptor() ([]byte, []int) { - return fileDescriptor_43f14146485f66eb, []int{1} -} - -func (m *Pong) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Pong.Unmarshal(m, b) -} -func (m *Pong) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Pong.Marshal(b, m, deterministic) -} -func (m *Pong) XXX_Merge(src proto.Message) { - xxx_messageInfo_Pong.Merge(m, src) -} -func (m *Pong) XXX_Size() int { - return xxx_messageInfo_Pong.Size(m) -} -func (m *Pong) XXX_DiscardUnknown() { - xxx_messageInfo_Pong.DiscardUnknown(m) -} - -var xxx_messageInfo_Pong proto.InternalMessageInfo - -func (m *Pong) GetPingHash() []byte { - if m != nil { - return m.PingHash - } - return nil -} - -func (m *Pong) GetTo() string { - if m != nil { - return m.To - } - return "" -} - -func (m *Pong) GetServices() *proto1.ServiceMap { - if m != nil { - return m.Services - } - return nil -} - -type DiscoveryRequest struct { - // string form of the recipient address - To string `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` - // unix time - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DiscoveryRequest) Reset() { *m = DiscoveryRequest{} } -func (m *DiscoveryRequest) String() string { return proto.CompactTextString(m) } -func (*DiscoveryRequest) ProtoMessage() {} -func (*DiscoveryRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_43f14146485f66eb, []int{2} -} - -func (m *DiscoveryRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DiscoveryRequest.Unmarshal(m, b) -} -func (m *DiscoveryRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DiscoveryRequest.Marshal(b, m, deterministic) -} -func (m *DiscoveryRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_DiscoveryRequest.Merge(m, src) -} -func (m *DiscoveryRequest) XXX_Size() int { - return xxx_messageInfo_DiscoveryRequest.Size(m) -} -func (m *DiscoveryRequest) XXX_DiscardUnknown() { - xxx_messageInfo_DiscoveryRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_DiscoveryRequest proto.InternalMessageInfo - -func (m *DiscoveryRequest) GetTo() string { - if m != nil { - return m.To - } - return "" -} - -func (m *DiscoveryRequest) GetTimestamp() int64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -type DiscoveryResponse struct { - // hash of the corresponding request - ReqHash []byte `protobuf:"bytes,1,opt,name=req_hash,json=reqHash,proto3" json:"req_hash,omitempty"` - // list of peers - Peers []*proto2.Peer `protobuf:"bytes,2,rep,name=peers,proto3" json:"peers,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DiscoveryResponse) Reset() { *m = DiscoveryResponse{} } -func (m *DiscoveryResponse) String() string { return proto.CompactTextString(m) } -func (*DiscoveryResponse) ProtoMessage() {} -func (*DiscoveryResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_43f14146485f66eb, []int{3} -} - -func (m *DiscoveryResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DiscoveryResponse.Unmarshal(m, b) -} -func (m *DiscoveryResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DiscoveryResponse.Marshal(b, m, deterministic) -} -func (m *DiscoveryResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_DiscoveryResponse.Merge(m, src) -} -func (m *DiscoveryResponse) XXX_Size() int { - return xxx_messageInfo_DiscoveryResponse.Size(m) -} -func (m *DiscoveryResponse) XXX_DiscardUnknown() { - xxx_messageInfo_DiscoveryResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_DiscoveryResponse proto.InternalMessageInfo - -func (m *DiscoveryResponse) GetReqHash() []byte { - if m != nil { - return m.ReqHash - } - return nil -} - -func (m *DiscoveryResponse) GetPeers() []*proto2.Peer { - if m != nil { - return m.Peers - } - return nil -} - -func init() { - proto.RegisterType((*Ping)(nil), "proto.Ping") - proto.RegisterType((*Pong)(nil), "proto.Pong") - proto.RegisterType((*DiscoveryRequest)(nil), "proto.DiscoveryRequest") - proto.RegisterType((*DiscoveryResponse)(nil), "proto.DiscoveryResponse") -} - -func init() { proto.RegisterFile("discover/proto/message.proto", fileDescriptor_43f14146485f66eb) } - -var fileDescriptor_43f14146485f66eb = []byte{ - // 309 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x91, 0xc1, 0x4b, 0xc3, 0x30, - 0x14, 0xc6, 0x59, 0xd7, 0xb9, 0xf6, 0x4d, 0xc5, 0x05, 0x84, 0x3a, 0x77, 0x98, 0x3b, 0x79, 0x59, - 0x0b, 0x2a, 0x9e, 0x45, 0x3c, 0x78, 0x11, 0x66, 0xbc, 0x79, 0x91, 0xb4, 0x8b, 0x6d, 0xc0, 0x36, - 0x59, 0x93, 0x0e, 0xfc, 0xef, 0x7d, 0x4d, 0x63, 0x9d, 0xe2, 0x29, 0xef, 0x7d, 0xef, 0xf1, 0x7d, - 0xbf, 0x24, 0x30, 0xdf, 0x08, 0x9d, 0xc9, 0x1d, 0xaf, 0x13, 0x55, 0x4b, 0x23, 0x93, 0x92, 0x6b, - 0xcd, 0x72, 0x1e, 0xdb, 0x8e, 0x8c, 0xec, 0x31, 0x3b, 0x55, 0xbc, 0x5f, 0x68, 0xcb, 0x6e, 0x3a, - 0x5b, 0x58, 0x59, 0xf3, 0x7a, 0x27, 0x32, 0xee, 0xc6, 0xae, 0xeb, 0x36, 0x96, 0x29, 0xf8, 0x6b, - 0x51, 0xe5, 0x24, 0x82, 0x31, 0x46, 0x68, 0x21, 0xab, 0x68, 0xb0, 0x18, 0x5c, 0x1e, 0xd1, 0xef, - 0x96, 0x10, 0xf0, 0xdf, 0x6b, 0x59, 0x46, 0x1e, 0xca, 0x21, 0xb5, 0x35, 0x39, 0x06, 0xcf, 0xc8, - 0x68, 0x68, 0x15, 0xac, 0xc8, 0x1c, 0x42, 0x23, 0x10, 0xcc, 0xb0, 0x52, 0x45, 0x3e, 0xca, 0x43, - 0xfa, 0x23, 0xd8, 0x0c, 0x89, 0x19, 0xe7, 0x10, 0x2a, 0xcc, 0x7a, 0x2b, 0x98, 0x2e, 0x6c, 0xca, - 0x21, 0x0d, 0x5a, 0xe1, 0x11, 0x7b, 0x67, 0xe9, 0xf5, 0x96, 0x2b, 0x08, 0x1c, 0xa9, 0xb6, 0x41, - 0x93, 0xab, 0x69, 0x87, 0x1c, 0xbf, 0x74, 0xf2, 0x13, 0x53, 0xb4, 0x5f, 0x59, 0xde, 0xc1, 0xc9, - 0x83, 0x7b, 0xa7, 0x4f, 0xca, 0xb7, 0x0d, 0x46, 0x3b, 0xcb, 0xc1, 0xff, 0x94, 0xde, 0x5f, 0xca, - 0x67, 0x98, 0xee, 0x39, 0x68, 0x25, 0x2b, 0xcd, 0xc9, 0x19, 0x04, 0x35, 0xdf, 0xee, 0x13, 0x8f, - 0xb1, 0xb7, 0xc0, 0x17, 0x30, 0x6a, 0x5f, 0x57, 0xa3, 0xd3, 0x10, 0xe9, 0x26, 0x8e, 0x6e, 0x8d, - 0x1a, 0xed, 0x26, 0xf7, 0xb7, 0xaf, 0x37, 0xb9, 0x30, 0x45, 0x93, 0xc6, 0x99, 0x2c, 0x13, 0x21, - 0x0d, 0xfb, 0xe0, 0x9b, 0x1c, 0x7f, 0x84, 0x35, 0x46, 0xb6, 0x2b, 0x78, 0xf9, 0x95, 0x16, 0x65, - 0xf2, 0xfb, 0x8b, 0xd3, 0x03, 0x7b, 0x5c, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0x4d, 0xa6, 0x1f, - 0xce, 0xfb, 0x01, 0x00, 0x00, -} diff --git a/packages/autopeering/discover/proto/message.proto b/packages/autopeering/discover/proto/message.proto deleted file mode 100644 index e76db4ea..00000000 --- a/packages/autopeering/discover/proto/message.proto +++ /dev/null @@ -1,42 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/discover/proto"; - -package proto; - -import "peer/proto/peer.proto"; -import "peer/service/proto/service.proto"; - -message Ping { - // protocol version number - uint32 version = 1; - // string form of the return address (e.g. "192.0.2.1:25", "[2001:db8::1]:80") - string from = 2; - // string form of the recipient address - string to = 3; - // unix time - int64 timestamp = 4; -} - -message Pong { - // hash of the ping packet - bytes ping_hash = 1; - // string form of the recipient address - string to = 2; - // services supported by the sender - ServiceMap services = 3; -} - -message DiscoveryRequest { - // string form of the recipient address - string to = 1; - // unix time - int64 timestamp = 2; -} - -message DiscoveryResponse { - // hash of the corresponding request - bytes req_hash = 1; - // list of peers - repeated Peer peers = 2; -} diff --git a/packages/autopeering/discover/protocol.go b/packages/autopeering/discover/protocol.go deleted file mode 100644 index 4b4ccb2e..00000000 --- a/packages/autopeering/discover/protocol.go +++ /dev/null @@ -1,492 +0,0 @@ -package discover - -import ( - "bytes" - "errors" - "fmt" - "sync" - "time" - - "github.com/golang/protobuf/proto" - pb "github.com/iotaledger/goshimmer/packages/autopeering/discover/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - peerpb "github.com/iotaledger/goshimmer/packages/autopeering/peer/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - "github.com/iotaledger/goshimmer/packages/autopeering/server" - "github.com/iotaledger/hive.go/backoff" - "github.com/iotaledger/hive.go/logger" - "github.com/iotaledger/hive.go/typeutils" -) - -const ( - maxRetries = 2 - logSends = true -) - -// policy for retrying failed network calls -var retryPolicy = backoff.ExponentialBackOff(500*time.Millisecond, 1.5).With( - backoff.Jitter(0.5), backoff.MaxRetries(maxRetries)) - -// The Protocol handles the peer discovery. -// It responds to incoming messages and sends own requests when needed. -type Protocol struct { - server.Protocol - - loc *peer.Local // local peer that runs the protocol - log *logger.Logger // logging - - mgr *manager // the manager handles the actual peer discovery and re-verification - running *typeutils.AtomicBool - closeOnce sync.Once -} - -// New creates a new discovery protocol. -func New(local *peer.Local, cfg Config) *Protocol { - p := &Protocol{ - Protocol: server.Protocol{}, - loc: local, - log: cfg.Log, - running: typeutils.NewAtomicBool(), - } - p.mgr = newManager(p, cfg.MasterPeers, cfg.Log.Named("mgr")) - - return p -} - -// Start starts the actual peer discovery over the provided Sender. -func (p *Protocol) Start(s server.Sender) { - p.Protocol.Sender = s - p.mgr.start() - p.log.Debug("discover started") - p.running.Set() -} - -// Close finalizes the protocol. -func (p *Protocol) Close() { - p.closeOnce.Do(func() { - p.running.UnSet() - p.mgr.close() - }) -} - -// IsVerified checks whether the given peer has recently been verified a recent enough endpoint proof. -func (p *Protocol) IsVerified(id peer.ID, addr string) bool { - return time.Since(p.loc.Database().LastPong(id, addr)) < PingExpiration -} - -// EnsureVerified checks if the given peer has recently sent a Ping; -// if not, we send a Ping to trigger a verification. -func (p *Protocol) EnsureVerified(peer *peer.Peer) error { - if !p.hasVerified(peer.ID(), peer.Address()) { - if err := p.Ping(peer); err != nil { - return err - } - // Wait for them to Ping back and process our pong - time.Sleep(server.ResponseTimeout) - } - return nil -} - -// GetVerifiedPeer returns the verified peer with the given ID, or nil if no such peer exists. -func (p *Protocol) GetVerifiedPeer(id peer.ID, addr string) *peer.Peer { - for _, verified := range p.mgr.getVerifiedPeers() { - if verified.ID() == id && verified.Address() == addr { - return unwrapPeer(verified) - } - } - return nil -} - -// GetVerifiedPeers returns all the currently managed peers that have been verified at least once. -func (p *Protocol) GetVerifiedPeers() []*peer.Peer { - return unwrapPeers(p.mgr.getVerifiedPeers()) -} - -// HandleMessage responds to incoming peer discovery messages. -func (p *Protocol) HandleMessage(s *server.Server, fromAddr string, fromID peer.ID, fromKey peer.PublicKey, data []byte) (bool, error) { - if !p.running.IsSet() { - return false, nil - } - - switch pb.MType(data[0]) { - // Ping - case pb.MPing: - m := new(pb.Ping) - if err := proto.Unmarshal(data[1:], m); err != nil { - return true, fmt.Errorf("invalid message: %w", err) - } - if p.validatePing(fromAddr, m) { - p.handlePing(s, fromAddr, fromID, fromKey, data) - } - - // Pong - case pb.MPong: - m := new(pb.Pong) - if err := proto.Unmarshal(data[1:], m); err != nil { - return true, fmt.Errorf("invalid message: %w", err) - } - if p.validatePong(s, fromAddr, fromID, m) { - p.handlePong(fromAddr, fromID, fromKey, m) - } - - // DiscoveryRequest - case pb.MDiscoveryRequest: - m := new(pb.DiscoveryRequest) - if err := proto.Unmarshal(data[1:], m); err != nil { - return true, fmt.Errorf("invalid message: %w", err) - } - if p.validateDiscoveryRequest(fromAddr, fromID, m) { - p.handleDiscoveryRequest(s, fromAddr, data) - } - - // DiscoveryResponse - case pb.MDiscoveryResponse: - m := new(pb.DiscoveryResponse) - if err := proto.Unmarshal(data[1:], m); err != nil { - return true, fmt.Errorf("invalid message: %w", err) - } - p.validateDiscoveryResponse(s, fromAddr, fromID, m) - // DiscoveryResponse messages are handled in the handleReply function of the validation - - default: - return false, nil - } - - return true, nil -} - -// local returns the associated local peer of the neighbor selection. -func (p *Protocol) local() *peer.Local { - return p.loc -} - -// publicAddr returns the public address of the peering service in string representation. -func (p *Protocol) publicAddr() string { - return p.loc.Services().Get(service.PeeringKey).String() -} - -// ------ message senders ------ - -// Ping sends a Ping to the specified peer and blocks until a reply is received or timeout. -func (p *Protocol) Ping(to *peer.Peer) error { - return backoff.Retry(retryPolicy, func() error { - err := <-p.sendPing(to.Address(), to.ID()) - if err != nil && !errors.Is(err, server.ErrTimeout) { - return backoff.Permanent(err) - } - return err - }) -} - -// sendPing sends a Ping to the specified address and expects a matching reply. -// This method is non-blocking, but it returns a channel that can be used to query potential errors. -func (p *Protocol) sendPing(toAddr string, toID peer.ID) <-chan error { - ping := newPing(p.publicAddr(), toAddr) - data := marshal(ping) - - // compute the message hash - hash := server.PacketHash(data) - hashEqual := func(m interface{}) bool { - return bytes.Equal(m.(*pb.Pong).GetPingHash(), hash) - } - - p.logSend(toAddr, ping) - return p.Protocol.SendExpectingReply(toAddr, toID, data, pb.MPong, hashEqual) -} - -// DiscoveryRequest request known peers from the given target. This method blocks -// until a response is received and the provided peers are returned. -func (p *Protocol) DiscoveryRequest(to *peer.Peer) ([]*peer.Peer, error) { - if err := p.EnsureVerified(to); err != nil { - return nil, err - } - - req := newDiscoveryRequest(to.Address()) - data := marshal(req) - - // compute the message hash - hash := server.PacketHash(data) - - peers := make([]*peer.Peer, 0, MaxPeersInResponse) - callback := func(m interface{}) bool { - res := m.(*pb.DiscoveryResponse) - if !bytes.Equal(res.GetReqHash(), hash) { - return false - } - - peers = peers[:0] - for _, protoPeer := range res.GetPeers() { - if p, _ := peer.FromProto(protoPeer); p != nil { - peers = append(peers, p) - } - } - return true - } - - err := backoff.Retry(retryPolicy, func() error { - p.logSend(to.Address(), req) - err := <-p.Protocol.SendExpectingReply(to.Address(), to.ID(), data, pb.MDiscoveryResponse, callback) - if err != nil && !errors.Is(err, server.ErrTimeout) { - return backoff.Permanent(err) - } - return err - }) - return peers, err -} - -// ------ helper functions ------ - -// hasVerified returns whether the given peer has recently verified the local peer. -func (p *Protocol) hasVerified(id peer.ID, addr string) bool { - return time.Since(p.loc.Database().LastPing(id, addr)) < PingExpiration -} - -func (p *Protocol) logSend(toAddr string, msg pb.Message) { - if logSends { - p.log.Debugw("send message", "type", msg.Name(), "addr", toAddr) - } -} - -func marshal(msg pb.Message) []byte { - mType := msg.Type() - if mType > 0xFF { - panic("invalid message") - } - - data, err := proto.Marshal(msg) - if err != nil { - panic("invalid message") - } - return append([]byte{byte(mType)}, data...) -} - -// newPeer creates a new peer that only has a peering service at the given address. -func newPeer(key peer.PublicKey, network string, address string) *peer.Peer { - services := service.New() - services.Update(service.PeeringKey, network, address) - - return peer.NewPeer(key, services) -} - -// ------ Message Constructors ------ - -func newPing(fromAddr string, toAddr string) *pb.Ping { - return &pb.Ping{ - Version: VersionNum, - From: fromAddr, - To: toAddr, - Timestamp: time.Now().Unix(), - } -} - -func newPong(toAddr string, reqData []byte, services *service.Record) *pb.Pong { - return &pb.Pong{ - PingHash: server.PacketHash(reqData), - To: toAddr, - Services: services.ToProto(), - } -} - -func newDiscoveryRequest(toAddr string) *pb.DiscoveryRequest { - return &pb.DiscoveryRequest{ - To: toAddr, - Timestamp: time.Now().Unix(), - } -} - -func newDiscoveryResponse(reqData []byte, list []*peer.Peer) *pb.DiscoveryResponse { - peers := make([]*peerpb.Peer, 0, len(list)) - for _, p := range list { - peers = append(peers, p.ToProto()) - } - return &pb.DiscoveryResponse{ - ReqHash: server.PacketHash(reqData), - Peers: peers, - } -} - -// ------ Message Handlers ------ - -func (p *Protocol) validatePing(fromAddr string, m *pb.Ping) bool { - // check version number - if m.GetVersion() != VersionNum { - p.log.Debugw("invalid message", - "type", m.Name(), - "version", m.GetVersion(), - "want", VersionNum, - ) - return false - } - // check that From matches the package sender address - if m.GetFrom() != fromAddr { - p.log.Debugw("invalid message", - "type", m.Name(), - "from", m.GetFrom(), - "want", fromAddr, - ) - return false - } - // check that To matches the local address - if m.GetTo() != p.publicAddr() { - p.log.Debugw("invalid message", - "type", m.Name(), - "to", m.GetTo(), - "want", p.publicAddr(), - ) - return false - } - // check Timestamp - if p.Protocol.IsExpired(m.GetTimestamp()) { - p.log.Debugw("invalid message", - "type", m.Name(), - "timestamp", time.Unix(m.GetTimestamp(), 0), - ) - return false - } - - p.log.Debugw("valid message", - "type", m.Name(), - "addr", fromAddr, - ) - return true -} - -func (p *Protocol) handlePing(s *server.Server, fromAddr string, fromID peer.ID, fromKey peer.PublicKey, rawData []byte) { - // create and send the pong response - pong := newPong(fromAddr, rawData, p.loc.Services().CreateRecord()) - - p.logSend(fromAddr, pong) - s.Send(fromAddr, marshal(pong)) - - // if the peer is new or expired, send a Ping to verify - if !p.IsVerified(fromID, fromAddr) { - p.sendPing(fromAddr, fromID) - } else if !p.mgr.isKnown(fromID) { // add a discovered peer to the manager if it is new - p.mgr.addDiscoveredPeer(newPeer(fromKey, s.LocalAddr().Network(), fromAddr)) - } - - _ = p.loc.Database().UpdateLastPing(fromID, fromAddr, time.Now()) -} - -func (p *Protocol) validatePong(s *server.Server, fromAddr string, fromID peer.ID, m *pb.Pong) bool { - // check that To matches the local address - if m.GetTo() != p.publicAddr() { - p.log.Debugw("invalid message", - "type", m.Name(), - "to", m.GetTo(), - "want", p.publicAddr(), - ) - return false - } - // there must be a Ping waiting for this pong as a reply - if !s.IsExpectedReply(fromAddr, fromID, m.Type(), m) { - p.log.Debugw("invalid message", - "type", m.Name(), - "unexpected", fromAddr, - ) - return false - } - // there must a valid number of services - numServices := len(m.GetServices().GetMap()) - if numServices <= 0 || numServices > MaxServices { - p.log.Debugw("invalid message", - "type", m.Name(), - "#peers", numServices, - ) - return false - } - - p.log.Debugw("valid message", - "type", m.Name(), - "addr", fromAddr, - ) - return true -} - -func (p *Protocol) handlePong(fromAddr string, fromID peer.ID, fromKey peer.PublicKey, m *pb.Pong) { - services, _ := service.FromProto(m.GetServices()) - peering := services.Get(service.PeeringKey) - if peering == nil || peering.String() != fromAddr { - p.log.Warn("invalid services") - return - } - - // create a proper key with these services - from := peer.NewPeer(fromKey, services) - - // a valid pong verifies the peer - p.mgr.addVerifiedPeer(from) - - // update peer database - db := p.loc.Database() - _ = db.UpdateLastPong(fromID, fromAddr, time.Now()) - _ = db.UpdatePeer(from) -} - -func (p *Protocol) validateDiscoveryRequest(fromAddr string, fromID peer.ID, m *pb.DiscoveryRequest) bool { - // check that To matches the local address - if m.GetTo() != p.publicAddr() { - p.log.Debugw("invalid message", - "type", m.Name(), - "to", m.GetTo(), - "want", p.publicAddr(), - ) - return false - } - // check Timestamp - if p.Protocol.IsExpired(m.GetTimestamp()) { - p.log.Debugw("invalid message", - "type", m.Name(), - "timestamp", time.Unix(m.GetTimestamp(), 0), - ) - return false - } - // check whether the sender is verified - if !p.IsVerified(fromID, fromAddr) { - p.log.Debugw("invalid message", - "type", m.Name(), - "unverified", fromAddr, - ) - return false - } - - p.log.Debugw("valid message", - "type", m.Name(), - "addr", fromAddr, - ) - return true -} - -func (p *Protocol) handleDiscoveryRequest(s *server.Server, fromAddr string, rawData []byte) { - // get a random list of verified peers - peers := p.mgr.getRandomPeers(MaxPeersInResponse, 1) - res := newDiscoveryResponse(rawData, peers) - - p.logSend(fromAddr, res) - s.Send(fromAddr, marshal(res)) -} - -func (p *Protocol) validateDiscoveryResponse(s *server.Server, fromAddr string, fromID peer.ID, m *pb.DiscoveryResponse) bool { - // there must not be too many peers - if len(m.GetPeers()) > MaxPeersInResponse { - p.log.Debugw("invalid message", - "type", m.Name(), - "#peers", len(m.GetPeers()), - ) - return false - } - // there must be a request waiting for this response - if !s.IsExpectedReply(fromAddr, fromID, m.Type(), m) { - p.log.Debugw("invalid message", - "type", m.Name(), - "unexpected", fromAddr, - ) - return false - } - - p.log.Debugw("valid message", - "type", m.Name(), - "addr", fromAddr, - ) - return true -} diff --git a/packages/autopeering/discover/protocol_test.go b/packages/autopeering/discover/protocol_test.go deleted file mode 100644 index 2a584280..00000000 --- a/packages/autopeering/discover/protocol_test.go +++ /dev/null @@ -1,293 +0,0 @@ -package discover - -import ( - "testing" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/peertest" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - "github.com/iotaledger/goshimmer/packages/autopeering/server" - "github.com/iotaledger/goshimmer/packages/autopeering/transport" - "github.com/iotaledger/goshimmer/packages/database/mapdb" - "github.com/iotaledger/hive.go/logger" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "go.uber.org/zap" -) - -const ( - testNetwork = "test" - testAddress = "test" - graceTime = 100 * time.Millisecond -) - -var log = logger.NewExampleLogger("discover") - -func init() { - // decrease parameters to simplify and speed up tests - SetParameter(Parameters{ - ReverifyInterval: 500 * time.Millisecond, - QueryInterval: 1000 * time.Millisecond, - MaxManaged: 10, - MaxReplacements: 2, - }) -} - -func TestProtVerifyMaster(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - - peerA := getPeer(protA) - - // use peerA as masters peer - protB, closeB := newTestProtocol(p2p.B, log, peerA) - - time.Sleep(graceTime) // wait for the packages to ripple through the network - closeB() // close srvB to avoid race conditions, when asserting - - if assert.EqualValues(t, 1, len(protB.mgr.active)) { - assert.EqualValues(t, peerA, &protB.mgr.active[0].Peer) - assert.EqualValues(t, 1, protB.mgr.active[0].verifiedCount) - } -} - -func TestProtPingPong(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - protB, closeB := newTestProtocol(p2p.B, log) - defer closeB() - - peerA := getPeer(protA) - peerB := getPeer(protB) - - // send a Ping from node A to B - t.Run("A->B", func(t *testing.T) { assert.NoError(t, protA.Ping(peerB)) }) - time.Sleep(graceTime) - - // send a Ping from node B to A - t.Run("B->A", func(t *testing.T) { assert.NoError(t, protB.Ping(peerA)) }) - time.Sleep(graceTime) -} - -func TestProtPingTimeout(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - protB, closeB := newTestProtocol(p2p.B, log) - closeB() // close the connection right away to prevent any replies - - // send a Ping from node A to B - err := protA.Ping(getPeer(protB)) - assert.EqualError(t, err, server.ErrTimeout.Error()) -} - -func TestProtVerifiedPeers(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - protB, closeB := newTestProtocol(p2p.B, log) - defer closeB() - - peerB := getPeer(protB) - - // send a Ping from node A to B - assert.NoError(t, protA.Ping(peerB)) - time.Sleep(graceTime) - - // protA should have peerB as the single verified peer - assert.ElementsMatch(t, []*peer.Peer{peerB}, protA.GetVerifiedPeers()) - for _, p := range protA.GetVerifiedPeers() { - assert.Equal(t, p, protA.GetVerifiedPeer(p.ID(), p.Address())) - } -} - -func TestProtVerifiedPeer(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - protB, closeB := newTestProtocol(p2p.B, log) - defer closeB() - - peerA := getPeer(protA) - peerB := getPeer(protB) - - // send a Ping from node A to B - assert.NoError(t, protA.Ping(peerB)) - time.Sleep(graceTime) - - // we should have peerB as a verified peer - assert.Equal(t, peerB, protA.GetVerifiedPeer(peerB.ID(), peerB.Address())) - // we should not have ourselves as a verified peer - assert.Nil(t, protA.GetVerifiedPeer(peerA.ID(), peerA.Address())) - // the address of peerB should match - assert.Nil(t, protA.GetVerifiedPeer(peerB.ID(), "")) -} - -func TestProtDiscoveryRequest(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - protB, closeB := newTestProtocol(p2p.B, log) - defer closeB() - - peerA := getPeer(protA) - peerB := getPeer(protB) - - // request peers from node A - t.Run("A->B", func(t *testing.T) { - if ps, err := protA.DiscoveryRequest(peerB); assert.NoError(t, err) { - assert.ElementsMatch(t, []*peer.Peer{peerA}, ps) - } - }) - // request peers from node B - t.Run("B->A", func(t *testing.T) { - if ps, err := protB.DiscoveryRequest(peerA); assert.NoError(t, err) { - assert.ElementsMatch(t, []*peer.Peer{peerB}, ps) - } - }) -} - -func TestProtServices(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - - err := protA.local().UpdateService(service.FPCKey, "fpc", p2p.A.LocalAddr().String()) - require.NoError(t, err) - - peerA := getPeer(protA) - - // use peerA as masters peer - protB, closeB := newTestProtocol(p2p.B, log, peerA) - defer closeB() - - time.Sleep(graceTime) // wait for the packages to ripple through the network - ps := protB.GetVerifiedPeers() - - if assert.ElementsMatch(t, []*peer.Peer{peerA}, ps) { - assert.Equal(t, protA.local().Services(), ps[0].Services()) - } -} - -func TestProtDiscovery(t *testing.T) { - net := transport.NewNetwork("M", "A", "B", "C") - defer net.Close() - - protM, closeM := newTestProtocol(net.GetTransport("M"), log) - defer closeM() - time.Sleep(graceTime) // wait for the master to initialize - - protA, closeA := newTestProtocol(net.GetTransport("A"), log, getPeer(protM)) - defer closeA() - protB, closeB := newTestProtocol(net.GetTransport("B"), log, getPeer(protM)) - defer closeB() - protC, closeC := newTestProtocol(net.GetTransport("C"), log, getPeer(protM)) - defer closeC() - - time.Sleep(queryInterval + graceTime) // wait for the next discovery cycle - time.Sleep(reverifyInterval + graceTime) // wait for the next verification cycle - - // now the full network should be discovered - assert.ElementsMatch(t, []*peer.Peer{getPeer(protA), getPeer(protB), getPeer(protC)}, protM.GetVerifiedPeers()) - assert.ElementsMatch(t, []*peer.Peer{getPeer(protM), getPeer(protB), getPeer(protC)}, protA.GetVerifiedPeers()) - assert.ElementsMatch(t, []*peer.Peer{getPeer(protM), getPeer(protA), getPeer(protC)}, protB.GetVerifiedPeers()) - assert.ElementsMatch(t, []*peer.Peer{getPeer(protM), getPeer(protA), getPeer(protB)}, protC.GetVerifiedPeers()) -} - -func BenchmarkPingPong(b *testing.B) { - p2p := transport.P2P() - defer p2p.Close() - log := zap.NewNop().Sugar() // disable logging - - // disable query/reverify - reverifyInterval = time.Hour - queryInterval = time.Hour - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - protB, closeB := newTestProtocol(p2p.B, log) - defer closeB() - - peerB := getPeer(protB) - - // send initial Ping to ensure that every peer is verified - err := protA.Ping(peerB) - require.NoError(b, err) - time.Sleep(graceTime) - - b.ResetTimer() - for n := 0; n < b.N; n++ { - // send a Ping from node A to B - _ = protA.Ping(peerB) - } - - b.StopTimer() -} - -func BenchmarkDiscoveryRequest(b *testing.B) { - p2p := transport.P2P() - defer p2p.Close() - log := zap.NewNop().Sugar() // disable logging - - // disable query/reverify - reverifyInterval = time.Hour - queryInterval = time.Hour - - protA, closeA := newTestProtocol(p2p.A, log) - defer closeA() - protB, closeB := newTestProtocol(p2p.B, log) - defer closeB() - - peerB := getPeer(protB) - - // send initial DiscoveryRequest to ensure that every peer is verified - _, err := protA.DiscoveryRequest(peerB) - require.NoError(b, err) - time.Sleep(graceTime) - - b.ResetTimer() - for n := 0; n < b.N; n++ { - _, _ = protA.DiscoveryRequest(peerB) - } - - b.StopTimer() -} - -// newTestProtocol creates a new discovery server and also returns the teardown. -func newTestProtocol(trans transport.Transport, logger *logger.Logger, masters ...*peer.Peer) (*Protocol, func()) { - db, _ := peer.NewDB(mapdb.NewMapDB()) - local := peertest.NewLocal(trans.LocalAddr().Network(), trans.LocalAddr().String(), db) - log := logger.Named(trans.LocalAddr().String()) - - prot := New(local, Config{Log: log, MasterPeers: masters}) - - srv := server.Serve(local, trans, log, prot) - prot.Start(srv) - - teardown := func() { - srv.Close() - prot.Close() - } - return prot, teardown -} - -func getPeer(p *Protocol) *peer.Peer { - return &p.local().Peer -} diff --git a/packages/autopeering/discover/query_strat.go b/packages/autopeering/discover/query_strat.go deleted file mode 100644 index 0f70a6ba..00000000 --- a/packages/autopeering/discover/query_strat.go +++ /dev/null @@ -1,94 +0,0 @@ -package discover - -import ( - "container/ring" - "math/rand" - "sync" - "time" -) - -// doQuery is the main method of the query strategy. -// It writes the next time this function should be called by the manager to next. -// The current strategy is to always select the latest verified peer and one of -// the peers that returned the most number of peers the last time it was queried. -func (m *manager) doQuery(next chan<- time.Duration) { - defer func() { next <- queryInterval }() - - ps := m.peersToQuery() - if len(ps) == 0 { - return - } - m.log.Debugw("querying", - "#peers", len(ps), - ) - - // request from peers in parallel - var wg sync.WaitGroup - wg.Add(len(ps)) - for _, p := range ps { - go m.requestWorker(p, &wg) - } - wg.Wait() -} - -func (m *manager) requestWorker(p *mpeer, wg *sync.WaitGroup) { - defer wg.Done() - - peers, err := m.net.DiscoveryRequest(unwrapPeer(p)) - if err != nil || len(peers) == 0 { - p.lastNewPeers = 0 - - m.log.Debugw("query failed", - "id", p.ID(), - "addr", p.Address(), - "err", err, - ) - return - } - - var added uint - for _, rp := range peers { - if m.addDiscoveredPeer(rp) { - added++ - } - } - p.lastNewPeers = added - - m.log.Debugw("queried", - "id", p.ID(), - "addr", p.Address(), - "#added", added, - ) -} - -// peersToQuery selects the peers that should be queried. -func (m *manager) peersToQuery() []*mpeer { - ps := m.getVerifiedPeers() - if len(ps) == 0 { - return nil - } - - latest := ps[0] - if len(ps) == 1 { - return []*mpeer{latest} - } - - // find the 3 heaviest peers - r := ring.New(3) - for i, p := range ps { - if i == 0 { - continue // the latest peer is already included - } - if r.Value == nil { - r.Value = p - } else if p.lastNewPeers >= r.Value.(*mpeer).lastNewPeers { - r = r.Next() - r.Value = p - } - } - - // select a random peer from the heaviest ones - r.Move(rand.Intn(r.Len())) - - return []*mpeer{latest, r.Value.(*mpeer)} -} diff --git a/packages/autopeering/distance/consts.go b/packages/autopeering/distance/consts.go deleted file mode 100644 index b51f994b..00000000 --- a/packages/autopeering/distance/consts.go +++ /dev/null @@ -1,5 +0,0 @@ -package distance - -const ( - Max = 4294967295 -) diff --git a/packages/autopeering/distance/distance.go b/packages/autopeering/distance/distance.go deleted file mode 100644 index 6a943b17..00000000 --- a/packages/autopeering/distance/distance.go +++ /dev/null @@ -1,33 +0,0 @@ -package distance - -import ( - "crypto/sha256" - "encoding/binary" -) - -// BySalt returns the distance (uint32) between x and y -// by xoring the hash of x and y + salt -// xor(hash(x), hash(y+salt))[:4] -func BySalt(x, y, salt []byte) uint32 { - return xorSHA32(x, joinBytes(y, salt)) -} - -func joinBytes(a, b []byte) (out []byte) { - out = make([]byte, len(a)+len(b)) - copy(out[0:], a) - copy(out[len(a):], b) - return out -} - -func xorSHA32(a, b []byte) uint32 { - return binary.LittleEndian.Uint32( - xorSHA(sha256.Sum256(a), sha256.Sum256(b))[:4]) -} - -func xorSHA(a, b [sha256.Size]byte) (out []byte) { - out = make([]byte, sha256.Size) - for i := 0; i < sha256.Size; i++ { - out[i] = a[i] ^ b[i] - } - return out -} diff --git a/packages/autopeering/distance/distance_test.go b/packages/autopeering/distance/distance_test.go deleted file mode 100644 index 1f922748..00000000 --- a/packages/autopeering/distance/distance_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package distance - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestBySalt(t *testing.T) { - type testCase struct { - x []byte - y []byte - salt []byte - zeroDistance bool - } - - tests := []testCase{ - { - x: []byte("X"), - y: []byte("Y"), - salt: []byte("salt"), - zeroDistance: false, - }, - { - x: []byte("X"), - y: []byte("X"), - salt: []byte{}, - zeroDistance: true, - }, - } - - for _, test := range tests { - d := BySalt(test.x, test.y, test.salt) - got := d == 0 - assert.Equal(t, test.zeroDistance, got, "Zero Distance") - } - -} diff --git a/packages/autopeering/peer/id.go b/packages/autopeering/peer/id.go deleted file mode 100644 index 7365c433..00000000 --- a/packages/autopeering/peer/id.go +++ /dev/null @@ -1,40 +0,0 @@ -package peer - -import ( - "crypto/sha256" - "encoding/hex" - "fmt" - "strings" -) - -// ID is a unique identifier for each peer. -type ID [sha256.Size]byte - -// Bytes returns the byte slice representation of the ID -func (id ID) Bytes() []byte { - return id[:] -} - -// String returns a shortened version of the ID as a hex encoded string. -func (id ID) String() string { - return hex.EncodeToString(id[:8]) -} - -// ParseID parses a hex encoded ID. -func ParseID(s string) (ID, error) { - var id ID - b, err := hex.DecodeString(strings.TrimPrefix(s, "0x")) - if err != nil { - return id, err - } - if len(b) != len(ID{}) { - return id, fmt.Errorf("invalid length: need %d hex chars", hex.EncodedLen(len(ID{}))) - } - copy(id[:], b) - return id, nil -} - -// ID computes the ID corresponding to the given public key. -func (k PublicKey) ID() ID { - return sha256.Sum256(k) -} diff --git a/packages/autopeering/peer/local.go b/packages/autopeering/peer/local.go deleted file mode 100644 index e4a8861e..00000000 --- a/packages/autopeering/peer/local.go +++ /dev/null @@ -1,126 +0,0 @@ -package peer - -import ( - "crypto/ed25519" - "fmt" - "sync" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" -) - -// Local defines the struct of a local peer -type Local struct { - Peer - key PrivateKey - db *DB - - // everything below is protected by a lock - mu sync.RWMutex - serviceRecord *service.Record - publicSalt *salt.Salt - privateSalt *salt.Salt -} - -// PrivateKey is the type of Ed25519 private keys used for the local peer. -type PrivateKey ed25519.PrivateKey - -// Public returns the PublicKey corresponding to priv. -func (priv PrivateKey) Public() PublicKey { - publicKey := ed25519.PrivateKey(priv).Public() - return PublicKey(publicKey.(ed25519.PublicKey)) -} - -// newLocal creates a new local peer. -func newLocal(key PrivateKey, serviceRecord *service.Record, db *DB) *Local { - return &Local{ - Peer: *NewPeer(key.Public(), serviceRecord), - key: key, - db: db, - serviceRecord: serviceRecord, - } -} - -// NewLocal creates a new local peer linked to the provided db. -// If an optional seed is provided, the seed is used to generate the private key. Without a seed, -// the provided key is loaded from the provided database and generated if not stored there. -func NewLocal(serviceRecord *service.Record, db *DB, seed ...[]byte) (*Local, error) { - var key PrivateKey - if len(seed) > 0 { - key = PrivateKey(ed25519.NewKeyFromSeed(seed[0])) - if db != nil { - if err := db.UpdateLocalPrivateKey(key); err != nil { - return nil, err - } - } - } else { - var err error - key, err = db.LocalPrivateKey() - if err != nil { - return nil, err - } - } - - if l := len(key); l != ed25519.PrivateKeySize { - return nil, fmt.Errorf("invalid key length: %d, need %d", l, ed25519.PrivateKeySize) - } - return newLocal(key, serviceRecord, db), nil -} - -// Database returns the node database associated with the local peer. -func (l *Local) Database() *DB { - return l.db -} - -// Sign signs the message with the local peer's private key and returns a signature. -func (l *Local) Sign(message []byte) []byte { - return ed25519.Sign(ed25519.PrivateKey(l.key), message) -} - -// UpdateService updates the endpoint address of the given local service. -func (l *Local) UpdateService(key service.Key, network string, address string) error { - l.mu.Lock() - defer l.mu.Unlock() - - // update the service in the read protected map - l.serviceRecord.Update(key, network, address) - - // create a new peer with the corresponding services - l.Peer = *NewPeer(l.key.Public(), l.serviceRecord) - - return nil -} - -// GetPublicSalt returns the public salt -func (l *Local) GetPublicSalt() *salt.Salt { - l.mu.RLock() - defer l.mu.RUnlock() - return l.publicSalt -} - -// SetPublicSalt sets the public salt -func (l *Local) SetPublicSalt(salt *salt.Salt) { - l.mu.Lock() - defer l.mu.Unlock() - l.publicSalt = salt -} - -// GetPrivateSalt returns the private salt -func (l *Local) GetPrivateSalt() *salt.Salt { - l.mu.RLock() - defer l.mu.RUnlock() - return l.privateSalt -} - -// SetPrivateSalt sets the private salt -func (l *Local) SetPrivateSalt(salt *salt.Salt) { - l.mu.Lock() - defer l.mu.Unlock() - l.privateSalt = salt -} - -// generatePrivateKey generates a private key that can be used for Local. -func generatePrivateKey() (PrivateKey, error) { - _, priv, err := ed25519.GenerateKey(nil) - return PrivateKey(priv), err -} diff --git a/packages/autopeering/peer/local_test.go b/packages/autopeering/peer/local_test.go deleted file mode 100644 index 5d61bfd4..00000000 --- a/packages/autopeering/peer/local_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package peer - -import ( - "crypto/ed25519" - "testing" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestID(t *testing.T) { - pub, priv, err := ed25519.GenerateKey(nil) - require.NoError(t, err) - - local := newLocal(PrivateKey(priv), newTestServiceRecord(), nil) - id := PublicKey(pub).ID() - assert.Equal(t, id, local.ID()) -} - -func TestPublicKey(t *testing.T) { - pub, priv, err := ed25519.GenerateKey(nil) - require.NoError(t, err) - - local := newLocal(PrivateKey(priv), newTestServiceRecord(), nil) - assert.EqualValues(t, pub, local.PublicKey()) -} - -func TestAddress(t *testing.T) { - local := newTestLocal(t, nil) - - address := local.Services().Get(service.PeeringKey).String() - assert.EqualValues(t, address, local.Address()) -} - -func TestPrivateSalt(t *testing.T) { - p := newTestLocal(t, nil) - - s, _ := salt.NewSalt(time.Second * 10) - p.SetPrivateSalt(s) - - got := p.GetPrivateSalt() - assert.Equal(t, s, got, "Private salt") -} - -func TestPublicSalt(t *testing.T) { - p := newTestLocal(t, nil) - - s, _ := salt.NewSalt(time.Second * 10) - p.SetPublicSalt(s) - - got := p.GetPublicSalt() - - assert.Equal(t, s, got, "Public salt") -} - -func newTestLocal(t require.TestingT, db *DB) *Local { - var priv PrivateKey - var err error - if db == nil { - priv, err = generatePrivateKey() - require.NoError(t, err) - } else { - priv, err = db.LocalPrivateKey() - require.NoError(t, err) - } - services := service.New() - services.Update(service.PeeringKey, testNetwork, testAddress) - return newLocal(priv, services, db) -} diff --git a/packages/autopeering/peer/peer.go b/packages/autopeering/peer/peer.go deleted file mode 100644 index cea34433..00000000 --- a/packages/autopeering/peer/peer.go +++ /dev/null @@ -1,143 +0,0 @@ -package peer - -import ( - "crypto/ed25519" - "encoding/base64" - "errors" - "fmt" - "net/url" - - "github.com/golang/protobuf/proto" - pb "github.com/iotaledger/goshimmer/packages/autopeering/peer/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" -) - -// Errors in the peer package. -var ( - ErrNeedsPeeringService = errors.New("needs peering service") - ErrInvalidSignature = errors.New("invalid signature") -) - -// PublicKey is the type of Ed25519 public keys used for peers. -type PublicKey ed25519.PublicKey - -// Peer defines the immutable data of a peer. -type Peer struct { - id ID // comparable node identifier - publicKey PublicKey // public key used to verify signatures - services *service.Record // unmodifiable services supported by the peer -} - -// ID returns the identifier of the peer. -func (p *Peer) ID() ID { - return p.id -} - -// PublicKey returns the public key of the peer. -func (p *Peer) PublicKey() PublicKey { - return p.publicKey -} - -// Network returns the autopeering network of the peer. -func (p *Peer) Network() string { - return p.services.Get(service.PeeringKey).Network() -} - -// Address returns the autopeering address of a peer. -func (p *Peer) Address() string { - return p.services.Get(service.PeeringKey).String() -} - -// Services returns the supported services of the peer. -func (p *Peer) Services() service.Service { - return p.services -} - -// String returns a string representation of the peer. -func (p *Peer) String() string { - u := url.URL{ - Scheme: "peer", - User: url.User(base64.StdEncoding.EncodeToString(p.PublicKey())), - Host: p.Address(), - } - return u.String() -} - -// SignedData is an interface wrapper around data with key and signature. -type SignedData interface { - GetData() []byte - GetPublicKey() []byte - GetSignature() []byte -} - -// RecoverKeyFromSignedData validates and returns the key that was used to sign the data. -func RecoverKeyFromSignedData(m SignedData) (PublicKey, error) { - return recoverKey(m.GetPublicKey(), m.GetData(), m.GetSignature()) -} - -// NewPeer creates a new unmodifiable peer. -func NewPeer(publicKey PublicKey, services service.Service) *Peer { - if services.Get(service.PeeringKey) == nil { - panic("need peering service") - } - - return &Peer{ - id: publicKey.ID(), - publicKey: publicKey, - services: services.CreateRecord(), - } -} - -// ToProto encodes a given peer into a proto buffer Peer message -func (p *Peer) ToProto() *pb.Peer { - return &pb.Peer{ - PublicKey: p.publicKey, - Services: p.services.ToProto(), - } -} - -// FromProto decodes a given proto buffer Peer message (in) and returns the corresponding Peer. -func FromProto(in *pb.Peer) (*Peer, error) { - if l := len(in.GetPublicKey()); l != ed25519.PublicKeySize { - return nil, fmt.Errorf("invalid key length: %d, need %d", l, ed25519.PublicKeySize) - } - services, err := service.FromProto(in.GetServices()) - if err != nil { - return nil, err - } - if services.Get(service.PeeringKey) == nil { - return nil, ErrNeedsPeeringService - } - - return NewPeer(in.GetPublicKey(), services), nil -} - -// Marshal serializes a given Peer (p) into a slice of bytes. -func (p *Peer) Marshal() ([]byte, error) { - return proto.Marshal(p.ToProto()) -} - -// Unmarshal de-serializes a given slice of bytes (data) into a Peer. -func Unmarshal(data []byte) (*Peer, error) { - s := &pb.Peer{} - if err := proto.Unmarshal(data, s); err != nil { - return nil, err - } - return FromProto(s) -} - -func recoverKey(key, data, sig []byte) (PublicKey, error) { - if l := len(key); l != ed25519.PublicKeySize { - return nil, fmt.Errorf("%w: invalid key length: %d, need %d", ErrInvalidSignature, l, ed25519.PublicKeySize) - } - if l := len(sig); l != ed25519.SignatureSize { - return nil, fmt.Errorf("%w: invalid signature length: %d, need %d", ErrInvalidSignature, l, ed25519.SignatureSize) - } - if !ed25519.Verify(key, data, sig) { - return nil, ErrInvalidSignature - } - - publicKey := make([]byte, ed25519.PublicKeySize) - copy(publicKey, key) - return publicKey, nil -} diff --git a/packages/autopeering/peer/peer_test.go b/packages/autopeering/peer/peer_test.go deleted file mode 100644 index a3375684..00000000 --- a/packages/autopeering/peer/peer_test.go +++ /dev/null @@ -1,82 +0,0 @@ -package peer - -import ( - "crypto/ed25519" - "testing" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const ( - testNetwork = "udp" - testAddress = "127.0.0.1:8000" - testMessage = "Hello World!" -) - -func newTestServiceRecord() *service.Record { - services := service.New() - services.Update(service.PeeringKey, testNetwork, testAddress) - - return services -} - -func newTestPeer(name string) *Peer { - key := make([]byte, ed25519.PublicKeySize) - copy(key, name) - return NewPeer(key, newTestServiceRecord()) -} - -func TestNoServicePeer(t *testing.T) { - key := make([]byte, ed25519.PublicKeySize) - services := service.New() - - assert.Panics(t, func() { - _ = NewPeer(key, services) - }) -} - -func TestInvalidServicePeer(t *testing.T) { - key := make([]byte, ed25519.PublicKeySize) - services := service.New() - services.Update(service.FPCKey, "network", "address") - - assert.Panics(t, func() { - _ = NewPeer(key, services) - }) -} - -func TestMarshalUnmarshal(t *testing.T) { - p := newTestPeer("test") - data, err := p.Marshal() - require.NoError(t, err) - - got, err := Unmarshal(data) - require.NoError(t, err) - - assert.Equal(t, p, got) -} - -func TestRecoverKeyFromSignedData(t *testing.T) { - msg := []byte(testMessage) - - pub, priv, err := ed25519.GenerateKey(nil) - require.NoError(t, err) - - sig := ed25519.Sign(priv, msg) - - d := signedData{pub: pub, msg: msg, sig: sig} - key, err := RecoverKeyFromSignedData(d) - require.NoError(t, err) - - assert.Equal(t, PublicKey(pub).ID(), key.ID()) -} - -type signedData struct { - pub, msg, sig []byte -} - -func (d signedData) GetPublicKey() []byte { return d.pub } -func (d signedData) GetData() []byte { return d.msg } -func (d signedData) GetSignature() []byte { return d.sig } diff --git a/packages/autopeering/peer/peerdb.go b/packages/autopeering/peer/peerdb.go deleted file mode 100644 index d0972c77..00000000 --- a/packages/autopeering/peer/peerdb.go +++ /dev/null @@ -1,246 +0,0 @@ -package peer - -import ( - "bytes" - "encoding/binary" - "math/rand" - "time" - - "github.com/iotaledger/goshimmer/packages/database" -) - -const ( - // remove peers from DB, when the last received ping was older than this - peerExpiration = 24 * time.Hour - // interval in which expired peers are checked - cleanupInterval = time.Hour - - // number of peers used for bootstrapping - seedCount = 10 - // time after which potential seed peers should expire - seedExpiration = 5 * 24 * time.Hour -) - -// Database defines the database functionality required by DB. -type Database interface { - Get(database.Key) (database.Entry, error) - Set(database.Entry) error - ForEachPrefix(database.KeyPrefix, func(database.Entry) bool) error -} - -// DB is the peer database, storing previously seen peers and any collected properties of them. -type DB struct { - db Database -} - -// Keys in the node database. -const ( - dbNodePrefix = "n:" // Identifier to prefix node entries with - dbLocalPrefix = "local:" // Identifier to prefix local entries - - // These fields are stored per ID and address. Use nodeFieldKey to create those keys. - dbNodePing = "lastping" - dbNodePong = "lastpong" - - // Local information is keyed by ID only. Use localFieldKey to create those keys. - dbLocalKey = "key" -) - -// NewDB creates a new peer database. -func NewDB(db Database) (*DB, error) { - pDB := &DB{ - db: db, - } - err := pDB.init() - if err != nil { - return nil, err - } - return pDB, nil -} - -func (db *DB) init() error { - // get all peers in the DB - peers := db.getPeers(0) - - for _, p := range peers { - // if they dont have an associated pong, give them a grace period - if db.LastPong(p.ID(), p.Address()).Unix() == 0 { - if err := db.setPeerWithTTL(p, cleanupInterval); err != nil { - return err - } - } - } - return nil -} - -// nodeKey returns the database key for a node record. -func nodeKey(id ID) []byte { - return append([]byte(dbNodePrefix), id.Bytes()...) -} - -// nodeFieldKey returns the database key for a node metadata field. -func nodeFieldKey(id ID, address string, field string) []byte { - return bytes.Join([][]byte{nodeKey(id), []byte(address), []byte(field)}, []byte{':'}) -} - -func localFieldKey(field string) []byte { - return append([]byte(dbLocalPrefix), []byte(field)...) -} - -func parseInt64(blob []byte) int64 { - val, read := binary.Varint(blob) - if read <= 0 { - return 0 - } - return val -} - -// getInt64 retrieves an integer associated with a particular key. -func (db *DB) getInt64(key []byte) int64 { - entry, err := db.db.Get(key) - if err != nil { - return 0 - } - return parseInt64(entry.Value) -} - -// setInt64 stores an integer in the given key. -func (db *DB) setInt64(key []byte, n int64) error { - blob := make([]byte, binary.MaxVarintLen64) - blob = blob[:binary.PutVarint(blob, n)] - return db.db.Set(database.Entry{Key: key, Value: blob, TTL: peerExpiration}) -} - -// LocalPrivateKey returns the private key stored in the database or creates a new one. -func (db *DB) LocalPrivateKey() (PrivateKey, error) { - entry, err := db.db.Get(localFieldKey(dbLocalKey)) - if err == database.ErrKeyNotFound { - key, genErr := generatePrivateKey() - if genErr == nil { - err = db.UpdateLocalPrivateKey(key) - } - return key, err - } - if err != nil { - return nil, err - } - return PrivateKey(entry.Value), nil -} - -// UpdateLocalPrivateKey stores the provided key in the database. -func (db *DB) UpdateLocalPrivateKey(key PrivateKey) error { - return db.db.Set(database.Entry{Key: localFieldKey(dbLocalKey), Value: []byte(key)}) -} - -// LastPing returns that property for the given peer ID and address. -func (db *DB) LastPing(id ID, address string) time.Time { - return time.Unix(db.getInt64(nodeFieldKey(id, address, dbNodePing)), 0) -} - -// UpdateLastPing updates that property for the given peer ID and address. -func (db *DB) UpdateLastPing(id ID, address string, t time.Time) error { - return db.setInt64(nodeFieldKey(id, address, dbNodePing), t.Unix()) -} - -// LastPong returns that property for the given peer ID and address. -func (db *DB) LastPong(id ID, address string) time.Time { - return time.Unix(db.getInt64(nodeFieldKey(id, address, dbNodePong)), 0) -} - -// UpdateLastPong updates that property for the given peer ID and address. -func (db *DB) UpdateLastPong(id ID, address string, t time.Time) error { - return db.setInt64(nodeFieldKey(id, address, dbNodePong), t.Unix()) -} - -func (db *DB) setPeerWithTTL(p *Peer, ttl time.Duration) error { - data, err := p.Marshal() - if err != nil { - return err - } - return db.db.Set(database.Entry{Key: nodeKey(p.ID()), Value: data, TTL: ttl}) -} - -// UpdatePeer updates a peer in the database. -func (db *DB) UpdatePeer(p *Peer) error { - return db.setPeerWithTTL(p, peerExpiration) -} - -func parsePeer(data []byte) *Peer { - p, err := Unmarshal(data) - if err != nil { - return nil - } - return p -} - -// Peer retrieves a peer from the database. -func (db *DB) Peer(id ID) *Peer { - data, err := db.db.Get(nodeKey(id)) - if err != nil { - return nil - } - return parsePeer(data.Value) -} - -func randomSubset(peers []*Peer, m int) []*Peer { - if len(peers) <= m { - return peers - } - - result := make([]*Peer, 0, m) - for i, p := range peers { - if rand.Intn(len(peers)-i) < m-len(result) { - result = append(result, p) - } - } - return result -} - -func (db *DB) getPeers(maxAge time.Duration) (peers []*Peer) { - now := time.Now() - err := db.db.ForEachPrefix([]byte(dbNodePrefix), func(entry database.Entry) bool { - var id ID - if len(entry.Key) != len(id) { - return false - } - copy(id[:], entry.Key) - - p := parsePeer(entry.Value) - if p == nil || p.ID() != id { - return false - } - if maxAge > 0 && now.Sub(db.LastPong(p.ID(), p.Address())) > maxAge { - return false - } - - peers = append(peers, p) - return false - }) - if err != nil { - return nil - } - return peers -} - -// SeedPeers retrieves random nodes to be used as potential bootstrap peers. -func (db *DB) SeedPeers() []*Peer { - // get all stored peers and select subset - peers := db.getPeers(0) - - return randomSubset(peers, seedCount) -} - -// PersistSeeds assures that potential bootstrap peers are not garbage collected. -// It returns the number of peers that have been persisted. -func (db *DB) PersistSeeds() int { - // randomly select potential bootstrap peers - peers := randomSubset(db.getPeers(peerExpiration), seedCount) - - for i, p := range peers { - err := db.setPeerWithTTL(p, seedExpiration) - if err != nil { - return i - } - } - return len(peers) -} diff --git a/packages/autopeering/peer/peerdb_test.go b/packages/autopeering/peer/peerdb_test.go deleted file mode 100644 index f52f45c9..00000000 --- a/packages/autopeering/peer/peerdb_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package peer - -import ( - "crypto/ed25519" - "fmt" - "testing" - "time" - - "github.com/iotaledger/goshimmer/packages/database/mapdb" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPeerDB(t *testing.T) { - db, err := NewDB(mapdb.NewMapDB()) - require.NoError(t, err) - p := newTestPeer("test") - - t.Run("LocalPrivateKey", func(t *testing.T) { - key, err := db.LocalPrivateKey() - require.NoError(t, err) - - assert.EqualValues(t, ed25519.PrivateKeySize, len(key)) - assert.EqualValues(t, ed25519.PublicKeySize, len(key.Public())) - - err = db.UpdateLocalPrivateKey(key) - require.NoError(t, err) - - key2, err := db.LocalPrivateKey() - require.NoError(t, err) - - assert.Equal(t, key, key2) - }) - - t.Run("Peer", func(t *testing.T) { - err := db.UpdatePeer(p) - require.NoError(t, err) - - assert.Equal(t, p, db.Peer(p.ID())) - }) - - t.Run("LastPing", func(t *testing.T) { - now := time.Now() - err := db.UpdateLastPing(p.ID(), p.Address(), now) - require.NoError(t, err) - - assert.Equal(t, now.Unix(), db.LastPing(p.ID(), p.Address()).Unix()) - }) - - t.Run("LastPong", func(t *testing.T) { - now := time.Now() - err := db.UpdateLastPong(p.ID(), p.Address(), now) - require.NoError(t, err) - - assert.Equal(t, now.Unix(), db.LastPong(p.ID(), p.Address()).Unix()) - }) - - t.Run("getPeers", func(t *testing.T) { - time.Sleep(time.Second) // let the old peers expire - - newPeer := newTestPeer("new") - assert.NoError(t, db.UpdatePeer(newPeer)) - assert.NoError(t, db.UpdateLastPong(newPeer.ID(), newPeer.Address(), time.Now())) - - peers := db.getPeers(time.Second) - assert.ElementsMatch(t, []*Peer{newPeer}, peers) - }) - - t.Run("SeedPeers", func(t *testing.T) { - for i := 0; i < seedCount+1; i++ { - p := newTestPeer(fmt.Sprintf("SeedPeers%0d", i)) - assert.NoError(t, db.UpdatePeer(p)) - assert.NoError(t, db.UpdateLastPong(p.ID(), p.Address(), time.Now())) - } - - peers := db.SeedPeers() - assert.EqualValues(t, seedCount, len(peers)) - }) - - t.Run("PersistSeeds", func(t *testing.T) { - for i := 0; i < seedCount+1; i++ { - p := newTestPeer(fmt.Sprintf("PersistSeeds%0d", i)) - assert.NoError(t, db.UpdatePeer(p)) - assert.NoError(t, db.UpdateLastPong(p.ID(), p.Address(), time.Now())) - } - - count := db.PersistSeeds() - assert.EqualValues(t, seedCount, count) - }) -} diff --git a/packages/autopeering/peer/peertest/peertest.go b/packages/autopeering/peer/peertest/peertest.go deleted file mode 100644 index e1c0fb8f..00000000 --- a/packages/autopeering/peer/peertest/peertest.go +++ /dev/null @@ -1,35 +0,0 @@ -// Package peertest provides utilities for writing tests with the peer package. -package peertest - -import ( - "crypto/ed25519" - "log" - "math/rand" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" -) - -func NewPeer(network, address string) *peer.Peer { - services := service.New() - services.Update(service.PeeringKey, network, address) - key := make([]byte, ed25519.PublicKeySize) - copy(key, address) - return peer.NewPeer(key, services) -} - -func NewLocal(network, address string, db *peer.DB) *peer.Local { - services := service.New() - services.Update(service.PeeringKey, network, address) - local, err := peer.NewLocal(services, db, randomSeed()) - if err != nil { - log.Panic(err) - } - return local -} - -func randomSeed() []byte { - seed := make([]byte, ed25519.SeedSize) - rand.Read(seed) - return seed -} diff --git a/packages/autopeering/peer/proto/peer.pb.go b/packages/autopeering/peer/proto/peer.pb.go deleted file mode 100644 index 471070ff..00000000 --- a/packages/autopeering/peer/proto/peer.pb.go +++ /dev/null @@ -1,94 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: peer/proto/peer.proto - -package proto - -import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" - proto1 "github.com/iotaledger/goshimmer/packages/autopeering/peer/service/proto" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Minimal encoding of a peer -type Peer struct { - // public key used for signing - PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - // services supported by the peer - Services *proto1.ServiceMap `protobuf:"bytes,2,opt,name=services,proto3" json:"services,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Peer) Reset() { *m = Peer{} } -func (m *Peer) String() string { return proto.CompactTextString(m) } -func (*Peer) ProtoMessage() {} -func (*Peer) Descriptor() ([]byte, []int) { - return fileDescriptor_155860cd2f47eba7, []int{0} -} - -func (m *Peer) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Peer.Unmarshal(m, b) -} -func (m *Peer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Peer.Marshal(b, m, deterministic) -} -func (m *Peer) XXX_Merge(src proto.Message) { - xxx_messageInfo_Peer.Merge(m, src) -} -func (m *Peer) XXX_Size() int { - return xxx_messageInfo_Peer.Size(m) -} -func (m *Peer) XXX_DiscardUnknown() { - xxx_messageInfo_Peer.DiscardUnknown(m) -} - -var xxx_messageInfo_Peer proto.InternalMessageInfo - -func (m *Peer) GetPublicKey() []byte { - if m != nil { - return m.PublicKey - } - return nil -} - -func (m *Peer) GetServices() *proto1.ServiceMap { - if m != nil { - return m.Services - } - return nil -} - -func init() { - proto.RegisterType((*Peer)(nil), "proto.Peer") -} - -func init() { proto.RegisterFile("peer/proto/peer.proto", fileDescriptor_155860cd2f47eba7) } - -var fileDescriptor_155860cd2f47eba7 = []byte{ - // 168 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x48, 0x4d, 0x2d, - 0xd2, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x07, 0x31, 0xf5, 0xc0, 0x4c, 0x21, 0x56, 0x30, 0x25, - 0xa5, 0x00, 0x96, 0x2d, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x85, 0xaa, 0x82, 0xf2, 0x20, 0x0a, - 0x95, 0x42, 0xb8, 0x58, 0x02, 0x80, 0x6a, 0x84, 0x64, 0xb9, 0xb8, 0x0a, 0x4a, 0x93, 0x72, 0x32, - 0x93, 0xe3, 0xb3, 0x53, 0x2b, 0x25, 0x18, 0x15, 0x18, 0x35, 0x78, 0x82, 0x38, 0x21, 0x22, 0xde, - 0xa9, 0x95, 0x42, 0xba, 0x5c, 0x1c, 0x50, 0x7d, 0xc5, 0x12, 0x4c, 0x40, 0x49, 0x6e, 0x23, 0x41, - 0x88, 0x01, 0x7a, 0xc1, 0x10, 0x61, 0xdf, 0xc4, 0x82, 0x20, 0xb8, 0x12, 0x27, 0xa3, 0x28, 0x83, - 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xcc, 0xfc, 0x92, 0xc4, 0x9c, - 0xd4, 0x94, 0x74, 0xa0, 0x53, 0x12, 0x4b, 0x4b, 0xf2, 0x41, 0x6e, 0xca, 0xcc, 0x4b, 0xd7, 0x2d, - 0xce, 0xcc, 0xd5, 0x47, 0xb8, 0x3e, 0x89, 0x0d, 0x4c, 0x19, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, - 0x8e, 0xb0, 0x04, 0x1f, 0xd2, 0x00, 0x00, 0x00, -} diff --git a/packages/autopeering/peer/proto/peer.proto b/packages/autopeering/peer/proto/peer.proto deleted file mode 100644 index 16327b26..00000000 --- a/packages/autopeering/peer/proto/peer.proto +++ /dev/null @@ -1,15 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/peer/proto"; - -package proto; - -import "peer/service/proto/service.proto"; - -// Minimal encoding of a peer -message Peer { - // public key used for signing - bytes public_key = 1; - // services supported by the peer - ServiceMap services = 2; -} diff --git a/packages/autopeering/peer/service/proto/service.pb.go b/packages/autopeering/peer/service/proto/service.pb.go deleted file mode 100644 index 9da648b9..00000000 --- a/packages/autopeering/peer/service/proto/service.pb.go +++ /dev/null @@ -1,137 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: peer/service/proto/service.proto - -package proto - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -// Mapping between a service ID and its tuple network_address -// e.g., map[autopeering:&{tcp, 198.51.100.1:80}] -type ServiceMap struct { - Map map[string]*NetworkAddress `protobuf:"bytes,1,rep,name=map,proto3" json:"map,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ServiceMap) Reset() { *m = ServiceMap{} } -func (m *ServiceMap) String() string { return proto.CompactTextString(m) } -func (*ServiceMap) ProtoMessage() {} -func (*ServiceMap) Descriptor() ([]byte, []int) { - return fileDescriptor_8dd4c5b37d65f758, []int{0} -} - -func (m *ServiceMap) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ServiceMap.Unmarshal(m, b) -} -func (m *ServiceMap) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ServiceMap.Marshal(b, m, deterministic) -} -func (m *ServiceMap) XXX_Merge(src proto.Message) { - xxx_messageInfo_ServiceMap.Merge(m, src) -} -func (m *ServiceMap) XXX_Size() int { - return xxx_messageInfo_ServiceMap.Size(m) -} -func (m *ServiceMap) XXX_DiscardUnknown() { - xxx_messageInfo_ServiceMap.DiscardUnknown(m) -} - -var xxx_messageInfo_ServiceMap proto.InternalMessageInfo - -func (m *ServiceMap) GetMap() map[string]*NetworkAddress { - if m != nil { - return m.Map - } - return nil -} - -// The service type (e.g., tcp, upd) and the address (e.g., 198.51.100.1:80) -type NetworkAddress struct { - Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *NetworkAddress) Reset() { *m = NetworkAddress{} } -func (m *NetworkAddress) String() string { return proto.CompactTextString(m) } -func (*NetworkAddress) ProtoMessage() {} -func (*NetworkAddress) Descriptor() ([]byte, []int) { - return fileDescriptor_8dd4c5b37d65f758, []int{1} -} - -func (m *NetworkAddress) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_NetworkAddress.Unmarshal(m, b) -} -func (m *NetworkAddress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_NetworkAddress.Marshal(b, m, deterministic) -} -func (m *NetworkAddress) XXX_Merge(src proto.Message) { - xxx_messageInfo_NetworkAddress.Merge(m, src) -} -func (m *NetworkAddress) XXX_Size() int { - return xxx_messageInfo_NetworkAddress.Size(m) -} -func (m *NetworkAddress) XXX_DiscardUnknown() { - xxx_messageInfo_NetworkAddress.DiscardUnknown(m) -} - -var xxx_messageInfo_NetworkAddress proto.InternalMessageInfo - -func (m *NetworkAddress) GetNetwork() string { - if m != nil { - return m.Network - } - return "" -} - -func (m *NetworkAddress) GetAddress() string { - if m != nil { - return m.Address - } - return "" -} - -func init() { - proto.RegisterType((*ServiceMap)(nil), "proto.ServiceMap") - proto.RegisterMapType((map[string]*NetworkAddress)(nil), "proto.ServiceMap.MapEntry") - proto.RegisterType((*NetworkAddress)(nil), "proto.NetworkAddress") -} - -func init() { proto.RegisterFile("peer/service/proto/service.proto", fileDescriptor_8dd4c5b37d65f758) } - -var fileDescriptor_8dd4c5b37d65f758 = []byte{ - // 227 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x52, 0x28, 0x48, 0x4d, 0x2d, - 0xd2, 0x2f, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x87, 0xf1, - 0xf4, 0xc0, 0x3c, 0x21, 0x56, 0x30, 0xa5, 0xd4, 0xc9, 0xc8, 0xc5, 0x15, 0x0c, 0x91, 0xf0, 0x4d, - 0x2c, 0x10, 0xd2, 0xe1, 0x62, 0xce, 0x4d, 0x2c, 0x90, 0x60, 0x54, 0x60, 0xd6, 0xe0, 0x36, 0x92, - 0x82, 0x28, 0xd5, 0x43, 0xc8, 0xeb, 0x01, 0xb1, 0x6b, 0x5e, 0x49, 0x51, 0x65, 0x10, 0x48, 0x99, - 0x94, 0x2f, 0x17, 0x07, 0x4c, 0x40, 0x48, 0x80, 0x8b, 0x39, 0x3b, 0xb5, 0x12, 0xa8, 0x93, 0x51, - 0x83, 0x33, 0x08, 0xc4, 0x14, 0xd2, 0xe6, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, 0x95, 0x60, 0x02, - 0x8a, 0x71, 0x1b, 0x89, 0x42, 0x4d, 0xf3, 0x4b, 0x2d, 0x29, 0xcf, 0x2f, 0xca, 0x76, 0x4c, 0x49, - 0x29, 0x4a, 0x2d, 0x2e, 0x0e, 0x82, 0xa8, 0xb1, 0x62, 0xb2, 0x60, 0x54, 0x72, 0xe1, 0xe2, 0x43, - 0x95, 0x14, 0x92, 0xe0, 0x62, 0xcf, 0x83, 0x88, 0x40, 0x0d, 0x86, 0x71, 0x41, 0x32, 0x89, 0x10, - 0x45, 0x60, 0xe3, 0x81, 0x32, 0x50, 0xae, 0x93, 0x55, 0x94, 0x45, 0x7a, 0x66, 0x49, 0x46, 0x69, - 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x66, 0x7e, 0x49, 0x62, 0x4e, 0x6a, 0x4a, 0x3a, 0x30, 0x34, - 0x12, 0x4b, 0x4b, 0xf2, 0x41, 0xc1, 0x92, 0x99, 0x97, 0xae, 0x5b, 0x9c, 0x99, 0xab, 0x8f, 0x19, - 0x44, 0x49, 0x6c, 0x60, 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x5f, 0xe9, 0x8e, 0x9d, 0x3f, - 0x01, 0x00, 0x00, -} diff --git a/packages/autopeering/peer/service/proto/service.proto b/packages/autopeering/peer/service/proto/service.proto deleted file mode 100644 index 28f04088..00000000 --- a/packages/autopeering/peer/service/proto/service.proto +++ /dev/null @@ -1,17 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/peer/service/proto"; - -package proto; - -// Mapping between a service ID and its tuple network_address -// e.g., map[autopeering:&{tcp, 198.51.100.1:80}] -message ServiceMap { - map<string, NetworkAddress> map = 1; -} - -// The service type (e.g., tcp, upd) and the address (e.g., 198.51.100.1:80) -message NetworkAddress { - string network = 1; - string address = 2; -} \ No newline at end of file diff --git a/packages/autopeering/peer/service/record.go b/packages/autopeering/peer/service/record.go deleted file mode 100644 index 7a6df07a..00000000 --- a/packages/autopeering/peer/service/record.go +++ /dev/null @@ -1,118 +0,0 @@ -package service - -import ( - "fmt" - "net" - - "github.com/golang/protobuf/proto" - pb "github.com/iotaledger/goshimmer/packages/autopeering/peer/service/proto" -) - -// Record defines the mapping between a service ID and its tuple TypePort -// e.g., map[autopeering:&{TCP, 8000}] -type Record struct { - m map[string]*networkAddress -} - -// networkAddress implements net.Addr -type networkAddress struct { - network string - address string -} - -// Network returns the service's network name. -func (a *networkAddress) Network() string { - return a.network -} - -// String returns the service's address in string form. -func (a *networkAddress) String() string { - return a.address -} - -// New initializes and returns an empty new Record -func New() *Record { - return &Record{ - m: make(map[string]*networkAddress), - } -} - -// Get returns the network end point address of the service with the given name. -func (s *Record) Get(key Key) net.Addr { - val, ok := s.m[string(key)] - if !ok { - return nil - } - return val -} - -// CreateRecord creates a modifyable Record from the services. -func (s *Record) CreateRecord() *Record { - result := New() - for k, v := range s.m { - result.m[k] = v - } - return result -} - -// Update adds a new service to the map. -func (s *Record) Update(key Key, network string, address string) { - s.m[string(key)] = &networkAddress{ - network: network, address: address, - } -} - -// String returns a string representation of the service record. -func (s *Record) String() string { - return fmt.Sprintf("%v", s.m) -} - -// FromProto creates a Record from the provided protobuf struct. -func FromProto(in *pb.ServiceMap) (*Record, error) { - m := in.GetMap() - if m == nil { - return nil, nil - } - - services := New() - for service, addr := range m { - services.m[service] = &networkAddress{ - network: addr.GetNetwork(), - address: addr.GetAddress(), - } - } - return services, nil -} - -// ToProto returns the corresponding protobuf struct. -func (s *Record) ToProto() *pb.ServiceMap { - if len(s.m) == 0 { - return nil - } - - services := make(map[string]*pb.NetworkAddress, len(s.m)) - for service, addr := range s.m { - services[service] = &pb.NetworkAddress{ - Network: addr.network, - Address: addr.address, - } - } - - return &pb.ServiceMap{ - Map: services, - } -} - -// Marshal serializes a given Peer (p) into a slice of bytes. -func (s *Record) Marshal() ([]byte, error) { - return proto.Marshal(s.ToProto()) -} - -// Unmarshal de-serializes a given slice of bytes (data) into a Peer. -func Unmarshal(data []byte) (*Record, error) { - s := &pb.ServiceMap{} - if err := proto.Unmarshal(data, s); err != nil { - return nil, err - } - return FromProto(s) -} diff --git a/packages/autopeering/peer/service/service.go b/packages/autopeering/peer/service/service.go deleted file mode 100644 index a201bce0..00000000 --- a/packages/autopeering/peer/service/service.go +++ /dev/null @@ -1,26 +0,0 @@ -package service - -import ( - "net" -) - -// Service is a read-only interface to access services. -type Service interface { - // Get returns the network end point address of the given service or nil if not supported. - Get(Key) net.Addr - - // CreateRecord creates a modifyable Record from the services. - CreateRecord() *Record -} - -// Key is the type of keys used to look-up a service. -type Key string - -const ( - // PeeringKey is the key for the auto peering service. - PeeringKey Key = "peering" - // FPCKey is the key for the FPC service. - FPCKey Key = "fpc" - // GossipKey is the key for the gossip service. - GossipKey Key = "gossip" -) diff --git a/packages/autopeering/peer/sort.go b/packages/autopeering/peer/sort.go deleted file mode 100644 index a0dd3e9a..00000000 --- a/packages/autopeering/peer/sort.go +++ /dev/null @@ -1,38 +0,0 @@ -package peer - -import ( - "sort" - - "github.com/iotaledger/goshimmer/packages/autopeering/distance" -) - -// PeerDistance defines the relative distance wrt a remote peer -type PeerDistance struct { - Remote *Peer - Distance uint32 -} - -// byDistance is a slice of PeerDistance used to sort -type byDistance []PeerDistance - -func (a byDistance) Len() int { return len(a) } -func (a byDistance) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a byDistance) Less(i, j int) bool { return a[i].Distance < a[j].Distance } - -// NewPeerDistance returns a new PeerDistance -func NewPeerDistance(anchorID, salt []byte, remote *Peer) PeerDistance { - return PeerDistance{ - Remote: remote, - Distance: distance.BySalt(anchorID, remote.ID().Bytes(), salt), - } -} - -// SortBySalt returns a slice of PeerDistance given a list of remote peers -func SortBySalt(anchor, salt []byte, remotePeers []*Peer) (result []PeerDistance) { - result = make(byDistance, len(remotePeers)) - for i, remote := range remotePeers { - result[i] = NewPeerDistance(anchor, salt, remote) - } - sort.Sort(byDistance(result)) - return result -} diff --git a/packages/autopeering/peer/sort_test.go b/packages/autopeering/peer/sort_test.go deleted file mode 100644 index 9357bb7a..00000000 --- a/packages/autopeering/peer/sort_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package peer - -import ( - "crypto/ed25519" - "testing" - - "github.com/stretchr/testify/assert" -) - -func newTestPeerWithID(ID byte) *Peer { - key := make([]byte, ed25519.PublicKeySize) - key[0] = ID - return NewPeer(key, newTestServiceRecord()) -} - -func TestOrderedDistanceList(t *testing.T) { - type testCase struct { - anchor []byte - salt []byte - ordered bool - } - - tests := []testCase{ - { - anchor: []byte("X"), - salt: []byte("salt"), - ordered: true, - }, - } - - remotePeers := make([]*Peer, 10) - for i := range remotePeers { - remotePeers[i] = newTestPeerWithID(byte(i + 1)) - } - - for _, test := range tests { - d := SortBySalt(test.anchor, test.salt, remotePeers) - prev := d[0] - for _, next := range d[1:] { - got := prev.Distance < next.Distance - assert.Equal(t, test.ordered, got, "Ordered distance list") - prev = next - } - } -} diff --git a/packages/autopeering/salt/proto/salt.pb.go b/packages/autopeering/salt/proto/salt.pb.go deleted file mode 100644 index cb7f27bf..00000000 --- a/packages/autopeering/salt/proto/salt.pb.go +++ /dev/null @@ -1,89 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: salt/proto/salt.proto - -package proto - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type Salt struct { - // value of the salt - Bytes []byte `protobuf:"bytes,1,opt,name=bytes,proto3" json:"bytes,omitempty"` - // expiration time of the salt - ExpTime uint64 `protobuf:"fixed64,2,opt,name=exp_time,json=expTime,proto3" json:"exp_time,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Salt) Reset() { *m = Salt{} } -func (m *Salt) String() string { return proto.CompactTextString(m) } -func (*Salt) ProtoMessage() {} -func (*Salt) Descriptor() ([]byte, []int) { - return fileDescriptor_d46762658484b01f, []int{0} -} - -func (m *Salt) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Salt.Unmarshal(m, b) -} -func (m *Salt) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Salt.Marshal(b, m, deterministic) -} -func (m *Salt) XXX_Merge(src proto.Message) { - xxx_messageInfo_Salt.Merge(m, src) -} -func (m *Salt) XXX_Size() int { - return xxx_messageInfo_Salt.Size(m) -} -func (m *Salt) XXX_DiscardUnknown() { - xxx_messageInfo_Salt.DiscardUnknown(m) -} - -var xxx_messageInfo_Salt proto.InternalMessageInfo - -func (m *Salt) GetBytes() []byte { - if m != nil { - return m.Bytes - } - return nil -} - -func (m *Salt) GetExpTime() uint64 { - if m != nil { - return m.ExpTime - } - return 0 -} - -func init() { - proto.RegisterType((*Salt)(nil), "proto.Salt") -} - -func init() { proto.RegisterFile("salt/proto/salt.proto", fileDescriptor_d46762658484b01f) } - -var fileDescriptor_d46762658484b01f = []byte{ - // 142 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x2d, 0x4e, 0xcc, 0x29, - 0xd1, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x07, 0x31, 0xf5, 0xc0, 0x4c, 0x21, 0x56, 0x30, 0xa5, - 0x64, 0xce, 0xc5, 0x12, 0x0c, 0x14, 0x14, 0x12, 0xe1, 0x62, 0x4d, 0xaa, 0x2c, 0x49, 0x2d, 0x96, - 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x09, 0x82, 0x70, 0x84, 0x24, 0xb9, 0x38, 0x52, 0x2b, 0x0a, 0xe2, - 0x4b, 0x32, 0x73, 0x53, 0x25, 0x98, 0x80, 0x12, 0x6c, 0x41, 0xec, 0x40, 0x7e, 0x08, 0x90, 0xeb, - 0x64, 0x14, 0x65, 0x90, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x9f, 0x99, - 0x5f, 0x92, 0x98, 0x93, 0x9a, 0x92, 0x9e, 0x5a, 0xa4, 0x9f, 0x58, 0x5a, 0x92, 0x5f, 0x90, 0x9a, - 0x5a, 0x94, 0x99, 0x97, 0xae, 0x5b, 0x9c, 0x99, 0xab, 0x8f, 0xb0, 0x3e, 0x89, 0x0d, 0x4c, 0x19, - 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x47, 0x07, 0x7d, 0x5f, 0x93, 0x00, 0x00, 0x00, -} diff --git a/packages/autopeering/salt/proto/salt.proto b/packages/autopeering/salt/proto/salt.proto deleted file mode 100644 index e1b61df5..00000000 --- a/packages/autopeering/salt/proto/salt.proto +++ /dev/null @@ -1,12 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/salt/proto"; - -package proto; - -message Salt { - // value of the salt - bytes bytes = 1; - // expiration time of the salt - fixed64 exp_time = 2; -} \ No newline at end of file diff --git a/packages/autopeering/salt/salt.go b/packages/autopeering/salt/salt.go deleted file mode 100644 index 07afb2ea..00000000 --- a/packages/autopeering/salt/salt.go +++ /dev/null @@ -1,86 +0,0 @@ -package salt - -import ( - "crypto/rand" - "fmt" - "sync" - "time" - - "github.com/golang/protobuf/proto" - pb "github.com/iotaledger/goshimmer/packages/autopeering/salt/proto" -) - -// SaltByteSize specifies the number of bytes used for the salt. -const SaltByteSize = 20 - -// Salt encapsulates high level functions around salt management. -type Salt struct { - bytes []byte // value of the salt - expirationTime time.Time // expiration time of the salt - mutex sync.RWMutex -} - -// NewSalt generates a new salt given a lifetime duration -func NewSalt(lifetime time.Duration) (salt *Salt, err error) { - salt = &Salt{ - bytes: make([]byte, SaltByteSize), - expirationTime: time.Now().Add(lifetime), - } - - if _, err = rand.Read(salt.bytes); err != nil { - return nil, err - } - - return salt, err -} - -func (s *Salt) GetBytes() []byte { - s.mutex.RLock() - defer s.mutex.RUnlock() - return append([]byte{}, s.bytes...) -} - -func (s *Salt) GetExpiration() time.Time { - s.mutex.RLock() - defer s.mutex.RUnlock() - return s.expirationTime -} - -// Expired returns true if the given salt expired -func (s *Salt) Expired() bool { - return time.Now().After(s.GetExpiration()) -} - -// ToProto encodes the Salt into a proto buffer Salt message -func (s *Salt) ToProto() *pb.Salt { - return &pb.Salt{ - Bytes: s.bytes, - ExpTime: uint64(s.expirationTime.Unix()), - } -} - -// FromProto decodes a given proto buffer Salt message (in) and returns the corresponding Salt. -func FromProto(in *pb.Salt) (*Salt, error) { - if l := len(in.GetBytes()); l != SaltByteSize { - return nil, fmt.Errorf("invalid salt length: %d, need %d", l, SaltByteSize) - } - out := &Salt{ - bytes: in.GetBytes(), - expirationTime: time.Unix(int64(in.GetExpTime()), 0), - } - return out, nil -} - -// Marshal serializes a given salt (s) into a slice of bytes (data) -func (s *Salt) Marshal() ([]byte, error) { - return proto.Marshal(s.ToProto()) -} - -// Unmarshal de-serializes a given slice of bytes (data) into a Salt. -func Unmarshal(data []byte) (*Salt, error) { - s := &pb.Salt{} - if err := proto.Unmarshal(data, s); err != nil { - return nil, err - } - return FromProto(s) -} diff --git a/packages/autopeering/salt/salt_test.go b/packages/autopeering/salt/salt_test.go deleted file mode 100644 index 3e5bbc3f..00000000 --- a/packages/autopeering/salt/salt_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package salt - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestNewSalt(t *testing.T) { - type testCase struct { - input time.Duration - want error - } - - tests := []testCase{ - {input: 0, want: nil}, - {input: 10, want: nil}, - {input: -1, want: nil}, - } - - for _, test := range tests { - _, err := NewSalt(test.input) - assert.Equal(t, test.want, err, test) - } -} - -func TestSaltExpired(t *testing.T) { - type testCase struct { - input time.Duration - want bool - } - - tests := []testCase{ - {input: 0, want: true}, - {input: time.Second * 10, want: false}, - {input: -1, want: true}, - } - - for _, test := range tests { - salt, _ := NewSalt(test.input) - got := salt.Expired() - assert.Equal(t, test.want, got, test) - } -} - -func TestMarshalUnmarshal(t *testing.T) { - type testCase struct { - input time.Duration - } - - tests := []testCase{ - {input: 0}, - {input: time.Second * 10}, - {input: -1}, - } - - for _, test := range tests { - salt, _ := NewSalt(test.input) - - data, err := salt.Marshal() - require.Equal(t, nil, err, "NoErrorCheck") - - got, err := Unmarshal(data) - require.Equal(t, nil, err, "NoErrorCheck") - - assert.Equal(t, salt.GetBytes(), got.GetBytes(), "Salt") - assert.Equal(t, salt.GetExpiration().Unix(), got.GetExpiration().Unix(), "SameSaltExpirationTime") - - } - -} diff --git a/packages/autopeering/selection/common.go b/packages/autopeering/selection/common.go deleted file mode 100644 index 60bff450..00000000 --- a/packages/autopeering/selection/common.go +++ /dev/null @@ -1,86 +0,0 @@ -package selection - -import ( - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/hive.go/logger" -) - -// Default values for the global parameters -const ( - DefaultInboundNeighborSize = 4 - DefaultOutboundNeighborSize = 4 - DefaultSaltLifetime = 30 * time.Minute - DefaultOutboundUpdateInterval = 1 * time.Second - DefaultFullOutboundUpdateInterval = 1 * time.Minute -) - -var ( - inboundNeighborSize = DefaultInboundNeighborSize // number of inbound neighbors - outboundNeighborSize = DefaultOutboundNeighborSize // number of outbound neighbors - saltLifetime = DefaultSaltLifetime // lifetime of the private and public local salt - outboundUpdateInterval = DefaultOutboundUpdateInterval // time after which out neighbors are updated - fullOutboundUpdateInterval = DefaultFullOutboundUpdateInterval // time after which full out neighbors are updated -) - -// Config holds settings for the peer selection. -type Config struct { - // Logger - Log *logger.Logger - - // These settings are optional: - DropOnUpdate bool // set true to drop all neighbors when the salt is updated - NeighborValidator Validator // potential neighbor validator -} - -// A Validator checks whether a peer is a valid neighbor -type Validator interface { - IsValid(*peer.Peer) bool -} - -// The ValidatorFunc type is an adapter to allow the use of ordinary functions as neighbor validators. -// If f is a function with the appropriate signature, ValidatorFunc(f) is a Validator that calls f. -type ValidatorFunc func(p *peer.Peer) bool - -// IsValid calls f(p). -func (f ValidatorFunc) IsValid(p *peer.Peer) bool { return f(p) } - -// Parameters holds the parameters that can be configured. -type Parameters struct { - InboundNeighborSize int // number of inbound neighbors - OutboundNeighborSize int // number of outbound neighbors - SaltLifetime time.Duration // lifetime of the private and public local salt - OutboundUpdateInterval time.Duration // time interval after which the outbound neighbors are checked - FullOutboundUpdateInterval time.Duration // time after which the full outbound neighbors are updated -} - -// SetParameters sets the global parameters for this package. -// This function cannot be used concurrently. -func SetParameters(param Parameters) { - if param.InboundNeighborSize > 0 { - inboundNeighborSize = param.InboundNeighborSize - } else { - inboundNeighborSize = DefaultInboundNeighborSize - } - if param.OutboundNeighborSize > 0 { - outboundNeighborSize = param.OutboundNeighborSize - } else { - outboundNeighborSize = DefaultOutboundNeighborSize - } - if param.SaltLifetime > 0 { - saltLifetime = param.SaltLifetime - } else { - saltLifetime = DefaultSaltLifetime - } - if param.OutboundUpdateInterval > 0 { - outboundUpdateInterval = param.OutboundUpdateInterval - } else { - outboundUpdateInterval = DefaultOutboundUpdateInterval - } - if param.FullOutboundUpdateInterval > 0 { - fullOutboundUpdateInterval = param.FullOutboundUpdateInterval - } else { - fullOutboundUpdateInterval = DefaultFullOutboundUpdateInterval - } -} diff --git a/packages/autopeering/selection/events.go b/packages/autopeering/selection/events.go deleted file mode 100644 index bc927e91..00000000 --- a/packages/autopeering/selection/events.go +++ /dev/null @@ -1,55 +0,0 @@ -package selection - -import ( - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" - "github.com/iotaledger/hive.go/events" -) - -// Events contains all the events that are triggered during the neighbor selection. -var Events = struct { - // A SaltUpdated event is triggered, when the private and public salt were updated. - SaltUpdated *events.Event - // An OutgoingPeering event is triggered, when a valid response of PeeringRequest has been received. - OutgoingPeering *events.Event - // An IncomingPeering event is triggered, when a valid PeerRequest has been received. - IncomingPeering *events.Event - // A Dropped event is triggered, when a neighbor is dropped or when a drop message is received. - Dropped *events.Event -}{ - SaltUpdated: events.NewEvent(saltUpdatedCaller), - OutgoingPeering: events.NewEvent(peeringCaller), - IncomingPeering: events.NewEvent(peeringCaller), - Dropped: events.NewEvent(droppedCaller), -} - -// SaltUpdatedEvent bundles the information sent in the SaltUpdated event. -type SaltUpdatedEvent struct { - Self peer.ID // ID of the peer triggering the event. - Public, Private *salt.Salt // the updated salt -} - -// PeeringEvent bundles the information sent in the OutgoingPeering and IncomingPeering event. -type PeeringEvent struct { - Self peer.ID // ID of the peer triggering the event. - Peer *peer.Peer // peering partner - Status bool // true, when the peering partner has accepted the request -} - -// DroppedEvent bundles the information sent in Dropped events. -type DroppedEvent struct { - Self peer.ID // ID of the peer triggering the event. - DroppedID peer.ID // ID of the peer that gets dropped. -} - -func saltUpdatedCaller(handler interface{}, params ...interface{}) { - handler.(func(*SaltUpdatedEvent))(params[0].(*SaltUpdatedEvent)) -} - -func peeringCaller(handler interface{}, params ...interface{}) { - handler.(func(*PeeringEvent))(params[0].(*PeeringEvent)) -} - -func droppedCaller(handler interface{}, params ...interface{}) { - handler.(func(*DroppedEvent))(params[0].(*DroppedEvent)) -} diff --git a/packages/autopeering/selection/manager.go b/packages/autopeering/selection/manager.go deleted file mode 100644 index 313d3c7c..00000000 --- a/packages/autopeering/selection/manager.go +++ /dev/null @@ -1,371 +0,0 @@ -package selection - -import ( - "sync" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" - "github.com/iotaledger/hive.go/logger" -) - -const ( - accept = true - reject = false - - // buffer size of the channels handling inbound requests and drops. - queueSize = 10 -) - -// A network represents the communication layer for the manager. -type network interface { - local() *peer.Local - - PeeringRequest(*peer.Peer, *salt.Salt) (bool, error) - PeeringDrop(*peer.Peer) -} - -type peeringRequest struct { - peer *peer.Peer - salt *salt.Salt -} - -type manager struct { - net network - getPeersToConnect func() []*peer.Peer - log *logger.Logger - dropOnUpdate bool // set true to drop all neighbors when the salt is updated - neighborValidator Validator // potential neighbor validator - - inbound *Neighborhood - outbound *Neighborhood - - rejectionFilter *Filter - - dropChan chan peer.ID - requestChan chan peeringRequest - replyChan chan bool - - wg sync.WaitGroup - closing chan struct{} -} - -func newManager(net network, peersFunc func() []*peer.Peer, log *logger.Logger, config Config) *manager { - return &manager{ - net: net, - getPeersToConnect: peersFunc, - log: log, - dropOnUpdate: config.DropOnUpdate, - neighborValidator: config.NeighborValidator, - inbound: NewNeighborhood(inboundNeighborSize), - outbound: NewNeighborhood(outboundNeighborSize), - rejectionFilter: NewFilter(), - dropChan: make(chan peer.ID, queueSize), - requestChan: make(chan peeringRequest, queueSize), - replyChan: make(chan bool, 1), - closing: make(chan struct{}), - } -} - -func (m *manager) start() { - if m.getPublicSalt() == nil || m.getPrivateSalt() == nil { - m.updateSalt() - } - m.wg.Add(1) - go m.loop() -} - -func (m *manager) close() { - close(m.closing) - m.wg.Wait() -} - -func (m *manager) getID() peer.ID { - return m.net.local().ID() -} - -func (m *manager) getPublicSalt() *salt.Salt { - return m.net.local().GetPublicSalt() -} - -func (m *manager) getPrivateSalt() *salt.Salt { - return m.net.local().GetPrivateSalt() -} - -func (m *manager) getNeighbors() []*peer.Peer { - var neighbors []*peer.Peer - neighbors = append(neighbors, m.inbound.GetPeers()...) - neighbors = append(neighbors, m.outbound.GetPeers()...) - - return neighbors -} - -func (m *manager) getInNeighbors() []*peer.Peer { - return m.inbound.GetPeers() -} - -func (m *manager) getOutNeighbors() []*peer.Peer { - return m.outbound.GetPeers() -} - -func (m *manager) requestPeering(p *peer.Peer, s *salt.Salt) bool { - var status bool - select { - case m.requestChan <- peeringRequest{p, s}: - status = <-m.replyChan - default: - // a full queue should count as a failed request - status = false - } - return status -} - -func (m *manager) removeNeighbor(id peer.ID) { - m.dropChan <- id -} - -func (m *manager) loop() { - defer m.wg.Done() - - var updateOutResultChan chan peer.PeerDistance - updateTimer := time.NewTimer(0) // setting this to 0 will cause a trigger right away - defer updateTimer.Stop() - -Loop: - for { - select { - - // update the outbound neighbors - case <-updateTimer.C: - updateOutResultChan = make(chan peer.PeerDistance) - // check salt and update if necessary - if m.getPublicSalt().Expired() { - m.updateSalt() - } - // check for new peers to connect to in a separate go routine - go m.updateOutbound(updateOutResultChan) - - // handle the result of updateOutbound - case req := <-updateOutResultChan: - if req.Remote != nil { - // if the peer is already in inbound, do not add it and remove it from inbound - if p := m.inbound.RemovePeer(req.Remote.ID()); p != nil { - m.triggerPeeringEvent(true, req.Remote, false) - m.dropPeering(p) - } else { - m.addNeighbor(m.outbound, req) - m.triggerPeeringEvent(true, req.Remote, true) - } - } - // call updateOutbound again after the given interval - updateOutResultChan = nil - updateTimer.Reset(m.getUpdateTimeout()) - - // handle a drop request - case id := <-m.dropChan: - droppedPeer := m.inbound.RemovePeer(id) - if p := m.outbound.RemovePeer(id); p != nil { - droppedPeer = p - m.rejectionFilter.AddPeer(id) - // if not yet updating, trigger an immediate update - if updateOutResultChan == nil && updateTimer.Stop() { - updateTimer.Reset(0) - } - } - if droppedPeer != nil { - m.dropPeering(droppedPeer) - } - - // handle an inbound request - case req := <-m.requestChan: - status := m.handleInRequest(req) - // trigger in the main loop to guarantee order of events - m.triggerPeeringEvent(false, req.peer, status) - - // on close, exit the loop - case <-m.closing: - break Loop - } - } - - // wait for the updateOutbound to finish - if updateOutResultChan != nil { - <-updateOutResultChan - } -} - -func (m *manager) getUpdateTimeout() time.Duration { - result := outboundUpdateInterval - if m.outbound.IsFull() { - result = fullOutboundUpdateInterval - } - saltExpiration := time.Until(m.getPublicSalt().GetExpiration()) - if saltExpiration < result { - result = saltExpiration - } - return result -} - -// updateOutbound updates outbound neighbors. -func (m *manager) updateOutbound(resultChan chan<- peer.PeerDistance) { - var result peer.PeerDistance - defer func() { resultChan <- result }() // assure that a result is always sent to the channel - - // sort verified peers by distance - distList := peer.SortBySalt(m.getID().Bytes(), m.getPublicSalt().GetBytes(), m.getPeersToConnect()) - - // filter out current neighbors - filter := m.getConnectedFilter() - if m.neighborValidator != nil { - filter.AddCondition(m.neighborValidator.IsValid) - } - - // filter out previous rejections - filteredList := m.rejectionFilter.Apply(filter.Apply(distList)) - if len(filteredList) == 0 { - return - } - - // select new candidate - candidate := m.outbound.Select(filteredList) - if candidate.Remote == nil { - return - } - - status, err := m.net.PeeringRequest(candidate.Remote, m.getPublicSalt()) - if err != nil { - m.rejectionFilter.AddPeer(candidate.Remote.ID()) - m.log.Debugw("error requesting peering", - "id", candidate.Remote.ID(), - "addr", candidate.Remote.Address(), "err", err, - ) - return - } - if !status { - m.rejectionFilter.AddPeer(candidate.Remote.ID()) - m.triggerPeeringEvent(true, candidate.Remote, false) - return - } - - result = candidate -} - -func (m *manager) handleInRequest(req peeringRequest) (resp bool) { - resp = reject - defer func() { m.replyChan <- resp }() // assure that a response is always issued - - if !m.isValidNeighbor(req.peer) { - return - } - - reqDistance := peer.NewPeerDistance(m.getID().Bytes(), m.getPrivateSalt().GetBytes(), req.peer) - filter := m.getConnectedFilter() - filteredList := filter.Apply([]peer.PeerDistance{reqDistance}) - if len(filteredList) == 0 { - return - } - - toAccept := m.inbound.Select(filteredList) - if toAccept.Remote == nil { - return - } - - m.addNeighbor(m.inbound, toAccept) - resp = accept - return -} - -func (m *manager) addNeighbor(nh *Neighborhood, toAdd peer.PeerDistance) { - // drop furthest neighbor if necessary - if furthest, _ := nh.getFurthest(); furthest.Remote != nil { - if p := nh.RemovePeer(furthest.Remote.ID()); p != nil { - m.dropPeering(p) - } - } - nh.Add(toAdd) -} - -func (m *manager) updateSalt() { - public, _ := salt.NewSalt(saltLifetime) - m.net.local().SetPublicSalt(public) - private, _ := salt.NewSalt(saltLifetime) - m.net.local().SetPrivateSalt(private) - - // clean the rejection filter - m.rejectionFilter.Clean() - - if !m.dropOnUpdate { // update distance without dropping neighbors - m.outbound.UpdateDistance(m.getID().Bytes(), m.getPublicSalt().GetBytes()) - m.inbound.UpdateDistance(m.getID().Bytes(), m.getPrivateSalt().GetBytes()) - } else { // drop all the neighbors - m.dropNeighborhood(m.inbound) - m.dropNeighborhood(m.outbound) - } - - m.log.Debugw("salt updated", - "public", saltLifetime, - "private", saltLifetime, - ) - Events.SaltUpdated.Trigger(&SaltUpdatedEvent{Self: m.getID(), Public: public, Private: private}) -} - -func (m *manager) dropNeighborhood(nh *Neighborhood) { - for _, p := range nh.GetPeers() { - nh.RemovePeer(p.ID()) - m.dropPeering(p) - } -} - -// dropPeering sends the peering drop over the network and triggers the corresponding event. -func (m *manager) dropPeering(p *peer.Peer) { - m.net.PeeringDrop(p) - - m.log.Debugw("peering dropped", - "id", p.ID(), - "#out", m.outbound, - "#in", m.inbound, - ) - Events.Dropped.Trigger(&DroppedEvent{Self: m.getID(), DroppedID: p.ID()}) -} - -func (m *manager) getConnectedFilter() *Filter { - filter := NewFilter() - filter.AddPeer(m.getID()) //set filter for oneself - filter.AddPeers(m.inbound.GetPeers()) // set filter for inbound neighbors - filter.AddPeers(m.outbound.GetPeers()) // set filter for outbound neighbors - return filter -} - -// isValidNeighbor returns whether the given peer is a valid neighbor candidate. -func (m *manager) isValidNeighbor(p *peer.Peer) bool { - // do not connect to oneself - if m.getID() == p.ID() { - return false - } - if m.neighborValidator == nil { - return true - } - return m.neighborValidator.IsValid(p) -} - -func (m *manager) triggerPeeringEvent(isOut bool, p *peer.Peer, status bool) { - if isOut { - m.log.Debugw("peering requested", - "direction", "out", - "status", status, - "to", p.ID(), - "#out", m.outbound, - "#in", m.inbound, - ) - Events.OutgoingPeering.Trigger(&PeeringEvent{Self: m.getID(), Peer: p, Status: status}) - } else { - m.log.Debugw("peering requested", - "direction", "in", - "status", status, - "from", p.ID(), - "#out", m.outbound, - "#in", m.inbound, - ) - Events.IncomingPeering.Trigger(&PeeringEvent{Self: m.getID(), Peer: p, Status: status}) - } -} diff --git a/packages/autopeering/selection/manager_test.go b/packages/autopeering/selection/manager_test.go deleted file mode 100644 index 5b9f746f..00000000 --- a/packages/autopeering/selection/manager_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package selection - -import ( - "fmt" - "sync" - "testing" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/peertest" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" - "github.com/iotaledger/goshimmer/packages/database/mapdb" - "github.com/iotaledger/hive.go/events" - "github.com/stretchr/testify/assert" -) - -const ( - testSaltLifetime = time.Hour // disable salt updates - testUpdateInterval = 2 * graceTime // very short update interval to speed up tests -) - -func TestMgrNoDuplicates(t *testing.T) { - const ( - nNeighbors = 4 - nNodes = 2*nNeighbors + 1 - ) - SetParameters(Parameters{ - OutboundNeighborSize: nNeighbors, - InboundNeighborSize: nNeighbors, - SaltLifetime: testSaltLifetime, - OutboundUpdateInterval: testUpdateInterval, - }) - - mgrMap := make(map[peer.ID]*manager) - runTestNetwork(nNodes, mgrMap) - - for _, mgr := range mgrMap { - assert.NotEmpty(t, mgr.getOutNeighbors()) - assert.NotEmpty(t, mgr.getInNeighbors()) - assert.Empty(t, getDuplicates(mgr.getNeighbors())) - } -} - -func TestEvents(t *testing.T) { - // we want many drops/connects - const ( - nNeighbors = 2 - nNodes = 10 - ) - SetParameters(Parameters{ - OutboundNeighborSize: nNeighbors, - InboundNeighborSize: nNeighbors, - SaltLifetime: 3 * testUpdateInterval, - OutboundUpdateInterval: testUpdateInterval, - }) - - e, teardown := newEventMock(t) - defer teardown() - mgrMap := make(map[peer.ID]*manager) - runTestNetwork(nNodes, mgrMap) - - // the events should lead to exactly the same neighbors - for _, mgr := range mgrMap { - nc := e.m[mgr.getID()] - assert.ElementsMatchf(t, mgr.getOutNeighbors(), getValues(nc.out), - "out neighbors of %s do not match", mgr.getID()) - assert.ElementsMatch(t, mgr.getInNeighbors(), getValues(nc.in), - "in neighbors of %s do not match", mgr.getID()) - } -} - -func getValues(m map[peer.ID]*peer.Peer) []*peer.Peer { - result := make([]*peer.Peer, 0, len(m)) - for _, p := range m { - result = append(result, p) - } - return result -} - -func runTestNetwork(n int, mgrMap map[peer.ID]*manager) { - for i := 0; i < n; i++ { - _ = newTestManager(fmt.Sprintf("%d", i), mgrMap) - } - for _, mgr := range mgrMap { - mgr.start() - } - - // give the managers time to potentially connect all other peers - time.Sleep((time.Duration(n) - 1) * (outboundUpdateInterval + graceTime)) - - // close all the managers - for _, mgr := range mgrMap { - mgr.close() - } -} - -func getDuplicates(peers []*peer.Peer) []*peer.Peer { - seen := make(map[peer.ID]bool, len(peers)) - result := make([]*peer.Peer, 0, len(peers)) - - for _, p := range peers { - if !seen[p.ID()] { - seen[p.ID()] = true - } else { - result = append(result, p) - } - } - - return result -} - -type neighbors struct { - out, in map[peer.ID]*peer.Peer -} - -type eventMock struct { - t *testing.T - lock sync.Mutex - m map[peer.ID]neighbors -} - -func newEventMock(t *testing.T) (*eventMock, func()) { - e := &eventMock{ - t: t, - m: make(map[peer.ID]neighbors), - } - - outgoingPeeringC := events.NewClosure(e.outgoingPeering) - incomingPeeringC := events.NewClosure(e.incomingPeering) - droppedC := events.NewClosure(e.dropped) - - Events.OutgoingPeering.Attach(outgoingPeeringC) - Events.IncomingPeering.Attach(incomingPeeringC) - Events.Dropped.Attach(droppedC) - - teardown := func() { - Events.OutgoingPeering.Detach(outgoingPeeringC) - Events.IncomingPeering.Detach(incomingPeeringC) - Events.Dropped.Detach(droppedC) - } - return e, teardown -} - -func (e *eventMock) outgoingPeering(ev *PeeringEvent) { - if !ev.Status { - return - } - e.lock.Lock() - defer e.lock.Unlock() - s, ok := e.m[ev.Self] - if !ok { - s = neighbors{out: make(map[peer.ID]*peer.Peer), in: make(map[peer.ID]*peer.Peer)} - e.m[ev.Self] = s - } - assert.NotContains(e.t, s.out, ev.Peer) - s.out[ev.Peer.ID()] = ev.Peer -} - -func (e *eventMock) incomingPeering(ev *PeeringEvent) { - if !ev.Status { - return - } - e.lock.Lock() - defer e.lock.Unlock() - s, ok := e.m[ev.Self] - if !ok { - s = neighbors{out: make(map[peer.ID]*peer.Peer), in: make(map[peer.ID]*peer.Peer)} - e.m[ev.Self] = s - } - assert.NotContains(e.t, s.in, ev.Peer) - s.in[ev.Peer.ID()] = ev.Peer -} - -func (e *eventMock) dropped(ev *DroppedEvent) { - e.lock.Lock() - defer e.lock.Unlock() - if assert.Contains(e.t, e.m, ev.Self) { - s := e.m[ev.Self] - delete(s.out, ev.DroppedID) - delete(s.in, ev.DroppedID) - } -} - -type networkMock struct { - loc *peer.Local - mgr map[peer.ID]*manager -} - -func (n *networkMock) local() *peer.Local { - return n.loc -} - -func (n *networkMock) PeeringDrop(p *peer.Peer) { - n.mgr[p.ID()].removeNeighbor(n.local().ID()) -} - -func (n *networkMock) PeeringRequest(p *peer.Peer, s *salt.Salt) (bool, error) { - return n.mgr[p.ID()].requestPeering(&n.local().Peer, s), nil -} - -func (n *networkMock) GetKnownPeers() []*peer.Peer { - peers := make([]*peer.Peer, 0, len(n.mgr)) - for _, m := range n.mgr { - peers = append(peers, &m.net.local().Peer) - } - return peers -} - -func newTestManager(name string, mgrMap map[peer.ID]*manager) *manager { - db, _ := peer.NewDB(mapdb.NewMapDB()) - local := peertest.NewLocal("mock", name, db) - networkMock := &networkMock{loc: local, mgr: mgrMap} - m := newManager(networkMock, networkMock.GetKnownPeers, log.Named(name), Config{}) - mgrMap[m.getID()] = m - return m -} diff --git a/packages/autopeering/selection/neighborhood.go b/packages/autopeering/selection/neighborhood.go deleted file mode 100644 index 638874b0..00000000 --- a/packages/autopeering/selection/neighborhood.go +++ /dev/null @@ -1,132 +0,0 @@ -package selection - -import ( - "fmt" - "sync" - - "github.com/iotaledger/goshimmer/packages/autopeering/distance" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" -) - -type Neighborhood struct { - neighbors []peer.PeerDistance - size int - mu sync.RWMutex -} - -func NewNeighborhood(size int) *Neighborhood { - return &Neighborhood{ - neighbors: []peer.PeerDistance{}, - size: size, - } -} - -func (nh *Neighborhood) String() string { - return fmt.Sprintf("%d/%d", nh.GetNumPeers(), nh.size) -} - -func (nh *Neighborhood) getFurthest() (peer.PeerDistance, int) { - nh.mu.RLock() - defer nh.mu.RUnlock() - if len(nh.neighbors) < nh.size { - return peer.PeerDistance{ - Remote: nil, - Distance: distance.Max, - }, len(nh.neighbors) - } - - index := 0 - furthest := nh.neighbors[index] - for i, n := range nh.neighbors { - if n.Distance > furthest.Distance { - furthest = n - index = i - } - } - return furthest, index -} - -func (nh *Neighborhood) Select(candidates []peer.PeerDistance) peer.PeerDistance { - if len(candidates) > 0 { - target, _ := nh.getFurthest() - for _, candidate := range candidates { - if candidate.Distance < target.Distance { - return candidate - } - } - } - return peer.PeerDistance{} -} - -// Add tries to add a new peer with distance to the neighborhood. -// It returns true, if the peer was added, or false if the neighborhood was full. -func (nh *Neighborhood) Add(toAdd peer.PeerDistance) bool { - nh.mu.Lock() - defer nh.mu.Unlock() - if len(nh.neighbors) >= nh.size { - return false - } - nh.neighbors = append(nh.neighbors, toAdd) - return true -} - -// RemovePeer removes the peer with the given ID from the neighborhood. -// It returns the peer that was removed or nil of no such peer exists. -func (nh *Neighborhood) RemovePeer(id peer.ID) *peer.Peer { - nh.mu.Lock() - defer nh.mu.Unlock() - - index := nh.getPeerIndex(id) - if index < 0 { - return nil - } - n := nh.neighbors[index] - - // remove index from slice - if index < len(nh.neighbors)-1 { - copy(nh.neighbors[index:], nh.neighbors[index+1:]) - } - nh.neighbors[len(nh.neighbors)-1] = peer.PeerDistance{} - nh.neighbors = nh.neighbors[:len(nh.neighbors)-1] - - return n.Remote -} - -func (nh *Neighborhood) getPeerIndex(id peer.ID) int { - for i, p := range nh.neighbors { - if p.Remote.ID() == id { - return i - } - } - return -1 -} - -func (nh *Neighborhood) UpdateDistance(anchor, salt []byte) { - nh.mu.Lock() - defer nh.mu.Unlock() - for i, n := range nh.neighbors { - nh.neighbors[i].Distance = distance.BySalt(anchor, n.Remote.ID().Bytes(), salt) - } -} - -func (nh *Neighborhood) IsFull() bool { - nh.mu.RLock() - defer nh.mu.RUnlock() - return len(nh.neighbors) >= nh.size -} - -func (nh *Neighborhood) GetPeers() []*peer.Peer { - nh.mu.RLock() - defer nh.mu.RUnlock() - result := make([]*peer.Peer, len(nh.neighbors)) - for i, n := range nh.neighbors { - result[i] = n.Remote - } - return result -} - -func (nh *Neighborhood) GetNumPeers() int { - nh.mu.RLock() - defer nh.mu.RUnlock() - return len(nh.neighbors) -} diff --git a/packages/autopeering/selection/neighborhood_test.go b/packages/autopeering/selection/neighborhood_test.go deleted file mode 100644 index 04891d91..00000000 --- a/packages/autopeering/selection/neighborhood_test.go +++ /dev/null @@ -1,254 +0,0 @@ -package selection - -import ( - "fmt" - "testing" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/distance" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/peertest" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" - "github.com/stretchr/testify/assert" -) - -func TestGetFurthest(t *testing.T) { - d := make([]peer.PeerDistance, 5) - for i := range d { - d[i].Remote = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - d[i].Distance = uint32(i + 1) - } - - type testCase struct { - input *Neighborhood - expected peer.PeerDistance - index int - } - - tests := []testCase{ - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0]}, - size: 4}, - expected: peer.PeerDistance{ - Remote: nil, - Distance: distance.Max}, - index: 1, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[2], d[3]}, - size: 4}, - expected: d[3], - index: 3, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[4], d[2]}, - size: 4}, - expected: d[4], - index: 2, - }, - } - - for _, test := range tests { - p, l := test.input.getFurthest() - assert.Equal(t, test.expected, p, "Get furthest neighbor") - assert.Equal(t, test.index, l, "Get furthest neighbor") - } -} - -func TestGetPeerIndex(t *testing.T) { - d := make([]peer.PeerDistance, 5) - for i := range d { - d[i].Remote = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - d[i].Distance = uint32(i + 1) - } - - type testCase struct { - input *Neighborhood - target *peer.Peer - index int - } - - tests := []testCase{ - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0]}, - size: 4}, - target: d[0].Remote, - index: 0, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[2], d[3]}, - size: 4}, - target: d[3].Remote, - index: 3, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[4], d[2]}, - size: 4}, - target: d[3].Remote, - index: -1, - }, - } - - for _, test := range tests { - i := test.input.getPeerIndex(test.target.ID()) - assert.Equal(t, test.index, i, "Get Peer Index") - } -} - -func TestRemove(t *testing.T) { - d := make([]peer.PeerDistance, 10) - for i := range d { - d[i].Remote = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - d[i].Distance = uint32(i + 1) - } - - type testCase struct { - input *Neighborhood - toRemove *peer.Peer - expected []peer.PeerDistance - } - - tests := []testCase{ - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0]}, - size: 4}, - toRemove: d[0].Remote, - expected: []peer.PeerDistance{}, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[3]}, - size: 4}, - toRemove: d[1].Remote, - expected: []peer.PeerDistance{d[0], d[3]}, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[4], d[2]}, - size: 4}, - toRemove: d[2].Remote, - expected: []peer.PeerDistance{d[0], d[1], d[4]}, - }, - } - - for _, test := range tests { - test.input.RemovePeer(test.toRemove.ID()) - assert.Equal(t, test.expected, test.input.neighbors, "Remove") - } -} - -func TestAdd(t *testing.T) { - d := make([]peer.PeerDistance, 10) - for i := range d { - d[i].Remote = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - d[i].Distance = uint32(i + 1) - } - - type testCase struct { - input *Neighborhood - toAdd peer.PeerDistance - expected []peer.PeerDistance - } - - tests := []testCase{ - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0]}, - size: 4}, - toAdd: d[2], - expected: []peer.PeerDistance{d[0], d[2]}, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[3]}, - size: 4}, - toAdd: d[2], - expected: []peer.PeerDistance{d[0], d[1], d[3], d[2]}, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[4], d[2]}, - size: 4}, - toAdd: d[3], - expected: []peer.PeerDistance{d[0], d[1], d[4], d[2]}, - }, - } - - for _, test := range tests { - test.input.Add(test.toAdd) - assert.Equal(t, test.expected, test.input.neighbors, "Add") - } -} - -func TestUpdateDistance(t *testing.T) { - d := make([]peer.PeerDistance, 5) - for i := range d { - d[i].Remote = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - d[i].Distance = uint32(i + 1) - } - - s, _ := salt.NewSalt(100 * time.Second) - - d2 := make([]peer.PeerDistance, 4) - for i := range d2 { - d2[i].Remote = d[i+1].Remote - d2[i].Distance = distance.BySalt(d[0].Remote.ID().Bytes(), d2[i].Remote.ID().Bytes(), s.GetBytes()) - } - - neighborhood := Neighborhood{ - neighbors: d[1:], - size: 4} - - neighborhood.UpdateDistance(d[0].Remote.ID().Bytes(), s.GetBytes()) - - assert.Equal(t, d2, neighborhood.neighbors, "UpdateDistance") -} - -func TestGetPeers(t *testing.T) { - d := make([]peer.PeerDistance, 4) - peers := make([]*peer.Peer, 4) - - for i := range d { - d[i].Remote = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - d[i].Distance = uint32(i + 1) - peers[i] = d[i].Remote - } - - type testCase struct { - input *Neighborhood - expected []*peer.Peer - } - - tests := []testCase{ - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{}, - size: 4}, - expected: []*peer.Peer{}, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0]}, - size: 4}, - expected: []*peer.Peer{peers[0]}, - }, - { - input: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[3], d[2]}, - size: 4}, - expected: []*peer.Peer{peers[0], peers[1], peers[3], peers[2]}, - }, - } - - for _, test := range tests { - got := test.input.GetPeers() - assert.Equal(t, test.expected, got, "GetPeers") - } -} diff --git a/packages/autopeering/selection/proto/message.go b/packages/autopeering/selection/proto/message.go deleted file mode 100644 index 3e1516a6..00000000 --- a/packages/autopeering/selection/proto/message.go +++ /dev/null @@ -1,35 +0,0 @@ -package proto - -import ( - "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/server" -) - -// MType is the type of message type enum. -type MType = server.MType - -// An enum for the different message types. -const ( - MPeeringRequest MType = 20 + iota - MPeeringResponse - MPeeringDrop -) - -// Message extends the proto.Message interface with additional util functions. -type Message interface { - proto.Message - - // Name returns the name of the corresponding message type for debugging. - Name() string - // Type returns the type of the corresponding message as an enum. - Type() MType -} - -func (m *PeeringRequest) Name() string { return "PEERING_REQUEST" } -func (m *PeeringRequest) Type() MType { return MPeeringRequest } - -func (m *PeeringResponse) Name() string { return "PEERING_RESPONSE" } -func (m *PeeringResponse) Type() MType { return MPeeringResponse } - -func (m *PeeringDrop) Name() string { return "PEERING_DROP" } -func (m *PeeringDrop) Type() MType { return MPeeringDrop } diff --git a/packages/autopeering/selection/proto/message.pb.go b/packages/autopeering/selection/proto/message.pb.go deleted file mode 100644 index 5ad61039..00000000 --- a/packages/autopeering/selection/proto/message.pb.go +++ /dev/null @@ -1,207 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: selection/proto/message.proto - -package proto - -import ( - fmt "fmt" - math "math" - - proto "github.com/golang/protobuf/proto" - proto1 "github.com/iotaledger/goshimmer/packages/autopeering/salt/proto" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type PeeringRequest struct { - // string form of the recipient address - To string `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` - // unix time - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // salt of the requester - Salt *proto1.Salt `protobuf:"bytes,3,opt,name=salt,proto3" json:"salt,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PeeringRequest) Reset() { *m = PeeringRequest{} } -func (m *PeeringRequest) String() string { return proto.CompactTextString(m) } -func (*PeeringRequest) ProtoMessage() {} -func (*PeeringRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_e39e4329bafd72d5, []int{0} -} - -func (m *PeeringRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PeeringRequest.Unmarshal(m, b) -} -func (m *PeeringRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PeeringRequest.Marshal(b, m, deterministic) -} -func (m *PeeringRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PeeringRequest.Merge(m, src) -} -func (m *PeeringRequest) XXX_Size() int { - return xxx_messageInfo_PeeringRequest.Size(m) -} -func (m *PeeringRequest) XXX_DiscardUnknown() { - xxx_messageInfo_PeeringRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_PeeringRequest proto.InternalMessageInfo - -func (m *PeeringRequest) GetTo() string { - if m != nil { - return m.To - } - return "" -} - -func (m *PeeringRequest) GetTimestamp() int64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -func (m *PeeringRequest) GetSalt() *proto1.Salt { - if m != nil { - return m.Salt - } - return nil -} - -type PeeringResponse struct { - // hash of the corresponding request - ReqHash []byte `protobuf:"bytes,1,opt,name=req_hash,json=reqHash,proto3" json:"req_hash,omitempty"` - // response of a peering request - Status bool `protobuf:"varint,2,opt,name=status,proto3" json:"status,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PeeringResponse) Reset() { *m = PeeringResponse{} } -func (m *PeeringResponse) String() string { return proto.CompactTextString(m) } -func (*PeeringResponse) ProtoMessage() {} -func (*PeeringResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_e39e4329bafd72d5, []int{1} -} - -func (m *PeeringResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PeeringResponse.Unmarshal(m, b) -} -func (m *PeeringResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PeeringResponse.Marshal(b, m, deterministic) -} -func (m *PeeringResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_PeeringResponse.Merge(m, src) -} -func (m *PeeringResponse) XXX_Size() int { - return xxx_messageInfo_PeeringResponse.Size(m) -} -func (m *PeeringResponse) XXX_DiscardUnknown() { - xxx_messageInfo_PeeringResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_PeeringResponse proto.InternalMessageInfo - -func (m *PeeringResponse) GetReqHash() []byte { - if m != nil { - return m.ReqHash - } - return nil -} - -func (m *PeeringResponse) GetStatus() bool { - if m != nil { - return m.Status - } - return false -} - -type PeeringDrop struct { - // string form of the recipient address - To string `protobuf:"bytes,1,opt,name=to,proto3" json:"to,omitempty"` - // unix time - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PeeringDrop) Reset() { *m = PeeringDrop{} } -func (m *PeeringDrop) String() string { return proto.CompactTextString(m) } -func (*PeeringDrop) ProtoMessage() {} -func (*PeeringDrop) Descriptor() ([]byte, []int) { - return fileDescriptor_e39e4329bafd72d5, []int{2} -} - -func (m *PeeringDrop) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PeeringDrop.Unmarshal(m, b) -} -func (m *PeeringDrop) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PeeringDrop.Marshal(b, m, deterministic) -} -func (m *PeeringDrop) XXX_Merge(src proto.Message) { - xxx_messageInfo_PeeringDrop.Merge(m, src) -} -func (m *PeeringDrop) XXX_Size() int { - return xxx_messageInfo_PeeringDrop.Size(m) -} -func (m *PeeringDrop) XXX_DiscardUnknown() { - xxx_messageInfo_PeeringDrop.DiscardUnknown(m) -} - -var xxx_messageInfo_PeeringDrop proto.InternalMessageInfo - -func (m *PeeringDrop) GetTo() string { - if m != nil { - return m.To - } - return "" -} - -func (m *PeeringDrop) GetTimestamp() int64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -func init() { - proto.RegisterType((*PeeringRequest)(nil), "proto.PeeringRequest") - proto.RegisterType((*PeeringResponse)(nil), "proto.PeeringResponse") - proto.RegisterType((*PeeringDrop)(nil), "proto.PeeringDrop") -} - -func init() { proto.RegisterFile("selection/proto/message.proto", fileDescriptor_e39e4329bafd72d5) } - -var fileDescriptor_e39e4329bafd72d5 = []byte{ - // 243 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x94, 0x90, 0xc1, 0x4b, 0xf3, 0x40, - 0x10, 0xc5, 0x69, 0xfb, 0x7d, 0xb5, 0x9d, 0x48, 0x85, 0x05, 0x25, 0x8a, 0xa2, 0xe4, 0xe4, 0xc5, - 0x2c, 0x28, 0xe2, 0xc1, 0x9b, 0xf4, 0xe0, 0x51, 0xd6, 0x9b, 0x97, 0xb2, 0xad, 0x43, 0xb2, 0x90, - 0x64, 0xb6, 0x3b, 0x93, 0xff, 0xdf, 0xed, 0x26, 0x28, 0x78, 0xf3, 0x34, 0xf3, 0xde, 0x5b, 0x7e, - 0x6f, 0x19, 0xb8, 0x62, 0x6c, 0x70, 0x27, 0x8e, 0x3a, 0xed, 0x03, 0x09, 0xe9, 0x16, 0x99, 0x6d, - 0x85, 0x65, 0x52, 0xea, 0x7f, 0x1a, 0x17, 0xa7, 0x6c, 0x1b, 0x19, 0x1f, 0x1c, 0xd6, 0x21, 0x2d, - 0x36, 0xb0, 0x7a, 0x43, 0x0c, 0xae, 0xab, 0x0c, 0xee, 0x7b, 0x64, 0x51, 0x2b, 0x98, 0x0a, 0xe5, - 0x93, 0x9b, 0xc9, 0xed, 0xd2, 0xc4, 0x4d, 0x5d, 0xc2, 0x52, 0x5c, 0x44, 0x8a, 0x6d, 0x7d, 0x3e, - 0x8d, 0xf6, 0xcc, 0xfc, 0x18, 0xea, 0x1a, 0xfe, 0x1d, 0x68, 0xf9, 0x2c, 0x06, 0xd9, 0x7d, 0x36, - 0x50, 0xcb, 0xf7, 0x68, 0x99, 0x14, 0x14, 0x6b, 0x38, 0xf9, 0x2e, 0x60, 0x4f, 0x1d, 0xa3, 0x3a, - 0x87, 0x45, 0xc0, 0xfd, 0xa6, 0xb6, 0x5c, 0xa7, 0x9e, 0x63, 0x73, 0x14, 0xf5, 0x6b, 0x94, 0xea, - 0x0c, 0xe6, 0x91, 0x2b, 0x3d, 0xa7, 0xa6, 0x85, 0x19, 0x55, 0xf1, 0x0c, 0xd9, 0x48, 0x59, 0x07, - 0xf2, 0x7f, 0xfb, 0xe3, 0xcb, 0xd3, 0xc7, 0x63, 0xe5, 0xa4, 0xee, 0xb7, 0xe5, 0x8e, 0x5a, 0xed, - 0x48, 0x6c, 0x83, 0x9f, 0x15, 0x06, 0x6d, 0x7b, 0x21, 0x3f, 0x60, 0xef, 0xd8, 0xb5, 0xfa, 0xd7, - 0x21, 0xb7, 0xf3, 0x34, 0x1e, 0xbe, 0x02, 0x00, 0x00, 0xff, 0xff, 0x3e, 0x1b, 0x76, 0xbe, 0x62, - 0x01, 0x00, 0x00, -} diff --git a/packages/autopeering/selection/proto/message.proto b/packages/autopeering/selection/proto/message.proto deleted file mode 100644 index 0e070951..00000000 --- a/packages/autopeering/selection/proto/message.proto +++ /dev/null @@ -1,30 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/selection/proto"; - -package proto; - -import "salt/proto/salt.proto"; - -message PeeringRequest { - // string form of the recipient address - string to = 1; - // unix time - int64 timestamp = 2; - // salt of the requester - Salt salt = 3; -} - -message PeeringResponse { - // hash of the corresponding request - bytes req_hash = 1; - // response of a peering request - bool status = 2; -} - -message PeeringDrop { - // string form of the recipient address - string to = 1; - // unix time - int64 timestamp = 2; -} diff --git a/packages/autopeering/selection/protocol.go b/packages/autopeering/selection/protocol.go deleted file mode 100644 index b0cf1cc1..00000000 --- a/packages/autopeering/selection/protocol.go +++ /dev/null @@ -1,370 +0,0 @@ -package selection - -import ( - "bytes" - "errors" - "fmt" - "sync" - "time" - - "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" - pb "github.com/iotaledger/goshimmer/packages/autopeering/selection/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/server" - "github.com/iotaledger/hive.go/backoff" - "github.com/iotaledger/hive.go/logger" - "github.com/iotaledger/hive.go/typeutils" -) - -const ( - maxRetries = 2 - logSends = true -) - -// policy for retrying failed network calls -var retryPolicy = backoff.ExponentialBackOff(500*time.Millisecond, 1.5).With( - backoff.Jitter(0.5), backoff.MaxRetries(maxRetries)) - -// DiscoverProtocol specifies the methods from the peer discovery that are required. -type DiscoverProtocol interface { - IsVerified(id peer.ID, addr string) bool - EnsureVerified(*peer.Peer) error - - GetVerifiedPeer(id peer.ID, addr string) *peer.Peer - GetVerifiedPeers() []*peer.Peer -} - -// The Protocol handles the neighbor selection. -// It responds to incoming messages and sends own requests when needed. -type Protocol struct { - server.Protocol - - disc DiscoverProtocol // reference to the peer discovery to query verified peers - loc *peer.Local // local peer that runs the protocol - log *logger.Logger // logging - - mgr *manager // the manager handles the actual neighbor selection - running *typeutils.AtomicBool - closeOnce sync.Once -} - -// New creates a new neighbor selection protocol. -func New(local *peer.Local, disc DiscoverProtocol, cfg Config) *Protocol { - p := &Protocol{ - Protocol: server.Protocol{}, - loc: local, - disc: disc, - log: cfg.Log, - running: typeutils.NewAtomicBool(), - } - p.mgr = newManager(p, disc.GetVerifiedPeers, cfg.Log.Named("mgr"), cfg) - - return p -} - -// Start starts the actual neighbor selection over the provided Sender. -func (p *Protocol) Start(s server.Sender) { - p.Protocol.Sender = s - p.mgr.start() - p.log.Debug("neighborhood started") - p.running.Set() -} - -// Close finalizes the protocol. -func (p *Protocol) Close() { - p.closeOnce.Do(func() { - p.running.UnSet() - p.mgr.close() - }) -} - -// GetNeighbors returns the current neighbors. -func (p *Protocol) GetNeighbors() []*peer.Peer { - return p.mgr.getNeighbors() -} - -// GetIncomingNeighbors returns the current incoming neighbors. -func (p *Protocol) GetIncomingNeighbors() []*peer.Peer { - return p.mgr.getInNeighbors() -} - -// GetOutgoingNeighbors returns the current outgoing neighbors. -func (p *Protocol) GetOutgoingNeighbors() []*peer.Peer { - return p.mgr.getOutNeighbors() -} - -// RemoveNeighbor removes the peer with the given id from the incoming and outgoing neighbors. -// If such a peer was actually contained in anyone of the neighbor sets, the corresponding event is triggered -// and the corresponding peering drop is sent. Otherwise the call is ignored. -func (p *Protocol) RemoveNeighbor(id peer.ID) { - p.mgr.removeNeighbor(id) -} - -// HandleMessage responds to incoming neighbor selection messages. -func (p *Protocol) HandleMessage(s *server.Server, fromAddr string, fromID peer.ID, _ peer.PublicKey, data []byte) (bool, error) { - if !p.running.IsSet() { - return false, nil - } - - switch pb.MType(data[0]) { - // PeeringRequest - case pb.MPeeringRequest: - m := new(pb.PeeringRequest) - if err := proto.Unmarshal(data[1:], m); err != nil { - return true, fmt.Errorf("invalid message: %w", err) - } - if p.validatePeeringRequest(fromAddr, fromID, m) { - p.handlePeeringRequest(s, fromAddr, fromID, data, m) - } - - // PeeringResponse - case pb.MPeeringResponse: - m := new(pb.PeeringResponse) - if err := proto.Unmarshal(data[1:], m); err != nil { - return true, fmt.Errorf("invalid message: %w", err) - } - p.validatePeeringResponse(s, fromAddr, fromID, m) - // PeeringResponse messages are handled in the handleReply function of the validation - - // PeeringDrop - case pb.MPeeringDrop: - m := new(pb.PeeringDrop) - if err := proto.Unmarshal(data[1:], m); err != nil { - return true, fmt.Errorf("invalid message: %w", err) - } - if p.validatePeeringDrop(fromAddr, m) { - p.handlePeeringDrop(fromID) - } - - default: - return false, nil - } - - return true, nil -} - -// Local returns the associated local peer of the neighbor selection. -func (p *Protocol) local() *peer.Local { - return p.loc -} - -// publicAddr returns the public address of the peering service in string representation. -func (p *Protocol) publicAddr() string { - return p.loc.Services().Get(service.PeeringKey).String() -} - -// ------ message senders ------ - -// PeeringRequest sends a PeeringRequest to the given peer. This method blocks -// until a response is received and the status answer is returned. -func (p *Protocol) PeeringRequest(to *peer.Peer, salt *salt.Salt) (bool, error) { - if err := p.disc.EnsureVerified(to); err != nil { - return false, err - } - - // create the request package - toAddr := to.Address() - req := newPeeringRequest(toAddr, salt) - data := marshal(req) - - // compute the message hash - hash := server.PacketHash(data) - - var status bool - callback := func(m interface{}) bool { - res := m.(*pb.PeeringResponse) - if !bytes.Equal(res.GetReqHash(), hash) { - return false - } - status = res.GetStatus() - return true - } - - err := backoff.Retry(retryPolicy, func() error { - p.logSend(toAddr, req) - err := <-p.Protocol.SendExpectingReply(toAddr, to.ID(), data, pb.MPeeringResponse, callback) - if err != nil && !errors.Is(err, server.ErrTimeout) { - return backoff.Permanent(err) - } - return err - }) - return status, err -} - -// PeeringDrop sends a peering drop message to the given peer, non-blocking and does not wait for any responses. -func (p *Protocol) PeeringDrop(to *peer.Peer) { - drop := newPeeringDrop(to.Address()) - - p.logSend(to.Address(), drop) - p.Protocol.Send(to, marshal(drop)) -} - -// ------ helper functions ------ - -func (p *Protocol) logSend(toAddr string, msg pb.Message) { - if logSends { - p.log.Debugw("send message", "type", msg.Name(), "addr", toAddr) - } -} - -func marshal(msg pb.Message) []byte { - mType := msg.Type() - if mType > 0xFF { - panic("invalid message") - } - - data, err := proto.Marshal(msg) - if err != nil { - panic("invalid message") - } - return append([]byte{byte(mType)}, data...) -} - -// ------ Message Constructors ------ - -func newPeeringRequest(toAddr string, salt *salt.Salt) *pb.PeeringRequest { - return &pb.PeeringRequest{ - To: toAddr, - Timestamp: time.Now().Unix(), - Salt: salt.ToProto(), - } -} - -func newPeeringResponse(reqData []byte, status bool) *pb.PeeringResponse { - return &pb.PeeringResponse{ - ReqHash: server.PacketHash(reqData), - Status: status, - } -} - -func newPeeringDrop(toAddr string) *pb.PeeringDrop { - return &pb.PeeringDrop{ - To: toAddr, - Timestamp: time.Now().Unix(), - } -} - -// ------ Packet Handlers ------ - -func (p *Protocol) validatePeeringRequest(fromAddr string, fromID peer.ID, m *pb.PeeringRequest) bool { - // check that To matches the local address - if m.GetTo() != p.publicAddr() { - p.log.Debugw("invalid message", - "type", m.Name(), - "to", m.GetTo(), - "want", p.publicAddr(), - ) - return false - } - // check Timestamp - if p.Protocol.IsExpired(m.GetTimestamp()) { - p.log.Debugw("invalid message", - "type", m.Name(), - "timestamp", time.Unix(m.GetTimestamp(), 0), - ) - return false - } - // check whether the sender is verified - if !p.disc.IsVerified(fromID, fromAddr) { - p.log.Debugw("invalid message", - "type", m.Name(), - "unverified", fromAddr, - ) - return false - } - // check Salt - s, err := salt.FromProto(m.GetSalt()) - if err != nil { - p.log.Debugw("invalid message", - "type", m.Name(), - "salt", err, - ) - return false - } - // check salt expiration - if s.Expired() { - p.log.Debugw("invalid message", - "type", m.Name(), - "salt.expiration", s.GetExpiration(), - ) - return false - } - - p.log.Debugw("valid message", - "type", m.Name(), - "addr", fromAddr, - ) - return true -} - -func (p *Protocol) handlePeeringRequest(s *server.Server, fromAddr string, fromID peer.ID, rawData []byte, m *pb.PeeringRequest) { - salt, err := salt.FromProto(m.GetSalt()) - if err != nil { - // this should not happen as it is checked in validation - p.log.Warnw("invalid salt", "err", err) - return - } - - from := p.disc.GetVerifiedPeer(fromID, fromAddr) - if from == nil { - // this should not happen as it is checked in validation - p.log.Warnw("invalid stored peer", - "id", fromID, - ) - return - } - - res := newPeeringResponse(rawData, p.mgr.requestPeering(from, salt)) - - p.logSend(fromAddr, res) - s.Send(fromAddr, marshal(res)) -} - -func (p *Protocol) validatePeeringResponse(s *server.Server, fromAddr string, fromID peer.ID, m *pb.PeeringResponse) bool { - // there must be a request waiting for this response - if !s.IsExpectedReply(fromAddr, fromID, m.Type(), m) { - p.log.Debugw("invalid message", - "type", m.Name(), - "unexpected", fromAddr, - ) - return false - } - - p.log.Debugw("valid message", - "type", m.Name(), - "addr", fromAddr, - ) - return true -} - -func (p *Protocol) validatePeeringDrop(fromAddr string, m *pb.PeeringDrop) bool { - // check that To matches the local address - if m.GetTo() != p.publicAddr() { - p.log.Debugw("invalid message", - "type", m.Name(), - "to", m.GetTo(), - "want", p.publicAddr(), - ) - return false - } - // check Timestamp - if p.Protocol.IsExpired(m.GetTimestamp()) { - p.log.Debugw("invalid message", - "type", m.Name(), - "timestamp", time.Unix(m.GetTimestamp(), 0), - ) - return false - } - - p.log.Debugw("valid message", - "type", m.Name(), - "addr", fromAddr, - ) - return true -} - -func (p *Protocol) handlePeeringDrop(fromID peer.ID) { - p.mgr.removeNeighbor(fromID) -} diff --git a/packages/autopeering/selection/protocol_test.go b/packages/autopeering/selection/protocol_test.go deleted file mode 100644 index 4df4b46a..00000000 --- a/packages/autopeering/selection/protocol_test.go +++ /dev/null @@ -1,185 +0,0 @@ -package selection - -import ( - "testing" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/discover" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/peertest" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" - "github.com/iotaledger/goshimmer/packages/autopeering/server" - "github.com/iotaledger/goshimmer/packages/autopeering/transport" - "github.com/iotaledger/goshimmer/packages/database/mapdb" - "github.com/iotaledger/hive.go/logger" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -const ( - testNetwork = "udp" - graceTime = 100 * time.Millisecond -) - -var ( - log = logger.NewExampleLogger("discover") - peerMap = make(map[peer.ID]*peer.Peer) -) - -func TestProtocol(t *testing.T) { - // assure that the default test parameters are used for all protocol tests - SetParameters(Parameters{ - SaltLifetime: testSaltLifetime, - OutboundUpdateInterval: testUpdateInterval, - }) - - t.Run("PeeringRequest", func(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A) - defer closeA() - protB, closeB := newTestProtocol(p2p.B) - defer closeB() - - peerA := getPeer(protA) - saltA, _ := salt.NewSalt(100 * time.Second) - peerB := getPeer(protB) - saltB, _ := salt.NewSalt(100 * time.Second) - - // request peering to peer B - t.Run("A->B", func(t *testing.T) { - if services, err := protA.PeeringRequest(peerB, saltA); assert.NoError(t, err) { - assert.NotEmpty(t, services) - } - }) - // request peering to peer A - t.Run("B->A", func(t *testing.T) { - if services, err := protB.PeeringRequest(peerA, saltB); assert.NoError(t, err) { - assert.NotEmpty(t, services) - } - }) - }) - - t.Run("ExpiredSalt", func(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A) - defer closeA() - protB, closeB := newTestProtocol(p2p.B) - defer closeB() - - saltA, _ := salt.NewSalt(-1 * time.Second) - peerB := getPeer(protB) - - // request peering to peer B - _, err := protA.PeeringRequest(peerB, saltA) - assert.EqualError(t, err, server.ErrTimeout.Error()) - }) - - t.Run("PeeringDrop", func(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newTestProtocol(p2p.A) - defer closeA() - protB, closeB := newTestProtocol(p2p.B) - defer closeB() - - peerA := getPeer(protA) - saltA, _ := salt.NewSalt(100 * time.Second) - peerB := getPeer(protB) - - // request peering to peer B - status, err := protA.PeeringRequest(peerB, saltA) - require.NoError(t, err) - assert.True(t, status) - - require.Contains(t, protB.GetNeighbors(), peerA) - - // drop peer A - protA.PeeringDrop(peerB) - time.Sleep(graceTime) - require.NotContains(t, protB.GetNeighbors(), peerA) - }) - - t.Run("FullTest", func(t *testing.T) { - p2p := transport.P2P() - defer p2p.Close() - - protA, closeA := newFullTestProtocol(p2p.A) - defer closeA() - - time.Sleep(graceTime) // wait for the master to initialize - - protB, closeB := newFullTestProtocol(p2p.B, getPeer(protA)) - defer closeB() - - time.Sleep(outboundUpdateInterval + graceTime) // wait for the next outbound cycle - - // the two peers should be peered - assert.ElementsMatch(t, []*peer.Peer{getPeer(protB)}, protA.GetNeighbors()) - assert.ElementsMatch(t, []*peer.Peer{getPeer(protA)}, protB.GetNeighbors()) - }) -} - -// dummyDiscovery is a dummy implementation of DiscoveryProtocol never returning any verified peers. -type dummyDiscovery struct{} - -func (d dummyDiscovery) IsVerified(peer.ID, string) bool { return true } -func (d dummyDiscovery) EnsureVerified(*peer.Peer) error { return nil } -func (d dummyDiscovery) GetVerifiedPeer(id peer.ID, _ string) *peer.Peer { return peerMap[id] } -func (d dummyDiscovery) GetVerifiedPeers() []*peer.Peer { return []*peer.Peer{} } - -// newTestProtocol creates a new neighborhood server and also returns the teardown. -func newTestProtocol(trans transport.Transport) (*Protocol, func()) { - db, _ := peer.NewDB(mapdb.NewMapDB()) - local := peertest.NewLocal(trans.LocalAddr().Network(), trans.LocalAddr().String(), db) - // add the new peer to the global map for dummyDiscovery - peerMap[local.ID()] = &local.Peer - l := log.Named(trans.LocalAddr().String()) - - prot := New(local, dummyDiscovery{}, Config{Log: l.Named("disc")}) - srv := server.Serve(local, trans, l.Named("srv"), prot) - prot.Start(srv) - - teardown := func() { - srv.Close() - prot.Close() - } - return prot, teardown -} - -// newTestProtocol creates a new server handling discover as well as neighborhood and also returns the teardown. -func newFullTestProtocol(trans transport.Transport, masterPeers ...*peer.Peer) (*Protocol, func()) { - db, _ := peer.NewDB(mapdb.NewMapDB()) - local := peertest.NewLocal(trans.LocalAddr().Network(), trans.LocalAddr().String(), db) - // add the new peer to the global map for dummyDiscovery - peerMap[local.ID()] = &local.Peer - l := log.Named(trans.LocalAddr().String()) - - discovery := discover.New(local, discover.Config{ - Log: l.Named("disc"), - MasterPeers: masterPeers, - }) - selection := New(local, discovery, Config{ - Log: l.Named("sel"), - }) - - srv := server.Serve(local, trans, l.Named("srv"), discovery, selection) - - discovery.Start(srv) - selection.Start(srv) - - teardown := func() { - srv.Close() - selection.Close() - discovery.Close() - } - return selection, teardown -} - -func getPeer(p *Protocol) *peer.Peer { - return &p.local().Peer -} diff --git a/packages/autopeering/selection/selection.go b/packages/autopeering/selection/selection.go deleted file mode 100644 index f27393ac..00000000 --- a/packages/autopeering/selection/selection.go +++ /dev/null @@ -1,72 +0,0 @@ -package selection - -import ( - "sync" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" -) - -type Selector interface { - Select(candidates peer.PeerDistance) *peer.Peer -} - -type Filter struct { - internal map[peer.ID]bool - conditions []func(*peer.Peer) bool - lock sync.RWMutex -} - -func NewFilter() *Filter { - return &Filter{ - internal: make(map[peer.ID]bool), - } -} - -func (f *Filter) Apply(list []peer.PeerDistance) (filtered []peer.PeerDistance) { - f.lock.RLock() - defer f.lock.RUnlock() - -list: - for _, p := range list { - if _, contains := f.internal[p.Remote.ID()]; contains { - continue - } - for _, c := range f.conditions { - if !c(p.Remote) { - continue list - } - } - filtered = append(filtered, p) - } - return -} - -func (f *Filter) AddPeers(n []*peer.Peer) { - f.lock.Lock() - for _, p := range n { - f.internal[p.ID()] = true - } - f.lock.Unlock() -} - -func (f *Filter) AddPeer(peer peer.ID) { - f.lock.Lock() - f.internal[peer] = true - f.lock.Unlock() -} - -func (f *Filter) RemovePeer(peer peer.ID) { - f.lock.Lock() - f.internal[peer] = false - f.lock.Unlock() -} - -func (f *Filter) AddCondition(c func(p *peer.Peer) bool) { - f.conditions = append(f.conditions, c) -} - -func (f *Filter) Clean() { - f.lock.Lock() - f.internal = make(map[peer.ID]bool) - f.lock.Unlock() -} diff --git a/packages/autopeering/selection/selection_test.go b/packages/autopeering/selection/selection_test.go deleted file mode 100644 index 49d22c8d..00000000 --- a/packages/autopeering/selection/selection_test.go +++ /dev/null @@ -1,165 +0,0 @@ -package selection - -import ( - "fmt" - "testing" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/peertest" - "github.com/stretchr/testify/assert" -) - -func TestFilterAddPeers(t *testing.T) { - p := make([]*peer.Peer, 5) - for i := range p { - p[i] = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - } - - type testCase struct { - input []*peer.Peer - notExist []*peer.Peer - } - - tests := []testCase{ - { - input: []*peer.Peer{p[0]}, - notExist: []*peer.Peer{p[1]}, - }, - { - input: []*peer.Peer{p[0], p[1], p[2]}, - notExist: []*peer.Peer{p[3], p[4]}, - }, - } - - for _, test := range tests { - f := NewFilter() - f.AddPeers(test.input) - for _, e := range test.input { - assert.Equal(t, true, f.internal[e.ID()], "Filter add peers") - } - - for _, e := range test.notExist { - assert.Equal(t, false, f.internal[e.ID()], "Filter add peers") - } - } -} - -func TestFilterRemovePeers(t *testing.T) { - p := make([]*peer.Peer, 5) - for i := range p { - p[i] = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - } - - type testCase struct { - input []*peer.Peer - toRemove *peer.Peer - left []*peer.Peer - } - - tests := []testCase{ - { - input: []*peer.Peer{p[0]}, - toRemove: p[0], - left: []*peer.Peer{}, - }, - { - input: []*peer.Peer{p[0], p[1], p[2]}, - toRemove: p[1], - left: []*peer.Peer{p[0], p[2]}, - }, - } - - for _, test := range tests { - f := NewFilter() - f.AddPeers(test.input) - f.RemovePeer(test.toRemove.ID()) - for _, e := range test.left { - assert.Equal(t, true, f.internal[e.ID()], "Filter remove peers") - } - assert.Equal(t, false, f.internal[test.toRemove.ID()], "Filter remove peers") - } -} - -func TestFilterApply(t *testing.T) { - d := make([]peer.PeerDistance, 5) - for i := range d { - d[i].Remote = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - d[i].Distance = uint32(i + 1) - } - - type testCase struct { - input []*peer.Peer - apply []peer.PeerDistance - expected []peer.PeerDistance - } - - tests := []testCase{ - { - input: []*peer.Peer{d[0].Remote}, - apply: []peer.PeerDistance{d[0], d[1], d[2]}, - expected: []peer.PeerDistance{d[1], d[2]}, - }, - { - input: []*peer.Peer{d[0].Remote, d[1].Remote}, - apply: []peer.PeerDistance{d[2], d[3], d[4]}, - expected: []peer.PeerDistance{d[2], d[3], d[4]}, - }, - } - - for _, test := range tests { - f := NewFilter() - f.AddPeers(test.input) - filteredList := f.Apply(test.apply) - assert.Equal(t, test.expected, filteredList, "Filter apply") - } -} - -func TestSelection(t *testing.T) { - d := make([]peer.PeerDistance, 10) - for i := range d { - d[i].Remote = peertest.NewPeer(testNetwork, fmt.Sprintf("%d", i)) - d[i].Distance = uint32(i + 1) - } - - type testCase struct { - nh *Neighborhood - expCandidate *peer.Peer - } - - tests := []testCase{ - { - nh: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0]}, - size: 4}, - expCandidate: d[1].Remote, - }, - { - nh: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[3]}, - size: 4}, - expCandidate: d[2].Remote, - }, - { - nh: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[4], d[2]}, - size: 4}, - expCandidate: d[3].Remote, - }, - { - nh: &Neighborhood{ - neighbors: []peer.PeerDistance{d[0], d[1], d[2], d[3]}, - size: 4}, - expCandidate: nil, - }, - } - - for _, test := range tests { - filter := NewFilter() - filter.AddPeers(test.nh.GetPeers()) - fList := filter.Apply(d) - - got := test.nh.Select(fList) - - assert.Equal(t, test.expCandidate, got.Remote, "Next Candidate", test) - } -} diff --git a/packages/autopeering/server/common.go b/packages/autopeering/server/common.go deleted file mode 100644 index 593d916b..00000000 --- a/packages/autopeering/server/common.go +++ /dev/null @@ -1,38 +0,0 @@ -package server - -import ( - "crypto/sha256" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" -) - -// MType is the type of message type enum. -type MType uint - -// The Sender interface specifies common method required to send requests. -type Sender interface { - Send(toAddr string, data []byte) - SendExpectingReply(toAddr string, toID peer.ID, data []byte, replyType MType, callback func(interface{}) bool) <-chan error -} - -// A Handler reacts to an incoming message. -type Handler interface { - // HandleMessage is called for each incoming message. - // It returns true, if that particular message type can be processed by the current Handler. - HandleMessage(s *Server, fromAddr string, fromID peer.ID, fromKey peer.PublicKey, data []byte) (bool, error) -} - -// The HandlerFunc type is an adapter to allow the use of ordinary functions as Server handlers. -// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler that calls f. -type HandlerFunc func(*Server, string, peer.ID, peer.PublicKey, []byte) (bool, error) - -// HandleMessage returns f(s, from, data). -func (f HandlerFunc) HandleMessage(s *Server, fromAddr string, fromID peer.ID, fromKey peer.PublicKey, data []byte) (bool, error) { - return f(s, fromAddr, fromID, fromKey, data) -} - -// PacketHash returns the hash of a packet -func PacketHash(data []byte) []byte { - sum := sha256.Sum256(data) - return sum[:] -} diff --git a/packages/autopeering/server/errors.go b/packages/autopeering/server/errors.go deleted file mode 100644 index c278c2f5..00000000 --- a/packages/autopeering/server/errors.go +++ /dev/null @@ -1,17 +0,0 @@ -package server - -import "errors" - -var ( - // ErrTimeout is returned when an expected response was not received in time. - ErrTimeout = errors.New("response timeout") - - // ErrClosed means that the server was shut down before a response could be received. - ErrClosed = errors.New("socket closed") - - // ErrNoMessage is returned when the package did not contain any data. - ErrNoMessage = errors.New("packet does not contain a message") - - // ErrInvalidMessage means that no handler could process the received message. - ErrInvalidMessage = errors.New("invalid message") -) diff --git a/packages/autopeering/server/proto/packet.pb.go b/packages/autopeering/server/proto/packet.pb.go deleted file mode 100644 index 9c8dd1f5..00000000 --- a/packages/autopeering/server/proto/packet.pb.go +++ /dev/null @@ -1,97 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: server/proto/packet.proto - -package proto - -import ( - fmt "fmt" - proto "github.com/golang/protobuf/proto" - math "math" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package - -type Packet struct { - PublicKey []byte `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"` - Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` - Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Packet) Reset() { *m = Packet{} } -func (m *Packet) String() string { return proto.CompactTextString(m) } -func (*Packet) ProtoMessage() {} -func (*Packet) Descriptor() ([]byte, []int) { - return fileDescriptor_c39379c1dd924f72, []int{0} -} - -func (m *Packet) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Packet.Unmarshal(m, b) -} -func (m *Packet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Packet.Marshal(b, m, deterministic) -} -func (m *Packet) XXX_Merge(src proto.Message) { - xxx_messageInfo_Packet.Merge(m, src) -} -func (m *Packet) XXX_Size() int { - return xxx_messageInfo_Packet.Size(m) -} -func (m *Packet) XXX_DiscardUnknown() { - xxx_messageInfo_Packet.DiscardUnknown(m) -} - -var xxx_messageInfo_Packet proto.InternalMessageInfo - -func (m *Packet) GetPublicKey() []byte { - if m != nil { - return m.PublicKey - } - return nil -} - -func (m *Packet) GetSignature() []byte { - if m != nil { - return m.Signature - } - return nil -} - -func (m *Packet) GetData() []byte { - if m != nil { - return m.Data - } - return nil -} - -func init() { - proto.RegisterType((*Packet)(nil), "proto.Packet") -} - -func init() { proto.RegisterFile("server/proto/packet.proto", fileDescriptor_c39379c1dd924f72) } - -var fileDescriptor_c39379c1dd924f72 = []byte{ - // 166 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x92, 0x2c, 0x4e, 0x2d, 0x2a, - 0x4b, 0x2d, 0xd2, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x2f, 0x48, 0x4c, 0xce, 0x4e, 0x2d, 0xd1, - 0x03, 0x73, 0x84, 0x58, 0xc1, 0x94, 0x52, 0x24, 0x17, 0x5b, 0x00, 0x58, 0x58, 0x48, 0x96, 0x8b, - 0xab, 0xa0, 0x34, 0x29, 0x27, 0x33, 0x39, 0x3e, 0x3b, 0xb5, 0x52, 0x82, 0x51, 0x81, 0x51, 0x83, - 0x27, 0x88, 0x13, 0x22, 0xe2, 0x9d, 0x5a, 0x29, 0x24, 0xc3, 0xc5, 0x59, 0x9c, 0x99, 0x9e, 0x97, - 0x58, 0x52, 0x5a, 0x94, 0x2a, 0xc1, 0x04, 0x91, 0x85, 0x0b, 0x08, 0x09, 0x71, 0xb1, 0xa4, 0x24, - 0x96, 0x24, 0x4a, 0x30, 0x83, 0x25, 0xc0, 0x6c, 0x27, 0x93, 0x28, 0xa3, 0xf4, 0xcc, 0x92, 0x8c, - 0xd2, 0x24, 0xbd, 0xe4, 0xfc, 0x5c, 0xfd, 0xcc, 0xfc, 0x92, 0xc4, 0x9c, 0xd4, 0x94, 0x74, 0xa0, - 0x6b, 0x12, 0x4b, 0x4b, 0xf2, 0x0b, 0x52, 0x53, 0x8b, 0x32, 0xf3, 0xd2, 0x75, 0x8b, 0x33, 0x73, - 0xf5, 0x91, 0x1d, 0x99, 0xc4, 0x06, 0xa6, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x50, 0x6a, - 0x7d, 0xb7, 0xbb, 0x00, 0x00, 0x00, -} diff --git a/packages/autopeering/server/proto/packet.proto b/packages/autopeering/server/proto/packet.proto deleted file mode 100644 index 59da610d..00000000 --- a/packages/autopeering/server/proto/packet.proto +++ /dev/null @@ -1,11 +0,0 @@ -syntax = "proto3"; - -option go_package = "github.com/iotaledger/goshimmer/packages/autopeering/server/proto"; - -package proto; - -message Packet { - bytes public_key = 1; - bytes signature = 2; - bytes data = 3; -} diff --git a/packages/autopeering/server/protocol.go b/packages/autopeering/server/protocol.go deleted file mode 100644 index e3ef6a62..00000000 --- a/packages/autopeering/server/protocol.go +++ /dev/null @@ -1,32 +0,0 @@ -package server - -import ( - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" -) - -const ( - packetExpiration = 20 * time.Second -) - -// Protocol provides a basis for server protocols handling incoming messages. -type Protocol struct { - Sender Sender // interface to send own requests -} - -// Send sends the data to the given peer. -func (p *Protocol) Send(to *peer.Peer, data []byte) { - p.Sender.Send(to.Address(), data) -} - -// SendExpectingReply sends request data to a peer and expects a response of the given type. -// On an incoming matching request the callback is executed to perform additional verification steps. -func (p *Protocol) SendExpectingReply(toAddr string, toID peer.ID, data []byte, replyType MType, callback func(interface{}) bool) <-chan error { - return p.Sender.SendExpectingReply(toAddr, toID, data, replyType, callback) -} - -// IsExpired checks whether the given UNIX time stamp is too far in the past. -func (p *Protocol) IsExpired(ts int64) bool { - return time.Since(time.Unix(ts, 0)) >= packetExpiration -} diff --git a/packages/autopeering/server/server.go b/packages/autopeering/server/server.go deleted file mode 100644 index d1bca6aa..00000000 --- a/packages/autopeering/server/server.go +++ /dev/null @@ -1,315 +0,0 @@ -package server - -import ( - "container/list" - "fmt" - "io" - "net" - "sync" - "time" - - "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - pb "github.com/iotaledger/goshimmer/packages/autopeering/server/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/transport" - "github.com/iotaledger/goshimmer/packages/netutil" - "github.com/iotaledger/hive.go/logger" -) - -const ( - // ResponseTimeout specifies the time limit after which a response must have been received. - ResponseTimeout = 500 * time.Millisecond -) - -// Server offers the functionality to start a server that handles requests and responses from peers. -type Server struct { - local *peer.Local - trans transport.Transport - handlers []Handler - log *logger.Logger - network string - address string - - closeOnce sync.Once - wg sync.WaitGroup - - addReplyMatcher chan *replyMatcher - replyReceived chan reply - closing chan struct{} // if this channel gets closed all pending waits should terminate -} - -// a replyMatcher stores the information required to identify and react to an expected replay. -type replyMatcher struct { - // fromAddr must match the sender of the reply - fromAddr string - // fromID must match the sender ID - fromID peer.ID - // mtype must match the type of the reply - mtype MType - - // deadline when the request must complete - deadline time.Time - - // callback is called when a matching reply arrives - // If it returns true, the reply is acceptable. - callback func(msg interface{}) bool - - // errc receives nil when the callback indicates completion or an - // error if no further reply is received within the timeout - errc chan error -} - -// reply is a reply packet from a certain peer -type reply struct { - fromAddr string - fromID peer.ID - mtype MType - msg interface{} // the actual reply message - matchedRequest chan<- bool // a matching request is indicated via this channel -} - -// Serve starts a new peer server using the given transport layer for communication. -// Sent data is signed using the identity of the local peer, -// received data with a valid peer signature is handled according to the provided Handler. -func Serve(local *peer.Local, t transport.Transport, log *logger.Logger, h ...Handler) *Server { - srv := &Server{ - local: local, - trans: t, - handlers: h, - log: log, - network: local.Network(), - address: local.Address(), - addReplyMatcher: make(chan *replyMatcher), - replyReceived: make(chan reply), - closing: make(chan struct{}), - } - - srv.wg.Add(2) - go srv.replyLoop() - go srv.readLoop() - - log.Debugw("server started", - "network", srv.LocalAddr().Network(), - "address", srv.LocalAddr().String(), - "#handlers", len(h)) - - return srv -} - -// Close shuts down the server. -func (s *Server) Close() { - s.closeOnce.Do(func() { - close(s.closing) - s.trans.Close() - s.wg.Wait() - }) -} - -// Local returns the the local peer. -func (s *Server) Local() *peer.Local { - return s.local -} - -// LocalAddr returns the address of the local peer in string form. -func (s *Server) LocalAddr() net.Addr { - return s.trans.LocalAddr() -} - -// Send sends a message to the given address -func (s *Server) Send(toAddr string, data []byte) { - pkt := s.encode(data) - s.write(toAddr, pkt) -} - -// SendExpectingReply sends a message to the given address and tells the Server -// to expect a reply message with the given specifications. -// If eventually nil is returned, a matching message was received. -func (s *Server) SendExpectingReply(toAddr string, toID peer.ID, data []byte, replyType MType, callback func(interface{}) bool) <-chan error { - errc := s.expectReply(toAddr, toID, replyType, callback) - s.Send(toAddr, data) - - return errc -} - -// expectReply tells the Server to expect a reply message with the given specifications. -// If eventually nil is returned, a matching message was received. -func (s *Server) expectReply(fromAddr string, fromID peer.ID, mtype MType, callback func(interface{}) bool) <-chan error { - ch := make(chan error, 1) - m := &replyMatcher{fromAddr: fromAddr, fromID: fromID, mtype: mtype, callback: callback, errc: ch} - select { - case s.addReplyMatcher <- m: - case <-s.closing: - ch <- ErrClosed - } - return ch -} - -// IsExpectedReply checks whether the given Message matches an expected reply added with SendExpectingReply. -func (s *Server) IsExpectedReply(fromAddr string, fromID peer.ID, mtype MType, msg interface{}) bool { - matched := make(chan bool, 1) - select { - case s.replyReceived <- reply{fromAddr, fromID, mtype, msg, matched}: - // wait for matcher and return whether a matching request was found - return <-matched - case <-s.closing: - return false - } -} - -// Loop checking for matching replies. -func (s *Server) replyLoop() { - defer s.wg.Done() - - var ( - matcherList = list.New() - timeout = time.NewTimer(0) - ) - defer timeout.Stop() - - <-timeout.C // ignore first timeout - - for { - - // Set the timer so that it fires when the next reply expires - if e := matcherList.Front(); e != nil { - // the first element always has the closest deadline - m := e.Value.(*replyMatcher) - timeout.Reset(time.Until(m.deadline)) - } else { - timeout.Stop() - } - - select { - - // add a new matcher to the list - case s := <-s.addReplyMatcher: - s.deadline = time.Now().Add(ResponseTimeout) - matcherList.PushBack(s) - - // on reply received, check all matchers for fits - case r := <-s.replyReceived: - matched := false - for e := matcherList.Front(); e != nil; e = e.Next() { - m := e.Value.(*replyMatcher) - if m.mtype == r.mtype && m.fromID == r.fromID && m.fromAddr == r.fromAddr { - if m.callback(r.msg) { - // request has been matched - matched = true - m.errc <- nil - matcherList.Remove(e) - } - } - } - r.matchedRequest <- matched - - // on timeout, check for expired matchers - case <-timeout.C: - now := time.Now() - - // notify and remove any expired matchers - for e := matcherList.Front(); e != nil; e = e.Next() { - m := e.Value.(*replyMatcher) - if now.After(m.deadline) || now.Equal(m.deadline) { - m.errc <- ErrTimeout - matcherList.Remove(e) - } - } - - // on close, notice all the matchers - case <-s.closing: - for e := matcherList.Front(); e != nil; e = e.Next() { - e.Value.(*replyMatcher).errc <- ErrClosed - } - return - - } - } -} - -func (s *Server) write(toAddr string, pkt *pb.Packet) { - b, err := proto.Marshal(pkt) - if err != nil { - s.log.Error("marshal error", "err", err) - return - } - if l := len(b); l > transport.MaxPacketSize { - s.log.Error("packet too large", "size", l, "max", transport.MaxPacketSize) - return - } - - err = s.trans.WriteTo(b, toAddr) - if err != nil { - s.log.Debugw("failed to write packet", "addr", toAddr, "err", err) - } -} - -// encodes a message as a data packet that can be written. -func (s *Server) encode(data []byte) *pb.Packet { - if len(data) == 0 { - panic("server: no data") - } - - return &pb.Packet{ - PublicKey: s.local.PublicKey(), - Signature: s.local.Sign(data), - Data: append([]byte(nil), data...), - } -} - -func (s *Server) readLoop() { - defer s.wg.Done() - - for { - b, fromAddr, err := s.trans.ReadFrom() - if netutil.IsTemporaryError(err) { - // ignore temporary read errors. - s.log.Debugw("temporary read error", "err", err) - continue - } - if err != nil { - // return from the loop on all other errors - if err != io.EOF { - s.log.Warnw("read error", "err", err) - } - s.log.Debug("reading stopped") - return - } - - pkt := new(pb.Packet) - if err := proto.Unmarshal(b, pkt); err != nil { - s.log.Debugw("bad packet", "from", fromAddr, "err", err) - continue - } - if err := s.handlePacket(pkt, fromAddr); err != nil { - s.log.Debugw("failed to handle packet", "from", fromAddr, "err", err) - } - } -} - -func (s *Server) handlePacket(pkt *pb.Packet, fromAddr string) error { - data, key, err := decode(pkt) - if err != nil { - return fmt.Errorf("invalid packet: %w", err) - } - - fromID := key.ID() - for _, handler := range s.handlers { - ok, err := handler.HandleMessage(s, fromAddr, fromID, key, data) - if ok { - return err - } - } - return ErrInvalidMessage -} - -func decode(pkt *pb.Packet) ([]byte, peer.PublicKey, error) { - if len(pkt.GetData()) == 0 { - return nil, nil, ErrNoMessage - } - - key, err := peer.RecoverKeyFromSignedData(pkt) - if err != nil { - return nil, nil, err - } - return pkt.GetData(), key, nil -} diff --git a/packages/autopeering/server/server_test.go b/packages/autopeering/server/server_test.go deleted file mode 100644 index 8fc6022f..00000000 --- a/packages/autopeering/server/server_test.go +++ /dev/null @@ -1,216 +0,0 @@ -package server - -import ( - "testing" - "time" - - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - "github.com/iotaledger/goshimmer/packages/autopeering/salt" - "github.com/iotaledger/goshimmer/packages/autopeering/transport" - "github.com/iotaledger/goshimmer/packages/database/mapdb" - "github.com/iotaledger/hive.go/logger" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/require" -) - -const graceTime = 5 * time.Millisecond - -var log = logger.NewExampleLogger("server") - -const ( - MPing MType = iota - MPong -) - -type Message interface { - Type() MType - Marshal() []byte -} - -type Ping struct{} -type Pong struct{} - -func (m *Ping) Type() MType { return MPing } -func (m *Ping) Marshal() []byte { return append([]byte{}, byte(MPing)) } - -func (m *Pong) Type() MType { return MPong } -func (m *Pong) Marshal() []byte { return append([]byte{}, byte(MPong)) } - -func sendPong(args mock.Arguments) { - srv := args.Get(0).(*Server) - addr := args.Get(1).(string) - srv.Send(addr, new(Pong).Marshal()) -} - -var ( - pingMock *mock.Mock - pongMock *mock.Mock -) - -func setupTest() func(t *testing.T) { - pingMock = new(mock.Mock) - pongMock = new(mock.Mock) - - return func(t *testing.T) { - pingMock.AssertExpectations(t) - pingMock = nil - pongMock.AssertExpectations(t) - pongMock = nil - } -} - -func handle(s *Server, fromAddr string, fromID peer.ID, fromKey peer.PublicKey, data []byte) (bool, error) { - msg, err := unmarshal(data) - if err != nil { - return false, err - } - - switch msg.Type() { - case MPing: - pingMock.Called(s, fromAddr, fromID, fromKey, data) - - case MPong: - if s.IsExpectedReply(fromAddr, fromID, MPong, msg) { - pongMock.Called(s, fromAddr, fromID, fromKey, data) - } - - default: - panic("unknown message type") - } - - return true, nil -} - -func unmarshal(data []byte) (Message, error) { - if len(data) != 1 { - return nil, ErrInvalidMessage - } - - switch MType(data[0]) { - case MPing: - return new(Ping), nil - case MPong: - return new(Pong), nil - } - return nil, ErrInvalidMessage -} - -func newTestDB(t require.TestingT) *peer.DB { - db, err := peer.NewDB(mapdb.NewMapDB()) - require.NoError(t, err) - return db -} - -func TestSrvEncodeDecodePing(t *testing.T) { - services := service.New() - services.Update(service.PeeringKey, "dummy", "local") - local, err := peer.NewLocal(services, newTestDB(t)) - require.NoError(t, err) - s := &Server{local: local} - - ping := new(Ping) - packet := s.encode(ping.Marshal()) - - data, key, err := decode(packet) - require.NoError(t, err) - - msg, _ := unmarshal(data) - assert.Equal(t, local.ID(), key.ID()) - assert.Equal(t, msg, ping) -} - -func newTestServer(t require.TestingT, name string, trans transport.Transport) (*Server, func()) { - l := log.Named(name) - - services := service.New() - services.Update(service.PeeringKey, trans.LocalAddr().Network(), trans.LocalAddr().String()) - local, err := peer.NewLocal(services, newTestDB(t)) - require.NoError(t, err) - - s, _ := salt.NewSalt(100 * time.Second) - local.SetPrivateSalt(s) - s, _ = salt.NewSalt(100 * time.Second) - local.SetPublicSalt(s) - - srv := Serve(local, trans, l, HandlerFunc(handle)) - - return srv, srv.Close -} - -func sendPing(s *Server, p *peer.Peer) error { - ping := new(Ping) - isPong := func(msg interface{}) bool { - _, ok := msg.(*Pong) - return ok - } - - errc := s.SendExpectingReply(p.Address(), p.ID(), ping.Marshal(), MPong, isPong) - return <-errc -} - -func TestPingPong(t *testing.T) { - p2p := transport.P2P() - - srvA, closeA := newTestServer(t, "A", p2p.A) - defer closeA() - srvB, closeB := newTestServer(t, "B", p2p.B) - defer closeB() - - peerA := &srvA.Local().Peer - peerB := &srvB.Local().Peer - - t.Run("A->B", func(t *testing.T) { - defer setupTest()(t) - - // B expects valid ping from A and sends pong back - pingMock.On("handle", srvB, peerA.Address(), peerA.ID(), peerA.PublicKey(), mock.Anything).Run(sendPong).Once() - // A expects valid pong from B - pongMock.On("handle", srvA, peerB.Address(), peerB.ID(), peerB.PublicKey(), mock.Anything).Once() - - assert.NoError(t, sendPing(srvA, peerB)) - time.Sleep(graceTime) - - }) - - t.Run("B->A", func(t *testing.T) { - defer setupTest()(t) - - pingMock.On("handle", srvA, peerB.Address(), peerB.ID(), peerB.PublicKey(), mock.Anything).Run(sendPong).Once() // A expects valid ping from B and sends pong back - pongMock.On("handle", srvB, peerA.Address(), peerA.ID(), peerA.PublicKey(), mock.Anything).Once() // B expects valid pong from A - - assert.NoError(t, sendPing(srvB, peerA)) - time.Sleep(graceTime) - }) -} - -func TestSrvPingTimeout(t *testing.T) { - defer setupTest()(t) - - p2p := transport.P2P() - - srvA, closeA := newTestServer(t, "A", p2p.A) - defer closeA() - srvB, closeB := newTestServer(t, "B", p2p.B) - closeB() - - peerB := &srvB.Local().Peer - assert.EqualError(t, sendPing(srvA, peerB), ErrTimeout.Error()) -} - -func TestUnexpectedPong(t *testing.T) { - defer setupTest()(t) - - p2p := transport.P2P() - - srvA, closeA := newTestServer(t, "A", p2p.A) - defer closeA() - srvB, closeB := newTestServer(t, "B", p2p.B) - defer closeB() - - // there should never be a Ping.Handle - // there should never be a Pong.Handle - - srvA.Send(srvB.LocalAddr().String(), new(Pong).Marshal()) -} diff --git a/packages/autopeering/transport/chan.go b/packages/autopeering/transport/chan.go deleted file mode 100644 index d26a8258..00000000 --- a/packages/autopeering/transport/chan.go +++ /dev/null @@ -1,116 +0,0 @@ -package transport - -import ( - "io" - "net" - "sync" -) - -// ChanNetwork offers in-memory transfers between an arbitrary number of clients. -type ChanNetwork struct { - peers map[string]*chanPeer -} - -type chanPeer struct { - network *ChanNetwork - addr chanAddr - - c chan transfer - closeOnce sync.Once - closing chan struct{} -} - -// chanAddr represents the address of an end point in the in-memory transport network. -type chanAddr struct { - address string -} - -func (a chanAddr) Network() string { return "chan-network" } -func (a chanAddr) String() string { return a.address } - -// NewNetwork creates a new in-memory transport network. -// For each provided address a corresponding client is created. -func NewNetwork(addresses ...string) *ChanNetwork { - network := &ChanNetwork{ - peers: make(map[string]*chanPeer, len(addresses)), - } - - for _, addr := range addresses { - network.AddTransport(addr) - } - - return network -} - -// AddTransport adds a new client transport layer to the network. -func (n *ChanNetwork) AddTransport(addr string) { - n.peers[addr] = newChanPeer(addr, n) -} - -// GetTransport returns the corresponding client transport layer for the provided address. -// This function will panic, if no transport layer for that address exists. -func (n *ChanNetwork) GetTransport(addr string) Transport { - peer, ok := n.peers[addr] - if !ok { - panic(errPeer.Error()) - } - - return peer -} - -// Close closes each of the peers' transport layers. -func (n *ChanNetwork) Close() { - for _, peer := range n.peers { - peer.Close() - } -} - -func newChanPeer(address string, network *ChanNetwork) *chanPeer { - return &chanPeer{ - addr: chanAddr{address: address}, - network: network, - c: make(chan transfer, queueSize), - closing: make(chan struct{}), - } -} - -// ReadFrom implements the Transport ReadFrom method. -func (p *chanPeer) ReadFrom() ([]byte, string, error) { - select { - case res := <-p.c: - return res.pkt, res.addr, nil - case <-p.closing: - return nil, "", io.EOF - } -} - -// WriteTo implements the Transport WriteTo method. -func (p *chanPeer) WriteTo(pkt []byte, address string) error { - // determine the receiving peer - peer, ok := p.network.peers[address] - if !ok { - return errPeer - } - - // clone the packet before sending, just to make sure... - req := transfer{pkt: append([]byte{}, pkt...), addr: p.addr.address} - - select { - case peer.c <- req: - return nil - case <-p.closing: - return errClosed - } -} - -// Close closes the transport layer. -func (p *chanPeer) Close() { - p.closeOnce.Do(func() { - close(p.closing) - }) -} - -// LocalAddr returns the local network address. -func (p *chanPeer) LocalAddr() net.Addr { - return p.addr -} diff --git a/packages/autopeering/transport/chan_test.go b/packages/autopeering/transport/chan_test.go deleted file mode 100644 index 09aa04dd..00000000 --- a/packages/autopeering/transport/chan_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package transport - -import ( - "io" - "sync" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestChanReadClosed(t *testing.T) { - network := NewNetwork("A") - defer network.Close() - - a := network.GetTransport("A") - a.Close() - _, _, err := a.ReadFrom() - assert.EqualError(t, err, io.EOF.Error()) -} - -func TestChanPacket(t *testing.T) { - network := NewNetwork("A", "B") - defer network.Close() - - a := network.GetTransport("A") - b := network.GetTransport("B") - - err := a.WriteTo(testPacket, b.LocalAddr().String()) - require.NoError(t, err) - - pkt, addr, err := b.ReadFrom() - require.NoError(t, err) - - assert.Equal(t, pkt, testPacket) - assert.Equal(t, addr, a.LocalAddr().String()) -} - -func TestChanConcurrentWrite(t *testing.T) { - network := NewNetwork("A", "B", "C", "D") - defer network.Close() - - a := network.GetTransport("A") - b := network.GetTransport("B") - c := network.GetTransport("C") - d := network.GetTransport("D") - - var wg sync.WaitGroup - numSender := 3 - - // reader - wg.Add(1) - go func() { - defer wg.Done() - for i := 0; i < numSender*1000; i++ { - _, _, err := d.ReadFrom() - assert.Equal(t, err, nil) - } - }() - - wg.Add(numSender) - burstWriteTo(a, d.LocalAddr().String(), 1000, &wg) - burstWriteTo(b, d.LocalAddr().String(), 1000, &wg) - burstWriteTo(c, d.LocalAddr().String(), 1000, &wg) - - // wait for everything to finish - wg.Wait() -} - -func burstWriteTo(t Transport, addr string, numPackets int, wg *sync.WaitGroup) { - defer wg.Done() - - go func() { - for i := 0; i < numPackets; i++ { - _ = t.WriteTo(testPacket, addr) - } - }() -} diff --git a/packages/autopeering/transport/conn.go b/packages/autopeering/transport/conn.go deleted file mode 100644 index 1d67a76e..00000000 --- a/packages/autopeering/transport/conn.go +++ /dev/null @@ -1,58 +0,0 @@ -package transport - -import ( - "io" - "net" - "strings" -) - -// ResolveFunc resolves a string address to the corresponding net.Addr. -type ResolveFunc func(network, address string) (net.Addr, error) - -// TransportConn wraps a PacketConn my un-/marshaling the packets using protobuf. -type TransportConn struct { - conn net.PacketConn - res ResolveFunc -} - -// Conn creates a new transport layer by using the underlying PacketConn. -func Conn(conn net.PacketConn, res ResolveFunc) *TransportConn { - return &TransportConn{conn: conn, res: res} -} - -// ReadFrom implements the Transport ReadFrom method. -func (t *TransportConn) ReadFrom() ([]byte, string, error) { - b := make([]byte, MaxPacketSize) - n, addr, err := t.conn.ReadFrom(b) - if err != nil { - // make ErrNetClosing handled consistently - if strings.Contains(err.Error(), "use of closed network connection") { - err = io.EOF - } - return nil, "", err - } - - return b[:n], addr.String(), nil -} - -// WriteTo implements the Transport WriteTo method. -func (t *TransportConn) WriteTo(pkt []byte, address string) error { - network := t.conn.LocalAddr().Network() - addr, err := t.res(network, address) - if err != nil { - return err - } - - _, err = t.conn.WriteTo(pkt, addr) - return err -} - -// Close closes the transport layer. -func (t *TransportConn) Close() { - _ = t.conn.Close() -} - -// LocalAddr returns the local network address. -func (t *TransportConn) LocalAddr() net.Addr { - return t.conn.LocalAddr() -} diff --git a/packages/autopeering/transport/conn_test.go b/packages/autopeering/transport/conn_test.go deleted file mode 100644 index 3066fae3..00000000 --- a/packages/autopeering/transport/conn_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package transport - -import ( - "io" - "net" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestConnUdpClosed(t *testing.T) { - conn := openUDP(t) - - conn.Close() - _, _, err := conn.ReadFrom() - assert.EqualError(t, err, io.EOF.Error()) -} - -func TestConnUdpPacket(t *testing.T) { - a := openUDP(t) - defer a.Close() - b := openUDP(t) - defer b.Close() - - err := a.WriteTo(testPacket, b.LocalAddr().String()) - require.NoError(t, err) - - pkt, addr, err := b.ReadFrom() - require.NoError(t, err) - - assert.Equal(t, pkt, testPacket) - assert.Equal(t, addr, a.LocalAddr().String()) -} - -func openUDP(t *testing.T) *TransportConn { - c, err := net.ListenPacket("udp", "127.0.0.1:0") - require.NoError(t, err) - - return Conn(c, func(network, address string) (net.Addr, error) { return net.ResolveUDPAddr(network, address) }) -} diff --git a/packages/autopeering/transport/const.go b/packages/autopeering/transport/const.go deleted file mode 100644 index afff55cb..00000000 --- a/packages/autopeering/transport/const.go +++ /dev/null @@ -1,5 +0,0 @@ -package transport - -const ( - queueSize = 5 -) diff --git a/packages/autopeering/transport/errors.go b/packages/autopeering/transport/errors.go deleted file mode 100644 index bb9ea0c1..00000000 --- a/packages/autopeering/transport/errors.go +++ /dev/null @@ -1,8 +0,0 @@ -package transport - -import "errors" - -var ( - errClosed = errors.New("socket closed") - errPeer = errors.New("could not determine peer") -) diff --git a/packages/autopeering/transport/p2p.go b/packages/autopeering/transport/p2p.go deleted file mode 100644 index 1bb3bb6f..00000000 --- a/packages/autopeering/transport/p2p.go +++ /dev/null @@ -1,93 +0,0 @@ -package transport - -import ( - "io" - "net" - "sync" -) - -// TransportP2P offers transfers between exactly two clients. -type TransportP2P struct { - A, B Transport -} - -// P2P creates a new in-memory two clients transport network. -// All writes in one client will always be received by the other client, no -// matter what address was specified. -func P2P() *TransportP2P { - chanA := make(chan transfer, queueSize) - chanB := make(chan transfer, queueSize) - - return &TransportP2P{ - A: newChanTransport(chanA, chanB, "A"), - B: newChanTransport(chanB, chanA, "B"), - } -} - -// Close closes each of the two client transport layers. -func (t *TransportP2P) Close() { - t.A.Close() - t.B.Close() -} - -// p2pAddr represents the address of an p2p end point. -type p2pAddr struct { - address string -} - -func (a p2pAddr) Network() string { return "p2p" } -func (a p2pAddr) String() string { return a.address } - -// chanTransport implements Transport by reading and writing to given channels. -type chanTransport struct { - in <-chan transfer - out chan<- transfer - addr p2pAddr - - closeOnce sync.Once - closing chan struct{} -} - -func newChanTransport(in <-chan transfer, out chan<- transfer, address string) *chanTransport { - return &chanTransport{ - in: in, - out: out, - addr: p2pAddr{address: address}, - closing: make(chan struct{}), - } -} - -// ReadFrom implements the Transport ReadFrom method. -func (t *chanTransport) ReadFrom() ([]byte, string, error) { - select { - case res := <-t.in: - return res.pkt, res.addr, nil - case <-t.closing: - return nil, "", io.EOF - } -} - -// WriteTo implements the Transport WriteTo method. -func (t *chanTransport) WriteTo(pkt []byte, _ string) error { - // clone the packet before sending, just to make sure... - req := transfer{pkt: append([]byte{}, pkt...), addr: t.addr.address} - - select { - case t.out <- req: - return nil - case <-t.closing: - return errClosed - } -} - -// Close closes the transport layer. -func (t *chanTransport) Close() { - t.closeOnce.Do(func() { - close(t.closing) - }) -} - -// LocalAddr returns the local network address. -func (t *chanTransport) LocalAddr() net.Addr { - return t.addr -} diff --git a/packages/autopeering/transport/p2p_test.go b/packages/autopeering/transport/p2p_test.go deleted file mode 100644 index 78a454cc..00000000 --- a/packages/autopeering/transport/p2p_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package transport - -import ( - "io" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -var testPacket = []byte("TEST") - -func TestP2PReadClosed(t *testing.T) { - p2p := P2P() - defer p2p.Close() - - p2p.A.Close() - _, _, err := p2p.A.ReadFrom() - assert.EqualError(t, err, io.EOF.Error()) -} - -func TestP2PPacket(t *testing.T) { - p2p := P2P() - defer p2p.Close() - - err := p2p.A.WriteTo(testPacket, p2p.B.LocalAddr().String()) - require.NoError(t, err) - - pkt, addr, err := p2p.B.ReadFrom() - require.NoError(t, err) - - assert.Equal(t, pkt, testPacket) - assert.Equal(t, addr, p2p.A.LocalAddr().String()) -} diff --git a/packages/autopeering/transport/transport.go b/packages/autopeering/transport/transport.go deleted file mode 100644 index 53a295e9..00000000 --- a/packages/autopeering/transport/transport.go +++ /dev/null @@ -1,37 +0,0 @@ -// Package transport provides implementations for simple address-based packet -// transfers. -package transport - -import ( - "net" -) - -const ( - // MaxPacketSize specifies the maximum allowed size of packets. - // Packets larger than this will be cut and thus treated as invalid. - MaxPacketSize = 1280 -) - -// Transport is generic network connection to transfer protobuf packages. -// Multiple goroutines may invoke methods on a Conn simultaneously. -type Transport interface { - // ReadFrom reads a packet from the connection. It returns the package and - // the return address for that package in string form. - ReadFrom() (pkt []byte, address string, err error) - - // WriteTo writes a packet to the string encoded target address. - WriteTo(pkt []byte, address string) error - - // Close closes the transport layer. - // Any blocked ReadFrom or WriteTo operations will return errors. - Close() - - // LocalAddr returns the local network address. - LocalAddr() net.Addr -} - -// transfer represents a send and contains the package and the return address. -type transfer struct { - pkt []byte - addr string -} diff --git a/packages/binary/identity/constants.go b/packages/binary/identity/constants.go new file mode 100644 index 00000000..b8fb9850 --- /dev/null +++ b/packages/binary/identity/constants.go @@ -0,0 +1,7 @@ +package identity + +const ( + PublicKeySize = 32 + PrivateKeySize = 64 + SignatureSize = 64 +) diff --git a/packages/binary/identity/identity.go b/packages/binary/identity/identity.go new file mode 100644 index 00000000..fd52c6f3 --- /dev/null +++ b/packages/binary/identity/identity.go @@ -0,0 +1,48 @@ +package identity + +import ( + "crypto/rand" + + "github.com/oasislabs/ed25519" +) + +type Identity struct { + Type Type + PublicKey []byte + PrivateKey []byte +} + +func New(publicKey []byte, optionalPrivateKey ...[]byte) *Identity { + this := &Identity{ + PublicKey: make([]byte, len(publicKey)), + } + + copy(this.PublicKey, publicKey) + + if len(optionalPrivateKey) == 0 { + this.Type = Public + } else { + this.Type = Private + this.PrivateKey = optionalPrivateKey[0] + } + + return this +} + +func Generate() *Identity { + if public, private, err := ed25519.GenerateKey(rand.Reader); err != nil { + panic(err) + } else { + return New(public, private) + } +} + +func (identity *Identity) Sign(data []byte) (sig []byte) { + sig = ed25519.Sign(identity.PrivateKey, data) + + return +} + +func (identity *Identity) VerifySignature(data []byte, signature []byte) bool { + return ed25519.Verify(identity.PublicKey, data, signature) +} diff --git a/packages/binary/identity/identity_test.go b/packages/binary/identity/identity_test.go new file mode 100644 index 00000000..f5fc0d52 --- /dev/null +++ b/packages/binary/identity/identity_test.go @@ -0,0 +1,41 @@ +package identity + +import ( + "sync" + "testing" + + "github.com/panjf2000/ants/v2" + + "github.com/stretchr/testify/assert" +) + +func BenchmarkIdentity_VerifySignature(b *testing.B) { + identity := Generate() + data := []byte("TESTDATA") + signature := identity.Sign(data) + + var wg sync.WaitGroup + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + wg.Add(1) + + _ = ants.Submit(func() { + identity.VerifySignature(data, signature) + + wg.Done() + }) + } + + wg.Wait() +} + +func Test(t *testing.T) { + identity := Generate() + + signature := identity.Sign([]byte("TESTDATA1")) + + assert.Equal(t, true, identity.VerifySignature([]byte("TESTDATA1"), signature)) + assert.Equal(t, false, identity.VerifySignature([]byte("TESTDATA2"), signature)) +} diff --git a/packages/binary/identity/type.go b/packages/binary/identity/type.go new file mode 100644 index 00000000..cc207b02 --- /dev/null +++ b/packages/binary/identity/type.go @@ -0,0 +1,8 @@ +package identity + +type Type int + +const ( + Private = Type(0) + Public = Type(1) +) diff --git a/packages/binary/signature/ed25119/ed25119.go b/packages/binary/signature/ed25119/ed25119.go new file mode 100644 index 00000000..d4b65997 --- /dev/null +++ b/packages/binary/signature/ed25119/ed25119.go @@ -0,0 +1,18 @@ +package ed25119 + +import ( + "crypto/rand" + + "github.com/oasislabs/ed25519" +) + +func GenerateKeyPair() (keyPair KeyPair) { + if public, private, err := ed25519.GenerateKey(rand.Reader); err != nil { + panic(err) + } else { + copy(keyPair.PrivateKey[:], private) + copy(keyPair.PublicKey[:], public) + + return + } +} diff --git a/packages/binary/signature/ed25119/key_pair.go b/packages/binary/signature/ed25119/key_pair.go new file mode 100644 index 00000000..ad0c31bb --- /dev/null +++ b/packages/binary/signature/ed25119/key_pair.go @@ -0,0 +1,6 @@ +package ed25119 + +type KeyPair struct { + PrivateKey PrivateKey + PublicKey PublicKey +} diff --git a/packages/binary/signature/ed25119/private_key.go b/packages/binary/signature/ed25119/private_key.go new file mode 100644 index 00000000..89dc1a15 --- /dev/null +++ b/packages/binary/signature/ed25119/private_key.go @@ -0,0 +1,15 @@ +package ed25119 + +import ( + "github.com/oasislabs/ed25519" +) + +type PrivateKey [PrivateKeySize]byte + +func (privateKey PrivateKey) Sign(data []byte) (result Signature) { + copy(result[:], ed25519.Sign(privateKey[:], data)) + + return +} + +const PrivateKeySize = 64 diff --git a/packages/binary/signature/ed25119/public_key.go b/packages/binary/signature/ed25119/public_key.go new file mode 100644 index 00000000..c9d58e25 --- /dev/null +++ b/packages/binary/signature/ed25119/public_key.go @@ -0,0 +1,25 @@ +package ed25119 + +import ( + "errors" + + "github.com/oasislabs/ed25519" +) + +type PublicKey [PublicKeySize]byte + +func (publicKey PublicKey) VerifySignature(data []byte, signature Signature) bool { + return ed25519.Verify(publicKey[:], data, signature[:]) +} + +func (publicKey *PublicKey) UnmarshalBinary(bytes []byte) (err error) { + if len(bytes) < PublicKeySize { + return errors.New("not enough bytes") + } + + copy(publicKey[:], bytes[:]) + + return +} + +const PublicKeySize = 32 diff --git a/packages/binary/signature/ed25119/signature.go b/packages/binary/signature/ed25119/signature.go new file mode 100644 index 00000000..bd33e113 --- /dev/null +++ b/packages/binary/signature/ed25119/signature.go @@ -0,0 +1,19 @@ +package ed25119 + +import ( + "errors" +) + +type Signature [SignatureSize]byte + +func (signature *Signature) UnmarshalBinary(bytes []byte) (err error) { + if len(bytes) < SignatureSize { + return errors.New("not enough bytes") + } + + copy(signature[:], bytes[:]) + + return +} + +const SignatureSize = 64 diff --git a/packages/binary/storageprefix/storageprefix.go b/packages/binary/storageprefix/storageprefix.go new file mode 100644 index 00000000..1e55f2db --- /dev/null +++ b/packages/binary/storageprefix/storageprefix.go @@ -0,0 +1,17 @@ +package storageprefix + +var ( + TangleTransaction = []byte{0} + TangleTransactionMetadata = []byte{1} + TangleApprovers = []byte{2} + TangleMissingTransaction = []byte{3} + + ValueTangleTransferMetadata = []byte{4} + ValueTangleConsumers = []byte{5} + ValueTangleMissingTransfers = []byte{6} + + LedgerStateTransferOutput = []byte{7} + LedgerStateTransferOutputBooking = []byte{8} + LedgerStateReality = []byte{9} + LedgerStateConflictSet = []byte{10} +) diff --git a/packages/binary/tangle/events.go b/packages/binary/tangle/events.go new file mode 100644 index 00000000..d2bf3552 --- /dev/null +++ b/packages/binary/tangle/events.go @@ -0,0 +1,39 @@ +package tangle + +import ( + "github.com/iotaledger/hive.go/events" + + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" +) + +type Events struct { + TransactionAttached *events.Event + TransactionSolid *events.Event + MissingTransactionReceived *events.Event + TransactionMissing *events.Event + TransactionUnsolidifiable *events.Event + TransactionRemoved *events.Event +} + +func newEvents() *Events { + return &Events{ + TransactionAttached: events.NewEvent(cachedTransactionEvent), + TransactionSolid: events.NewEvent(cachedTransactionEvent), + MissingTransactionReceived: events.NewEvent(cachedTransactionEvent), + TransactionMissing: events.NewEvent(transactionIdEvent), + TransactionUnsolidifiable: events.NewEvent(transactionIdEvent), + TransactionRemoved: events.NewEvent(transactionIdEvent), + } +} + +func transactionIdEvent(handler interface{}, params ...interface{}) { + handler.(func(transaction.Id))(params[0].(transaction.Id)) +} + +func cachedTransactionEvent(handler interface{}, params ...interface{}) { + handler.(func(*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata))( + params[0].(*transaction.CachedTransaction).Retain(), + params[1].(*transactionmetadata.CachedTransactionMetadata).Retain().(*transactionmetadata.CachedTransactionMetadata), + ) +} diff --git a/packages/binary/tangle/model/approver/approver.go b/packages/binary/tangle/model/approver/approver.go new file mode 100644 index 00000000..b08bf342 --- /dev/null +++ b/packages/binary/tangle/model/approver/approver.go @@ -0,0 +1,59 @@ +package approver + +import ( + "github.com/iotaledger/hive.go/objectstorage" + + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" +) + +type Approver struct { + objectstorage.StorableObjectFlags + + storageKey []byte + referencedTransaction transaction.Id + approvingTransaction transaction.Id +} + +func New(referencedTransaction transaction.Id, approvingTransaction transaction.Id) *Approver { + approver := &Approver{ + storageKey: make([]byte, transaction.IdLength+transaction.IdLength), + referencedTransaction: referencedTransaction, + approvingTransaction: approvingTransaction, + } + + copy(approver.storageKey[:transaction.IdLength], referencedTransaction[:]) + copy(approver.storageKey[transaction.IdLength:], approvingTransaction[:]) + + return approver +} + +func FromStorage(id []byte) (result objectstorage.StorableObject) { + approver := &Approver{ + storageKey: make([]byte, transaction.IdLength+transaction.IdLength), + } + copy(approver.referencedTransaction[:], id[:transaction.IdLength]) + copy(approver.approvingTransaction[:], id[transaction.IdLength:]) + copy(approver.storageKey, id) + + return approver +} + +func (approver *Approver) GetStorageKey() []byte { + return approver.storageKey +} + +func (approver *Approver) GetApprovingTransactionId() transaction.Id { + return approver.approvingTransaction +} + +func (approver *Approver) Update(other objectstorage.StorableObject) { + panic("approvers should never be overwritten and only stored once to optimize IO") +} + +func (approver *Approver) MarshalBinary() (result []byte, err error) { + return +} + +func (approver *Approver) UnmarshalBinary(data []byte) (err error) { + return +} diff --git a/packages/binary/tangle/model/approver/cached_approver.go b/packages/binary/tangle/model/approver/cached_approver.go new file mode 100644 index 00000000..c5284432 --- /dev/null +++ b/packages/binary/tangle/model/approver/cached_approver.go @@ -0,0 +1,33 @@ +package approver + +import ( + "github.com/iotaledger/hive.go/objectstorage" +) + +type CachedApprover struct { + objectstorage.CachedObject +} + +func (cachedApprover *CachedApprover) Unwrap() *Approver { + if untypedObject := cachedApprover.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*Approver); typedObject == nil || typedObject.IsDeleted() { + return nil + } else { + return typedObject + } + } +} + +type CachedApprovers []*CachedApprover + +func (cachedApprovers CachedApprovers) Consume(consumer func(approver *Approver)) (consumed bool) { + for _, cachedApprover := range cachedApprovers { + consumed = consumed || cachedApprover.Consume(func(object objectstorage.StorableObject) { + consumer(object.(*Approver)) + }) + } + + return +} diff --git a/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go b/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go new file mode 100644 index 00000000..f50f5321 --- /dev/null +++ b/packages/binary/tangle/model/missingtransaction/cached_missingtransaction.go @@ -0,0 +1,21 @@ +package missingtransaction + +import ( + "github.com/iotaledger/hive.go/objectstorage" +) + +type CachedMissingTransaction struct { + objectstorage.CachedObject +} + +func (cachedObject *CachedMissingTransaction) Unwrap() *MissingTransaction { + if untypedObject := cachedObject.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*MissingTransaction); typedObject == nil || typedObject.IsDeleted() { + return nil + } else { + return typedObject + } + } +} diff --git a/packages/binary/tangle/model/missingtransaction/missingtransaction.go b/packages/binary/tangle/model/missingtransaction/missingtransaction.go new file mode 100644 index 00000000..ea1715c8 --- /dev/null +++ b/packages/binary/tangle/model/missingtransaction/missingtransaction.go @@ -0,0 +1,53 @@ +package missingtransaction + +import ( + "time" + + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" + "github.com/iotaledger/hive.go/objectstorage" +) + +type MissingTransaction struct { + objectstorage.StorableObjectFlags + + transactionId transaction.Id + missingSince time.Time +} + +func New(transactionId transaction.Id) *MissingTransaction { + return &MissingTransaction{ + transactionId: transactionId, + missingSince: time.Now(), + } +} + +func FromStorage(key []byte) objectstorage.StorableObject { + result := &MissingTransaction{} + copy(result.transactionId[:], key) + + return result +} + +func (missingTransaction *MissingTransaction) GetTransactionId() transaction.Id { + return missingTransaction.transactionId +} + +func (missingTransaction *MissingTransaction) GetMissingSince() time.Time { + return missingTransaction.missingSince +} + +func (missingTransaction *MissingTransaction) GetStorageKey() []byte { + return missingTransaction.transactionId[:] +} + +func (missingTransaction *MissingTransaction) Update(other objectstorage.StorableObject) { + panic("missing transactions should never be overwritten and only stored once to optimize IO") +} + +func (missingTransaction *MissingTransaction) MarshalBinary() (result []byte, err error) { + return missingTransaction.missingSince.MarshalBinary() +} + +func (missingTransaction *MissingTransaction) UnmarshalBinary(data []byte) (err error) { + return missingTransaction.missingSince.UnmarshalBinary(data) +} diff --git a/packages/binary/tangle/model/transaction/cached_transaction.go b/packages/binary/tangle/model/transaction/cached_transaction.go new file mode 100644 index 00000000..5f177ea7 --- /dev/null +++ b/packages/binary/tangle/model/transaction/cached_transaction.go @@ -0,0 +1,31 @@ +package transaction + +import ( + "github.com/iotaledger/hive.go/objectstorage" +) + +type CachedTransaction struct { + objectstorage.CachedObject +} + +func (cachedTransaction *CachedTransaction) Retain() *CachedTransaction { + return &CachedTransaction{cachedTransaction.CachedObject.Retain()} +} + +func (cachedTransaction *CachedTransaction) Consume(consumer func(object *Transaction)) bool { + return cachedTransaction.CachedObject.Consume(func(object objectstorage.StorableObject) { + consumer(object.(*Transaction)) + }) +} + +func (cachedTransaction *CachedTransaction) Unwrap() *Transaction { + if untypedTransaction := cachedTransaction.Get(); untypedTransaction == nil { + return nil + } else { + if typeCastedTransaction := untypedTransaction.(*Transaction); typeCastedTransaction == nil || typeCastedTransaction.IsDeleted() { + return nil + } else { + return typeCastedTransaction + } + } +} diff --git a/packages/binary/tangle/model/transaction/id.go b/packages/binary/tangle/model/transaction/id.go new file mode 100644 index 00000000..dd5ca154 --- /dev/null +++ b/packages/binary/tangle/model/transaction/id.go @@ -0,0 +1,34 @@ +package transaction + +import ( + "github.com/mr-tron/base58" +) + +type Id [IdLength]byte + +func NewId(id []byte) (result Id) { + copy(result[:], id) + + return +} + +func (id *Id) MarshalBinary() (result []byte, err error) { + result = make([]byte, IdLength) + copy(result, id[:]) + + return +} + +func (id *Id) UnmarshalBinary(data []byte) (err error) { + copy(id[:], data) + + return +} + +func (id Id) String() string { + return base58.Encode(id[:]) +} + +var EmptyId = Id{} + +const IdLength = 64 diff --git a/packages/binary/tangle/model/transaction/init.go b/packages/binary/tangle/model/transaction/init.go new file mode 100644 index 00000000..5805588c --- /dev/null +++ b/packages/binary/tangle/model/transaction/init.go @@ -0,0 +1,10 @@ +package transaction + +import ( + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data" +) + +func init() { + payload.SetGenericUnmarshalerFactory(data.GenericPayloadUnmarshalerFactory) +} diff --git a/packages/binary/tangle/model/transaction/payload/data/data.go b/packages/binary/tangle/model/transaction/payload/data/data.go new file mode 100644 index 00000000..8b41b99c --- /dev/null +++ b/packages/binary/tangle/model/transaction/payload/data/data.go @@ -0,0 +1,52 @@ +package data + +import ( + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload" +) + +type Data struct { + payloadType payload.Type + data []byte +} + +var Type = payload.Type(0) + +func New(data []byte) *Data { + return &Data{ + payloadType: Type, + data: data, + } +} + +func (dataPayload *Data) GetType() payload.Type { + return dataPayload.payloadType +} + +func (dataPayload *Data) GetData() []byte { + return dataPayload.data +} + +func (dataPayload *Data) UnmarshalBinary(data []byte) error { + dataPayload.data = make([]byte, len(data)) + copy(dataPayload.data, data) + + return nil +} + +func (dataPayload *Data) MarshalBinary() (data []byte, err error) { + data = make([]byte, len(dataPayload.data)) + copy(data, dataPayload.data) + + return +} + +func GenericPayloadUnmarshalerFactory(payloadType payload.Type) payload.Unmarshaler { + return func(data []byte) (payload payload.Payload, err error) { + payload = &Data{ + payloadType: payloadType, + } + err = payload.UnmarshalBinary(data) + + return + } +} diff --git a/packages/binary/tangle/model/transaction/payload/data/init.go b/packages/binary/tangle/model/transaction/payload/data/init.go new file mode 100644 index 00000000..547bca2c --- /dev/null +++ b/packages/binary/tangle/model/transaction/payload/data/init.go @@ -0,0 +1,9 @@ +package data + +import ( + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload" +) + +func init() { + payload.RegisterType(Type, GenericPayloadUnmarshalerFactory(Type)) +} diff --git a/packages/binary/tangle/model/transaction/payload/id.go b/packages/binary/tangle/model/transaction/payload/id.go new file mode 100644 index 00000000..a20ce75f --- /dev/null +++ b/packages/binary/tangle/model/transaction/payload/id.go @@ -0,0 +1,5 @@ +package payload + +type Id [IdLength]byte + +const IdLength = 64 diff --git a/packages/binary/tangle/model/transaction/payload/payload.go b/packages/binary/tangle/model/transaction/payload/payload.go new file mode 100644 index 00000000..23ff09be --- /dev/null +++ b/packages/binary/tangle/model/transaction/payload/payload.go @@ -0,0 +1,12 @@ +package payload + +import ( + "encoding" +) + +type Payload interface { + encoding.BinaryMarshaler + encoding.BinaryUnmarshaler + + GetType() Type +} diff --git a/packages/binary/tangle/model/transaction/payload/type.go b/packages/binary/tangle/model/transaction/payload/type.go new file mode 100644 index 00000000..a1594aa4 --- /dev/null +++ b/packages/binary/tangle/model/transaction/payload/type.go @@ -0,0 +1,3 @@ +package payload + +type Type = uint32 diff --git a/packages/binary/tangle/model/transaction/payload/type_register.go b/packages/binary/tangle/model/transaction/payload/type_register.go new file mode 100644 index 00000000..aac9a919 --- /dev/null +++ b/packages/binary/tangle/model/transaction/payload/type_register.go @@ -0,0 +1,36 @@ +package payload + +import ( + "sync" +) + +type Unmarshaler func(data []byte) (Payload, error) + +var ( + typeRegister = make(map[Type]Unmarshaler) + typeRegisterMutex sync.RWMutex + genericUnmarshalerFactory func(payloadType Type) Unmarshaler +) + +func RegisterType(payloadType Type, unmarshaler Unmarshaler) { + typeRegisterMutex.Lock() + typeRegister[payloadType] = unmarshaler + typeRegisterMutex.Unlock() +} + +func GetUnmarshaler(payloadType Type) Unmarshaler { + typeRegisterMutex.RLock() + if unmarshaler, exists := typeRegister[payloadType]; exists { + typeRegisterMutex.RUnlock() + + return unmarshaler + } else { + typeRegisterMutex.RUnlock() + + return genericUnmarshalerFactory(payloadType) + } +} + +func SetGenericUnmarshalerFactory(unmarshalerFactory func(payloadType Type) Unmarshaler) { + genericUnmarshalerFactory = unmarshalerFactory +} diff --git a/packages/binary/tangle/model/transaction/test/transaction_test.go b/packages/binary/tangle/model/transaction/test/transaction_test.go new file mode 100644 index 00000000..1ca51d61 --- /dev/null +++ b/packages/binary/tangle/model/transaction/test/transaction_test.go @@ -0,0 +1,77 @@ +package test + +import ( + "runtime" + "sync" + "testing" + + "github.com/iotaledger/hive.go/async" + + "github.com/panjf2000/ants/v2" + + "github.com/iotaledger/goshimmer/packages/binary/identity" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data" +) + +func BenchmarkVerifyDataTransactions(b *testing.B) { + var pool async.WorkerPool + pool.Tune(runtime.NumCPU() * 2) + + transactions := make([][]byte, b.N) + for i := 0; i < b.N; i++ { + tx := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("some data"))) + + if marshaledTransaction, err := tx.MarshalBinary(); err != nil { + b.Error(err) + } else { + transactions[i] = marshaledTransaction + } + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + currentIndex := i + pool.Submit(func() { + if tx, err := transaction.FromBytes(transactions[currentIndex]); err != nil { + b.Error(err) + } else { + tx.VerifySignature() + } + }) + } + + pool.Shutdown() +} + +func BenchmarkVerifySignature(b *testing.B) { + pool, _ := ants.NewPool(80, ants.WithNonblocking(false)) + + transactions := make([]*transaction.Transaction, b.N) + for i := 0; i < b.N; i++ { + transactions[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("test"))) + transactions[i].GetBytes() + } + + var wg sync.WaitGroup + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + wg.Add(1) + + currentIndex := i + if err := pool.Submit(func() { + transactions[currentIndex].VerifySignature() + + wg.Done() + }); err != nil { + b.Error(err) + + return + } + } + + wg.Wait() +} diff --git a/packages/binary/tangle/model/transaction/transaction.go b/packages/binary/tangle/model/transaction/transaction.go new file mode 100644 index 00000000..de18ffc4 --- /dev/null +++ b/packages/binary/tangle/model/transaction/transaction.go @@ -0,0 +1,277 @@ +package transaction + +import ( + "encoding/binary" + "sync" + + "github.com/iotaledger/hive.go/stringify" + + "github.com/iotaledger/goshimmer/packages/binary/identity" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload" + + "github.com/iotaledger/hive.go/objectstorage" + + "github.com/mr-tron/base58" + + "golang.org/x/crypto/blake2b" +) + +type Transaction struct { + // base functionality of StorableObject + objectstorage.StorableObjectFlags + + // core properties (they are part of the transaction when being sent) + trunkTransactionId Id + branchTransactionId Id + issuer *identity.Identity + payload payload.Payload + bytes []byte + bytesMutex sync.RWMutex + signature [identity.SignatureSize]byte + signatureMutex sync.RWMutex + + // derived properties + id *Id + idMutex sync.RWMutex + payloadId *payload.Id + payloadIdMutex sync.RWMutex +} + +// Allows us to "issue" a transaction. +func New(trunkTransactionId Id, branchTransactionId Id, issuer *identity.Identity, payload payload.Payload) (result *Transaction) { + return &Transaction{ + trunkTransactionId: trunkTransactionId, + branchTransactionId: branchTransactionId, + issuer: issuer, + payload: payload, + } +} + +// Get's called when we restore a transaction from storage. The bytes and the content will be unmarshaled by an external +// caller (the objectStorage factory). +func FromStorage(id []byte) (result objectstorage.StorableObject) { + var transactionId Id + copy(transactionId[:], id) + + result = &Transaction{ + id: &transactionId, + } + + return +} + +func FromBytes(bytes []byte) (result *Transaction, err error) { + result = &Transaction{} + err = result.UnmarshalBinary(bytes) + + return +} + +func (transaction *Transaction) VerifySignature() (result bool) { + transactionBytes := transaction.GetBytes() + + transaction.signatureMutex.RLock() + result = transaction.issuer.VerifySignature(transactionBytes[:len(transactionBytes)-identity.SignatureSize], transaction.signature[:]) + transaction.signatureMutex.RUnlock() + + return +} + +func (transaction *Transaction) GetId() (result Id) { + transaction.idMutex.RLock() + if transaction.id == nil { + transaction.idMutex.RUnlock() + + transaction.idMutex.Lock() + if transaction.id == nil { + result = transaction.calculateTransactionId() + + transaction.id = &result + } else { + result = *transaction.id + } + transaction.idMutex.Unlock() + } else { + result = *transaction.id + + transaction.idMutex.RUnlock() + } + + return +} + +func (transaction *Transaction) GetTrunkTransactionId() Id { + return transaction.trunkTransactionId +} + +func (transaction *Transaction) GetBranchTransactionId() Id { + return transaction.branchTransactionId +} + +func (transaction *Transaction) GetPayload() payload.Payload { + return transaction.payload +} + +func (transaction *Transaction) GetPayloadId() (result payload.Id) { + transaction.payloadIdMutex.RLock() + if transaction.payloadId == nil { + transaction.payloadIdMutex.RUnlock() + + transaction.payloadIdMutex.Lock() + if transaction.payloadId == nil { + result = transaction.calculatePayloadId() + + transaction.payloadId = &result + } else { + result = *transaction.payloadId + } + transaction.payloadIdMutex.Unlock() + } else { + result = *transaction.payloadId + + transaction.payloadIdMutex.RUnlock() + } + + return +} + +func (transaction *Transaction) GetBytes() []byte { + if result, err := transaction.MarshalBinary(); err != nil { + panic(err) + } else { + return result + } +} + +func (transaction *Transaction) calculateTransactionId() Id { + payloadId := transaction.GetPayloadId() + + hashBase := make([]byte, IdLength+IdLength+payload.IdLength) + offset := 0 + + copy(hashBase[offset:], transaction.trunkTransactionId[:]) + offset += IdLength + + copy(hashBase[offset:], transaction.branchTransactionId[:]) + offset += IdLength + + copy(hashBase[offset:], payloadId[:]) + // offset += payloadIdLength + + return blake2b.Sum512(hashBase) +} + +func (transaction *Transaction) calculatePayloadId() payload.Id { + bytes := transaction.GetBytes() + + return blake2b.Sum512(bytes[2*IdLength:]) +} + +// Since transactions are immutable and do not get changed after being created, we cache the result of the marshaling. +func (transaction *Transaction) MarshalBinary() (result []byte, err error) { + transaction.bytesMutex.RLock() + if transaction.bytes == nil { + transaction.bytesMutex.RUnlock() + + transaction.bytesMutex.Lock() + if transaction.bytes == nil { + var serializedPayload []byte + if transaction.payload != nil { + if serializedPayload, err = transaction.payload.MarshalBinary(); err != nil { + return + } + } + serializedPayloadLength := len(serializedPayload) + + result = make([]byte, IdLength+IdLength+identity.PublicKeySize+4+serializedPayloadLength+identity.SignatureSize) + offset := 0 + + copy(result[offset:], transaction.trunkTransactionId[:]) + offset += IdLength + + copy(result[offset:], transaction.branchTransactionId[:]) + offset += IdLength + + if transaction.issuer != nil { + copy(result[offset:], transaction.issuer.PublicKey) + } + offset += identity.PublicKeySize + + binary.LittleEndian.PutUint32(result[offset:], transaction.payload.GetType()) + offset += 4 + + if serializedPayloadLength != 0 { + copy(result[offset:], serializedPayload) + offset += serializedPayloadLength + } + + if transaction.issuer != nil { + transaction.signatureMutex.Lock() + copy(transaction.signature[:], transaction.issuer.Sign(result[:offset])) + transaction.signatureMutex.Unlock() + copy(result[offset:], transaction.signature[:]) + } + // offset += identity.SignatureSize + + transaction.bytes = result + } else { + result = transaction.bytes + } + transaction.bytesMutex.Unlock() + } else { + result = transaction.bytes + + transaction.bytesMutex.RUnlock() + } + + return +} + +func (transaction *Transaction) UnmarshalBinary(data []byte) (err error) { + offset := 0 + + copy(transaction.trunkTransactionId[:], data[offset:]) + offset += IdLength + + copy(transaction.branchTransactionId[:], data[offset:]) + offset += IdLength + + transaction.issuer = identity.New(data[offset : offset+identity.PublicKeySize]) + offset += identity.PublicKeySize + + payloadType := binary.LittleEndian.Uint32(data[offset:]) + offset += 4 + + if transaction.payload, err = payload.GetUnmarshaler(payloadType)(data[offset : len(data)-identity.SignatureSize]); err != nil { + return + } + offset += len(data) - identity.SignatureSize - offset + + copy(transaction.signature[:], data[offset:]) + // offset += identity.SignatureSize + + transaction.bytes = make([]byte, len(data)) + copy(transaction.bytes, data) + + return +} + +func (transaction *Transaction) GetStorageKey() []byte { + transactionId := transaction.GetId() + + return transactionId[:] +} + +func (transaction *Transaction) Update(other objectstorage.StorableObject) { + panic("transactions should never be overwritten and only stored once to optimize IO") +} + +func (transaction *Transaction) String() string { + transactionId := transaction.GetId() + + return stringify.Struct("Transaction", + stringify.StructField("id", base58.Encode(transactionId[:])), + stringify.StructField("trunkTransactionId", base58.Encode(transaction.trunkTransactionId[:])), + stringify.StructField("trunkTransactionId", base58.Encode(transaction.branchTransactionId[:])), + ) +} diff --git a/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go new file mode 100644 index 00000000..8b327806 --- /dev/null +++ b/packages/binary/tangle/model/transactionmetadata/cached_transactionmetadata.go @@ -0,0 +1,25 @@ +package transactionmetadata + +import ( + "github.com/iotaledger/hive.go/objectstorage" +) + +type CachedTransactionMetadata struct { + objectstorage.CachedObject +} + +func (cachedObject *CachedTransactionMetadata) Retain() objectstorage.CachedObject { + return &CachedTransactionMetadata{cachedObject} +} + +func (cachedObject *CachedTransactionMetadata) Unwrap() *TransactionMetadata { + if untypedObject := cachedObject.Get(); untypedObject == nil { + return nil + } else { + if typedObject := untypedObject.(*TransactionMetadata); typedObject == nil || typedObject.IsDeleted() { + return nil + } else { + return typedObject + } + } +} diff --git a/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go new file mode 100644 index 00000000..f29a6e34 --- /dev/null +++ b/packages/binary/tangle/model/transactionmetadata/transactionmetadata.go @@ -0,0 +1,94 @@ +package transactionmetadata + +import ( + "sync" + "time" + + "github.com/iotaledger/hive.go/objectstorage" + + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" +) + +type TransactionMetadata struct { + objectstorage.StorableObjectFlags + + transactionId transaction.Id + receivedTime time.Time + solid bool + solidificationTime time.Time + + solidMutex sync.RWMutex + solidificationTimeMutex sync.RWMutex +} + +func New(transactionId transaction.Id) *TransactionMetadata { + return &TransactionMetadata{ + transactionId: transactionId, + receivedTime: time.Now(), + } +} + +func FromStorage(id []byte) objectstorage.StorableObject { + result := &TransactionMetadata{} + copy(result.transactionId[:], id) + + return result +} + +func (transactionMetadata *TransactionMetadata) IsSolid() (result bool) { + transactionMetadata.solidMutex.RLock() + result = transactionMetadata.solid + transactionMetadata.solidMutex.RUnlock() + + return +} + +func (transactionMetadata *TransactionMetadata) SetSolid(solid bool) (modified bool) { + transactionMetadata.solidMutex.RLock() + if transactionMetadata.solid != solid { + transactionMetadata.solidMutex.RUnlock() + + transactionMetadata.solidMutex.Lock() + if transactionMetadata.solid != solid { + transactionMetadata.solid = solid + if solid { + transactionMetadata.solidificationTimeMutex.Lock() + transactionMetadata.solidificationTime = time.Now() + transactionMetadata.solidificationTimeMutex.Unlock() + } + + transactionMetadata.SetModified() + + modified = true + } + transactionMetadata.solidMutex.Unlock() + + } else { + transactionMetadata.solidMutex.RUnlock() + } + + return +} + +func (transactionMetadata *TransactionMetadata) GetSoldificationTime() time.Time { + transactionMetadata.solidificationTimeMutex.RLock() + defer transactionMetadata.solidificationTimeMutex.RUnlock() + + return transactionMetadata.solidificationTime +} + +func (transactionMetadata *TransactionMetadata) GetStorageKey() []byte { + return transactionMetadata.transactionId[:] +} + +func (transactionMetadata *TransactionMetadata) Update(other objectstorage.StorableObject) { + +} + +func (transactionMetadata *TransactionMetadata) MarshalBinary() ([]byte, error) { + return nil, nil +} + +func (transactionMetadata *TransactionMetadata) UnmarshalBinary([]byte) error { + return nil +} diff --git a/packages/binary/tangle/tangle.go b/packages/binary/tangle/tangle.go new file mode 100644 index 00000000..18593704 --- /dev/null +++ b/packages/binary/tangle/tangle.go @@ -0,0 +1,323 @@ +package tangle + +import ( + "container/list" + "time" + + "github.com/dgraph-io/badger/v2" + "github.com/iotaledger/hive.go/types" + + "github.com/iotaledger/goshimmer/packages/binary/storageprefix" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/approver" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/missingtransaction" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" + + "github.com/iotaledger/hive.go/async" + "github.com/iotaledger/hive.go/objectstorage" +) + +const ( + MAX_MISSING_TIME_BEFORE_CLEANUP = 30 * time.Second + MISSING_CHECK_INTERVAL = 5 * time.Second +) + +type Tangle struct { + storageId []byte + + transactionStorage *objectstorage.ObjectStorage + transactionMetadataStorage *objectstorage.ObjectStorage + approverStorage *objectstorage.ObjectStorage + missingTransactionsStorage *objectstorage.ObjectStorage + + Events Events + + storeTransactionsWorkerPool async.WorkerPool + solidifierWorkerPool async.WorkerPool + cleanupWorkerPool async.WorkerPool +} + +// Constructor for the tangle. +func New(badgerInstance *badger.DB, storageId []byte) (result *Tangle) { + result = &Tangle{ + storageId: storageId, + transactionStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransaction...), transaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), + transactionMetadataStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleTransactionMetadata...), transactionmetadata.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), + approverStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleApprovers...), approver.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.PartitionKey(transaction.IdLength, transaction.IdLength), objectstorage.LeakDetectionEnabled(false)), + missingTransactionsStorage: objectstorage.New(badgerInstance, append(storageId, storageprefix.TangleMissingTransaction...), missingtransaction.FromStorage, objectstorage.CacheTime(10*time.Second), objectstorage.LeakDetectionEnabled(false)), + + Events: *newEvents(), + } + + result.solidifierWorkerPool.Tune(1024) + + return +} + +// Returns the storage id of this tangle (can be used to create ontologies that follow the storage of the main tangle). +func (tangle *Tangle) GetStorageId() []byte { + return tangle.storageId +} + +// Attaches a new transaction to the tangle. +func (tangle *Tangle) AttachTransaction(transaction *transaction.Transaction) { + tangle.storeTransactionsWorkerPool.Submit(func() { tangle.storeTransactionWorker(transaction) }) +} + +// Retrieves a transaction from the tangle. +func (tangle *Tangle) GetTransaction(transactionId transaction.Id) *transaction.CachedTransaction { + return &transaction.CachedTransaction{CachedObject: tangle.transactionStorage.Load(transactionId[:])} +} + +// Retrieves the metadata of a transaction from the tangle. +func (tangle *Tangle) GetTransactionMetadata(transactionId transaction.Id) *transactionmetadata.CachedTransactionMetadata { + return &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Load(transactionId[:])} +} + +// Retrieves the approvers of a transaction from the tangle. +func (tangle *Tangle) GetApprovers(transactionId transaction.Id) approver.CachedApprovers { + approvers := make(approver.CachedApprovers, 0) + tangle.approverStorage.ForEach(func(key []byte, cachedObject objectstorage.CachedObject) bool { + approvers = append(approvers, &approver.CachedApprover{CachedObject: cachedObject}) + + return true + }, transactionId[:]) + + return approvers +} + +// Deletes a transaction from the tangle (i.e. for local snapshots) +func (tangle *Tangle) DeleteTransaction(transactionId transaction.Id) { + tangle.GetTransaction(transactionId).Consume(func(currentTransaction *transaction.Transaction) { + trunkTransactionId := currentTransaction.GetTrunkTransactionId() + tangle.deleteApprover(trunkTransactionId, transactionId) + + branchTransactionId := currentTransaction.GetBranchTransactionId() + if branchTransactionId != trunkTransactionId { + tangle.deleteApprover(branchTransactionId, transactionId) + } + + tangle.transactionMetadataStorage.Delete(transactionId[:]) + tangle.transactionStorage.Delete(transactionId[:]) + + tangle.Events.TransactionRemoved.Trigger(transactionId) + }) +} + +// Marks the tangle as stopped, so it will not accept any new transactions (waits for all backgroundTasks to finish. +func (tangle *Tangle) Shutdown() *Tangle { + tangle.storeTransactionsWorkerPool.ShutdownGracefully() + tangle.solidifierWorkerPool.ShutdownGracefully() + tangle.cleanupWorkerPool.ShutdownGracefully() + + tangle.transactionStorage.Shutdown() + tangle.transactionMetadataStorage.Shutdown() + tangle.approverStorage.Shutdown() + tangle.missingTransactionsStorage.Shutdown() + + return tangle +} + +// Resets the database and deletes all objects (good for testing or "node resets"). +func (tangle *Tangle) Prune() error { + for _, storage := range []*objectstorage.ObjectStorage{ + tangle.transactionStorage, + tangle.transactionMetadataStorage, + tangle.approverStorage, + tangle.missingTransactionsStorage, + } { + if err := storage.Prune(); err != nil { + return err + } + } + + return nil +} + +// Worker that stores the transactions and calls the corresponding "Storage events" +func (tangle *Tangle) storeTransactionWorker(tx *transaction.Transaction) { + // store transaction + var cachedTransaction *transaction.CachedTransaction + if _tmp, transactionIsNew := tangle.transactionStorage.StoreIfAbsent(tx); !transactionIsNew { + return + } else { + cachedTransaction = &transaction.CachedTransaction{CachedObject: _tmp} + } + + // store transaction metadata + transactionId := tx.GetId() + cachedTransactionMetadata := &transactionmetadata.CachedTransactionMetadata{CachedObject: tangle.transactionMetadataStorage.Store(transactionmetadata.New(transactionId))} + + // store trunk approver + trunkTransactionID := tx.GetTrunkTransactionId() + tangle.approverStorage.Store(approver.New(trunkTransactionID, transactionId)).Release() + + // store branch approver + if branchTransactionID := tx.GetBranchTransactionId(); branchTransactionID != trunkTransactionID { + tangle.approverStorage.Store(approver.New(branchTransactionID, transactionId)).Release() + } + + // trigger events + if tangle.missingTransactionsStorage.DeleteIfPresent(transactionId[:]) { + tangle.Events.MissingTransactionReceived.Trigger(cachedTransaction, cachedTransactionMetadata) + } + tangle.Events.TransactionAttached.Trigger(cachedTransaction, cachedTransactionMetadata) + + // check solidity + tangle.solidifierWorkerPool.Submit(func() { + tangle.solidifyTransactionWorker(cachedTransaction, cachedTransactionMetadata) + }) +} + +// Worker that solidifies the transactions (recursively from past to present). +func (tangle *Tangle) solidifyTransactionWorker(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { + isTransactionMarkedAsSolid := func(transactionId transaction.Id) bool { + if transactionId == transaction.EmptyId { + return true + } + + transactionMetadataCached := tangle.GetTransactionMetadata(transactionId) + if transactionMetadata := transactionMetadataCached.Unwrap(); transactionMetadata == nil { + transactionMetadataCached.Release() + + // if transaction is missing and was not reported as missing, yet + if cachedMissingTransaction, missingTransactionStored := tangle.missingTransactionsStorage.StoreIfAbsent(missingtransaction.New(transactionId)); missingTransactionStored { + cachedMissingTransaction.Consume(func(object objectstorage.StorableObject) { + tangle.monitorMissingTransactionWorker(object.(*missingtransaction.MissingTransaction).GetTransactionId()) + }) + } + + return false + } else if !transactionMetadata.IsSolid() { + transactionMetadataCached.Release() + + return false + } + transactionMetadataCached.Release() + + return true + } + + isTransactionSolid := func(transaction *transaction.Transaction, transactionMetadata *transactionmetadata.TransactionMetadata) bool { + if transaction == nil || transaction.IsDeleted() { + return false + } + + if transactionMetadata == nil || transactionMetadata.IsDeleted() { + return false + } + + if transactionMetadata.IsSolid() { + return true + } + + return isTransactionMarkedAsSolid(transaction.GetTrunkTransactionId()) && isTransactionMarkedAsSolid(transaction.GetBranchTransactionId()) + } + + popElementsFromStack := func(stack *list.List) (*transaction.CachedTransaction, *transactionmetadata.CachedTransactionMetadata) { + currentSolidificationEntry := stack.Front() + currentCachedTransaction := currentSolidificationEntry.Value.([2]interface{})[0] + currentCachedTransactionMetadata := currentSolidificationEntry.Value.([2]interface{})[1] + stack.Remove(currentSolidificationEntry) + + return currentCachedTransaction.(*transaction.CachedTransaction), currentCachedTransactionMetadata.(*transactionmetadata.CachedTransactionMetadata) + } + + // initialize the stack + solidificationStack := list.New() + solidificationStack.PushBack([2]interface{}{cachedTransaction, cachedTransactionMetadata}) + + // process transactions that are supposed to be checked for solidity recursively + for solidificationStack.Len() > 0 { + currentCachedTransaction, currentCachedTransactionMetadata := popElementsFromStack(solidificationStack) + + currentTransaction := currentCachedTransaction.Unwrap() + currentTransactionMetadata := currentCachedTransactionMetadata.Unwrap() + if currentTransaction == nil || currentTransactionMetadata == nil { + currentCachedTransaction.Release() + currentCachedTransactionMetadata.Release() + + continue + } + + // if current transaction is solid and was not marked as solid before: mark as solid and propagate + if isTransactionSolid(currentTransaction, currentTransactionMetadata) && currentTransactionMetadata.SetSolid(true) { + tangle.Events.TransactionSolid.Trigger(currentCachedTransaction, currentCachedTransactionMetadata) + + tangle.GetApprovers(currentTransaction.GetId()).Consume(func(approver *approver.Approver) { + approverTransactionId := approver.GetApprovingTransactionId() + + solidificationStack.PushBack([2]interface{}{ + tangle.GetTransaction(approverTransactionId), + tangle.GetTransactionMetadata(approverTransactionId), + }) + }) + } + + // release cached results + currentCachedTransaction.Release() + currentCachedTransactionMetadata.Release() + } +} + +// Worker that Monitors the missing transactions (by scheduling regular checks). +func (tangle *Tangle) monitorMissingTransactionWorker(transactionId transaction.Id) { + var scheduleNextMissingCheck func(transactionId transaction.Id) + scheduleNextMissingCheck = func(transactionId transaction.Id) { + time.AfterFunc(MISSING_CHECK_INTERVAL, func() { + tangle.missingTransactionsStorage.Load(transactionId[:]).Consume(func(object objectstorage.StorableObject) { + missingTransaction := object.(*missingtransaction.MissingTransaction) + + if time.Since(missingTransaction.GetMissingSince()) >= MAX_MISSING_TIME_BEFORE_CLEANUP { + tangle.cleanupWorkerPool.Submit(func() { + tangle.Events.TransactionUnsolidifiable.Trigger(transactionId) + + tangle.deleteSubtangle(missingTransaction.GetTransactionId()) + }) + } else { + // TRIGGER STILL MISSING EVENT? + + scheduleNextMissingCheck(transactionId) + } + }) + }) + } + + tangle.Events.TransactionMissing.Trigger(transactionId) + + scheduleNextMissingCheck(transactionId) +} + +func (tangle *Tangle) deleteApprover(approvedTransaction transaction.Id, approvingTransaction transaction.Id) { + idToDelete := make([]byte, transaction.IdLength+transaction.IdLength) + copy(idToDelete[:transaction.IdLength], approvedTransaction[:]) + copy(idToDelete[transaction.IdLength:], approvingTransaction[:]) + tangle.approverStorage.Delete(idToDelete) +} + +// Deletes a transaction and all of its approvers (recursively). +func (tangle *Tangle) deleteSubtangle(transactionId transaction.Id) { + cleanupStack := list.New() + cleanupStack.PushBack(transactionId) + + processedTransactions := make(map[transaction.Id]types.Empty) + processedTransactions[transactionId] = types.Void + + for cleanupStack.Len() >= 1 { + currentStackEntry := cleanupStack.Front() + currentTransactionId := currentStackEntry.Value.(transaction.Id) + cleanupStack.Remove(currentStackEntry) + + tangle.DeleteTransaction(currentTransactionId) + + tangle.GetApprovers(currentTransactionId).Consume(func(approver *approver.Approver) { + approverId := approver.GetApprovingTransactionId() + + if _, transactionProcessed := processedTransactions[approverId]; !transactionProcessed { + cleanupStack.PushBack(approverId) + + processedTransactions[approverId] = types.Void + } + }) + } +} diff --git a/packages/binary/tangle/tangle_test.go b/packages/binary/tangle/tangle_test.go new file mode 100644 index 00000000..c8ff887c --- /dev/null +++ b/packages/binary/tangle/tangle_test.go @@ -0,0 +1,95 @@ +package tangle + +import ( + "fmt" + "testing" + "time" + + "github.com/dgraph-io/badger/v2" + "github.com/iotaledger/hive.go/events" + + "github.com/iotaledger/hive.go/database" + + "github.com/iotaledger/goshimmer/packages/binary/identity" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transactionmetadata" + "github.com/iotaledger/goshimmer/plugins/config" +) + +var testDatabase *badger.DB + +var _ = config.PLUGIN + +func init() { + testDatabase = database.GetBadgerInstance() +} + +func BenchmarkTangle_AttachTransaction(b *testing.B) { + tangle := New(testDatabase, []byte("TEST_BINARY_TANGLE")) + if err := tangle.Prune(); err != nil { + b.Error(err) + + return + } + + testIdentity := identity.Generate() + + transactionBytes := make([]*transaction.Transaction, b.N) + for i := 0; i < b.N; i++ { + transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, testIdentity, data.New([]byte("some data"))) + transactionBytes[i].GetBytes() + } + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + tangle.AttachTransaction(transactionBytes[i]) + } + + tangle.Shutdown() +} + +func TestTangle_AttachTransaction(t *testing.T) { + tangle := New(testDatabase, []byte("TEST_BINARY_TANGLE")) + if err := tangle.Prune(); err != nil { + t.Error(err) + + return + } + + tangle.Events.TransactionAttached.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { + cachedTransaction.Consume(func(transaction *transaction.Transaction) { + fmt.Println("ATTACHED:", transaction.GetId()) + }) + })) + + tangle.Events.TransactionSolid.Attach(events.NewClosure(func(cachedTransaction *transaction.CachedTransaction, cachedTransactionMetadata *transactionmetadata.CachedTransactionMetadata) { + cachedTransaction.Consume(func(transaction *transaction.Transaction) { + fmt.Println("SOLID:", transaction.GetId()) + }) + })) + + tangle.Events.TransactionUnsolidifiable.Attach(events.NewClosure(func(transactionId transaction.Id) { + fmt.Println("UNSOLIDIFIABLE:", transactionId) + })) + + tangle.Events.TransactionMissing.Attach(events.NewClosure(func(transactionId transaction.Id) { + fmt.Println("MISSING:", transactionId) + })) + + tangle.Events.TransactionRemoved.Attach(events.NewClosure(func(transactionId transaction.Id) { + fmt.Println("REMOVED:", transactionId) + })) + + newTransaction1 := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("some data"))) + newTransaction2 := transaction.New(newTransaction1.GetId(), newTransaction1.GetId(), identity.Generate(), data.New([]byte("some other data"))) + + tangle.AttachTransaction(newTransaction2) + + time.Sleep(7 * time.Second) + + tangle.AttachTransaction(newTransaction1) + + tangle.Shutdown() +} diff --git a/packages/binary/tangle/transactionparser/builtinfilters/recently_seen_bytes_filter.go b/packages/binary/tangle/transactionparser/builtinfilters/recently_seen_bytes_filter.go new file mode 100644 index 00000000..c0a6002f --- /dev/null +++ b/packages/binary/tangle/transactionparser/builtinfilters/recently_seen_bytes_filter.go @@ -0,0 +1,68 @@ +package builtinfilters + +import ( + "sync" + + "github.com/iotaledger/hive.go/async" + "github.com/iotaledger/hive.go/bytesfilter" +) + +type RecentlySeenBytesFilter struct { + bytesFilter *bytesfilter.BytesFilter + onAcceptCallback func(bytes []byte) + onRejectCallback func(bytes []byte) + workerPool async.WorkerPool + + onAcceptCallbackMutex sync.RWMutex + onRejectCallbackMutex sync.RWMutex +} + +func NewRecentlySeenBytesFilter() (result *RecentlySeenBytesFilter) { + result = &RecentlySeenBytesFilter{ + bytesFilter: bytesfilter.New(100000), + } + + return +} + +func (filter *RecentlySeenBytesFilter) Filter(bytes []byte) { + filter.workerPool.Submit(func() { + if filter.bytesFilter.Add(bytes) { + filter.getAcceptCallback()(bytes) + } else { + filter.getRejectCallback()(bytes) + } + }) +} + +func (filter *RecentlySeenBytesFilter) OnAccept(callback func(bytes []byte)) { + filter.onAcceptCallbackMutex.Lock() + filter.onAcceptCallback = callback + filter.onAcceptCallbackMutex.Unlock() +} + +func (filter *RecentlySeenBytesFilter) OnReject(callback func(bytes []byte)) { + filter.onRejectCallbackMutex.Lock() + filter.onRejectCallback = callback + filter.onRejectCallbackMutex.Unlock() +} + +func (filter *RecentlySeenBytesFilter) getAcceptCallback() (result func(bytes []byte)) { + filter.onAcceptCallbackMutex.Lock() + result = filter.onAcceptCallback + filter.onAcceptCallbackMutex.Unlock() + + return +} + +func (filter *RecentlySeenBytesFilter) getRejectCallback() (result func(bytes []byte)) { + filter.onRejectCallbackMutex.Lock() + result = filter.onRejectCallback + filter.onRejectCallbackMutex.Unlock() + + return +} + +func (filter *RecentlySeenBytesFilter) Shutdown() { + filter.workerPool.ShutdownGracefully() +} diff --git a/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go b/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go new file mode 100644 index 00000000..9160d821 --- /dev/null +++ b/packages/binary/tangle/transactionparser/builtinfilters/transaction_signature_filter.go @@ -0,0 +1,66 @@ +package builtinfilters + +import ( + "sync" + + "github.com/iotaledger/hive.go/async" + + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" +) + +type TransactionSignatureFilter struct { + onAcceptCallback func(tx *transaction.Transaction) + onRejectCallback func(tx *transaction.Transaction) + workerPool async.WorkerPool + + onAcceptCallbackMutex sync.RWMutex + onRejectCallbackMutex sync.RWMutex +} + +func NewTransactionSignatureFilter() (result *TransactionSignatureFilter) { + result = &TransactionSignatureFilter{} + + return +} + +func (filter *TransactionSignatureFilter) Filter(tx *transaction.Transaction) { + filter.workerPool.Submit(func() { + if tx.VerifySignature() { + filter.getAcceptCallback()(tx) + } else { + filter.getRejectCallback()(tx) + } + }) +} + +func (filter *TransactionSignatureFilter) OnAccept(callback func(tx *transaction.Transaction)) { + filter.onAcceptCallbackMutex.Lock() + filter.onAcceptCallback = callback + filter.onAcceptCallbackMutex.Unlock() +} + +func (filter *TransactionSignatureFilter) OnReject(callback func(tx *transaction.Transaction)) { + filter.onRejectCallbackMutex.Lock() + filter.onRejectCallback = callback + filter.onRejectCallbackMutex.Unlock() +} + +func (filter *TransactionSignatureFilter) Shutdown() { + filter.workerPool.ShutdownGracefully() +} + +func (filter *TransactionSignatureFilter) getAcceptCallback() (result func(tx *transaction.Transaction)) { + filter.onAcceptCallbackMutex.RLock() + result = filter.onAcceptCallback + filter.onAcceptCallbackMutex.RUnlock() + + return +} + +func (filter *TransactionSignatureFilter) getRejectCallback() (result func(tx *transaction.Transaction)) { + filter.onRejectCallbackMutex.RLock() + result = filter.onRejectCallback + filter.onRejectCallbackMutex.RUnlock() + + return +} diff --git a/packages/binary/tangle/transactionparser/bytes_filter.go b/packages/binary/tangle/transactionparser/bytes_filter.go new file mode 100644 index 00000000..c8e2bab6 --- /dev/null +++ b/packages/binary/tangle/transactionparser/bytes_filter.go @@ -0,0 +1,8 @@ +package transactionparser + +type BytesFilter interface { + Filter(bytes []byte) + OnAccept(callback func(bytes []byte)) + OnReject(callback func(bytes []byte)) + Shutdown() +} diff --git a/packages/binary/tangle/transactionparser/events.go b/packages/binary/tangle/transactionparser/events.go new file mode 100644 index 00000000..9bde1a04 --- /dev/null +++ b/packages/binary/tangle/transactionparser/events.go @@ -0,0 +1,9 @@ +package transactionparser + +import "github.com/iotaledger/hive.go/events" + +type transactionParserEvents struct { + BytesRejected *events.Event + TransactionParsed *events.Event + TransactionRejected *events.Event +} diff --git a/packages/binary/tangle/transactionparser/transaction_filter.go b/packages/binary/tangle/transactionparser/transaction_filter.go new file mode 100644 index 00000000..39dfc277 --- /dev/null +++ b/packages/binary/tangle/transactionparser/transaction_filter.go @@ -0,0 +1,12 @@ +package transactionparser + +import ( + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" +) + +type TransactionFilter interface { + Filter(tx *transaction.Transaction) + OnAccept(callback func(tx *transaction.Transaction)) + OnReject(callback func(tx *transaction.Transaction)) + Shutdown() +} diff --git a/packages/binary/tangle/transactionparser/transactionparser.go b/packages/binary/tangle/transactionparser/transactionparser.go new file mode 100644 index 00000000..1d5f5366 --- /dev/null +++ b/packages/binary/tangle/transactionparser/transactionparser.go @@ -0,0 +1,137 @@ +package transactionparser + +import ( + "sync" + + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" + "github.com/iotaledger/goshimmer/packages/binary/tangle/transactionparser/builtinfilters" + + "github.com/iotaledger/hive.go/events" + "github.com/iotaledger/hive.go/typeutils" +) + +type TransactionParser struct { + bytesFilters []BytesFilter + transactionFilters []TransactionFilter + Events transactionParserEvents + + byteFiltersModified typeutils.AtomicBool + transactionFiltersModified typeutils.AtomicBool + bytesFiltersMutex sync.Mutex + transactionFiltersMutex sync.Mutex +} + +func New() (result *TransactionParser) { + result = &TransactionParser{ + bytesFilters: make([]BytesFilter, 0), + transactionFilters: make([]TransactionFilter, 0), + + Events: transactionParserEvents{ + BytesRejected: events.NewEvent(func(handler interface{}, params ...interface{}) { + handler.(func([]byte))(params[0].([]byte)) + }), + TransactionParsed: events.NewEvent(func(handler interface{}, params ...interface{}) { + handler.(func(*transaction.Transaction))(params[0].(*transaction.Transaction)) + }), + TransactionRejected: events.NewEvent(func(handler interface{}, params ...interface{}) { + handler.(func(*transaction.Transaction))(params[0].(*transaction.Transaction)) + }), + }, + } + + // add builtin filters + result.AddBytesFilter(builtinfilters.NewRecentlySeenBytesFilter()) + result.AddTransactionsFilter(builtinfilters.NewTransactionSignatureFilter()) + + return +} + +func (transactionParser *TransactionParser) Parse(transactionBytes []byte) { + transactionParser.setupBytesFilterDataFlow() + transactionParser.setupTransactionsFilterDataFlow() + + transactionParser.bytesFilters[0].Filter(transactionBytes) +} + +func (transactionParser *TransactionParser) AddBytesFilter(filter BytesFilter) { + transactionParser.bytesFiltersMutex.Lock() + transactionParser.bytesFilters = append(transactionParser.bytesFilters, filter) + transactionParser.bytesFiltersMutex.Unlock() + + transactionParser.byteFiltersModified.Set() +} + +func (transactionParser *TransactionParser) AddTransactionsFilter(filter TransactionFilter) { + transactionParser.transactionFiltersMutex.Lock() + transactionParser.transactionFilters = append(transactionParser.transactionFilters, filter) + transactionParser.transactionFiltersMutex.Unlock() + + transactionParser.transactionFiltersModified.Set() +} + +func (transactionParser *TransactionParser) Shutdown() { + transactionParser.bytesFiltersMutex.Lock() + for _, bytesFilter := range transactionParser.bytesFilters { + bytesFilter.Shutdown() + } + transactionParser.bytesFiltersMutex.Unlock() + + transactionParser.transactionFiltersMutex.Lock() + for _, transactionFilter := range transactionParser.transactionFilters { + transactionFilter.Shutdown() + } + transactionParser.transactionFiltersMutex.Unlock() +} + +func (transactionParser *TransactionParser) setupBytesFilterDataFlow() { + if !transactionParser.byteFiltersModified.IsSet() { + return + } + + transactionParser.bytesFiltersMutex.Lock() + if transactionParser.byteFiltersModified.IsSet() { + transactionParser.byteFiltersModified.SetTo(false) + + numberOfBytesFilters := len(transactionParser.bytesFilters) + for i := 0; i < numberOfBytesFilters; i++ { + if i == numberOfBytesFilters-1 { + transactionParser.bytesFilters[i].OnAccept(transactionParser.parseTransaction) + } else { + transactionParser.bytesFilters[i].OnAccept(transactionParser.bytesFilters[i+1].Filter) + } + transactionParser.bytesFilters[i].OnReject(func(bytes []byte) { transactionParser.Events.BytesRejected.Trigger(bytes) }) + } + } + transactionParser.bytesFiltersMutex.Unlock() +} + +func (transactionParser *TransactionParser) setupTransactionsFilterDataFlow() { + if !transactionParser.transactionFiltersModified.IsSet() { + return + } + + transactionParser.transactionFiltersMutex.Lock() + if transactionParser.transactionFiltersModified.IsSet() { + transactionParser.transactionFiltersModified.SetTo(false) + + numberOfTransactionFilters := len(transactionParser.transactionFilters) + for i := 0; i < numberOfTransactionFilters; i++ { + if i == numberOfTransactionFilters-1 { + transactionParser.transactionFilters[i].OnAccept(func(tx *transaction.Transaction) { transactionParser.Events.TransactionParsed.Trigger(tx) }) + } else { + transactionParser.transactionFilters[i].OnAccept(transactionParser.transactionFilters[i+1].Filter) + } + transactionParser.transactionFilters[i].OnReject(func(tx *transaction.Transaction) { transactionParser.Events.TransactionRejected.Trigger(tx) }) + } + } + transactionParser.transactionFiltersMutex.Unlock() +} + +func (transactionParser *TransactionParser) parseTransaction(bytes []byte) { + if parsedTransaction, err := transaction.FromBytes(bytes); err != nil { + // trigger parsingError + panic(err) + } else { + transactionParser.transactionFilters[0].Filter(parsedTransaction) + } +} diff --git a/packages/binary/tangle/transactionparser/transactionparser_test.go b/packages/binary/tangle/transactionparser/transactionparser_test.go new file mode 100644 index 00000000..c7dc3b18 --- /dev/null +++ b/packages/binary/tangle/transactionparser/transactionparser_test.go @@ -0,0 +1,56 @@ +package transactionparser + +import ( + "fmt" + "strconv" + "testing" + + "github.com/iotaledger/hive.go/events" + + "github.com/iotaledger/goshimmer/packages/binary/identity" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction/payload/data" +) + +func BenchmarkTransactionParser_ParseBytesSame(b *testing.B) { + txBytes := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("Test"))).GetBytes() + txParser := New() + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + txParser.Parse(txBytes) + } + + txParser.Shutdown() +} + +func BenchmarkTransactionParser_ParseBytesDifferent(b *testing.B) { + transactionBytes := make([][]byte, b.N) + for i := 0; i < b.N; i++ { + transactionBytes[i] = transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("Test"+strconv.Itoa(i)))).GetBytes() + } + + txParser := New() + + b.ResetTimer() + + for i := 0; i < b.N; i++ { + txParser.Parse(transactionBytes[i]) + } + + txParser.Shutdown() +} + +func TestTransactionParser_ParseTransaction(t *testing.T) { + tx := transaction.New(transaction.EmptyId, transaction.EmptyId, identity.Generate(), data.New([]byte("Test"))) + + txParser := New() + txParser.Parse(tx.GetBytes()) + + txParser.Events.TransactionParsed.Attach(events.NewClosure(func(tx *transaction.Transaction) { + fmt.Println("PARSED!!!") + })) + + txParser.Shutdown() +} diff --git a/packages/binary/tangle/transactionrequester/constants.go b/packages/binary/tangle/transactionrequester/constants.go new file mode 100644 index 00000000..b9f59769 --- /dev/null +++ b/packages/binary/tangle/transactionrequester/constants.go @@ -0,0 +1,10 @@ +package transactionrequester + +import ( + "time" +) + +const ( + DEFAULT_REQUEST_WORKER_COUNT = 1024 + DEFAULT_RETRY_INTERVAL = 10 * time.Second +) diff --git a/packages/binary/tangle/transactionrequester/events.go b/packages/binary/tangle/transactionrequester/events.go new file mode 100644 index 00000000..e845d790 --- /dev/null +++ b/packages/binary/tangle/transactionrequester/events.go @@ -0,0 +1,9 @@ +package transactionrequester + +import ( + "github.com/iotaledger/hive.go/events" +) + +type Events struct { + SendRequest *events.Event +} diff --git a/packages/binary/tangle/transactionrequester/options.go b/packages/binary/tangle/transactionrequester/options.go new file mode 100644 index 00000000..05db5db4 --- /dev/null +++ b/packages/binary/tangle/transactionrequester/options.go @@ -0,0 +1,37 @@ +package transactionrequester + +import ( + "time" +) + +type Options struct { + retryInterval time.Duration + workerCount int +} + +func newOptions(optionalOptions []Option) *Options { + result := &Options{ + retryInterval: 10 * time.Second, + workerCount: DEFAULT_REQUEST_WORKER_COUNT, + } + + for _, optionalOption := range optionalOptions { + optionalOption(result) + } + + return result +} + +type Option func(*Options) + +func RetryInterval(interval time.Duration) Option { + return func(args *Options) { + args.retryInterval = interval + } +} + +func WorkerCount(workerCount int) Option { + return func(args *Options) { + args.workerCount = workerCount + } +} diff --git a/packages/binary/tangle/transactionrequester/transactionrequester.go b/packages/binary/tangle/transactionrequester/transactionrequester.go new file mode 100644 index 00000000..6f036e81 --- /dev/null +++ b/packages/binary/tangle/transactionrequester/transactionrequester.go @@ -0,0 +1,74 @@ +package transactionrequester + +import ( + "sync" + "time" + + "github.com/iotaledger/hive.go/async" + "github.com/iotaledger/hive.go/events" + + "github.com/iotaledger/goshimmer/packages/binary/tangle/model/transaction" +) + +type TransactionRequester struct { + scheduledRequests map[transaction.Id]*time.Timer + requestWorker async.NonBlockingWorkerPool + options *Options + Events Events + + scheduledRequestsMutex sync.RWMutex +} + +func New(optionalOptions ...Option) *TransactionRequester { + requester := &TransactionRequester{ + scheduledRequests: make(map[transaction.Id]*time.Timer), + options: newOptions(optionalOptions), + Events: Events{ + SendRequest: events.NewEvent(func(handler interface{}, params ...interface{}) { + handler.(func(transaction.Id))(params[0].(transaction.Id)) + }), + }, + } + + requester.requestWorker.Tune(requester.options.workerCount) + + return requester +} + +func (requester *TransactionRequester) ScheduleRequest(transactionId transaction.Id) { + var retryRequest func(bool) + retryRequest = func(initialRequest bool) { + requester.requestWorker.Submit(func() { + requester.scheduledRequestsMutex.RLock() + if _, requestExists := requester.scheduledRequests[transactionId]; !initialRequest && !requestExists { + requester.scheduledRequestsMutex.RUnlock() + + return + } + requester.scheduledRequestsMutex.RUnlock() + + requester.Events.SendRequest.Trigger(transactionId) + + requester.scheduledRequestsMutex.Lock() + requester.scheduledRequests[transactionId] = time.AfterFunc(requester.options.retryInterval, func() { retryRequest(false) }) + requester.scheduledRequestsMutex.Unlock() + }) + } + + retryRequest(true) +} + +func (requester *TransactionRequester) StopRequest(transactionId transaction.Id) { + requester.scheduledRequestsMutex.RLock() + if timer, timerExists := requester.scheduledRequests[transactionId]; timerExists { + requester.scheduledRequestsMutex.RUnlock() + + timer.Stop() + + requester.scheduledRequestsMutex.Lock() + delete(requester.scheduledRequests, transactionId) + requester.scheduledRequestsMutex.Unlock() + } else { + requester.scheduledRequestsMutex.RUnlock() + } +} diff --git a/packages/gossip/common.go b/packages/gossip/common.go index 6f48f378..237e11ab 100644 --- a/packages/gossip/common.go +++ b/packages/gossip/common.go @@ -1,8 +1,8 @@ package gossip import ( - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" ) // IsSupported returns whether the peer supports the gossip service. diff --git a/packages/gossip/events.go b/packages/gossip/events.go index 5f96044a..96bec6af 100644 --- a/packages/gossip/events.go +++ b/packages/gossip/events.go @@ -1,7 +1,7 @@ package gossip import ( - "github.com/iotaledger/goshimmer/packages/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/events" ) diff --git a/packages/gossip/manager.go b/packages/gossip/manager.go index bc08ab56..49a9d0d9 100644 --- a/packages/gossip/manager.go +++ b/packages/gossip/manager.go @@ -7,9 +7,9 @@ import ( "sync" "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" pb "github.com/iotaledger/goshimmer/packages/gossip/proto" "github.com/iotaledger/goshimmer/packages/gossip/server" + "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/events" "go.uber.org/zap" ) diff --git a/packages/gossip/manager_test.go b/packages/gossip/manager_test.go index 07dda401..83dba46e 100644 --- a/packages/gossip/manager_test.go +++ b/packages/gossip/manager_test.go @@ -7,11 +7,11 @@ import ( "time" "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" "github.com/iotaledger/goshimmer/packages/database/mapdb" pb "github.com/iotaledger/goshimmer/packages/gossip/proto" "github.com/iotaledger/goshimmer/packages/gossip/server" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" "github.com/stretchr/testify/assert" diff --git a/packages/gossip/neighbor.go b/packages/gossip/neighbor.go index 07509a52..a529aab2 100644 --- a/packages/gossip/neighbor.go +++ b/packages/gossip/neighbor.go @@ -7,10 +7,11 @@ import ( "strings" "sync" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" "github.com/iotaledger/goshimmer/packages/netutil" "github.com/iotaledger/goshimmer/packages/netutil/buffconn" + "github.com/iotaledger/hive.go/autopeering/peer" "github.com/iotaledger/hive.go/logger" + "go.uber.org/atomic" ) var ( @@ -19,16 +20,18 @@ var ( ) const ( - neighborQueueSize = 5000 - maxNumReadErrors = 10 + neighborQueueSize = 5000 + maxNumReadErrors = 10 + droppedMessagesThreshold = 1000 ) type Neighbor struct { *peer.Peer *buffconn.BufferedConnection - log *logger.Logger - queue chan []byte + log *logger.Logger + queue chan []byte + messagesDropped atomic.Int32 wg sync.WaitGroup closing chan struct{} @@ -150,6 +153,10 @@ func (n *Neighbor) Write(b []byte) (int, error) { case <-n.closing: return 0, nil default: - return 0, ErrNeighborQueueFull + if n.messagesDropped.Inc() >= droppedMessagesThreshold { + n.messagesDropped.Store(0) + return 0, ErrNeighborQueueFull + } + return 0, nil } } diff --git a/packages/gossip/neighbor_test.go b/packages/gossip/neighbor_test.go index fd1e0fc7..36c61534 100644 --- a/packages/gossip/neighbor_test.go +++ b/packages/gossip/neighbor_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/events" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -87,8 +87,8 @@ func TestNeighborParallelWrite(t *testing.T) { go func() { defer wg.Done() for i := 0; i < neighborQueueSize; i++ { - _, err := neighborA.Write(testData) - if err == ErrNeighborQueueFull { + l, err := neighborA.Write(testData) + if err == ErrNeighborQueueFull || l == 0 { continue } assert.NoError(t, err) @@ -99,8 +99,8 @@ func TestNeighborParallelWrite(t *testing.T) { go func() { defer wg.Done() for i := 0; i < neighborQueueSize; i++ { - _, err := neighborA.Write(testData) - if err == ErrNeighborQueueFull { + l, err := neighborA.Write(testData) + if err == ErrNeighborQueueFull || l == 0 { continue } assert.NoError(t, err) diff --git a/packages/gossip/server/handshake.go b/packages/gossip/server/handshake.go index fb291c8e..719ff0de 100644 --- a/packages/gossip/server/handshake.go +++ b/packages/gossip/server/handshake.go @@ -5,8 +5,8 @@ import ( "time" "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/server" pb "github.com/iotaledger/goshimmer/packages/gossip/server/proto" + "github.com/iotaledger/hive.go/autopeering/server" ) const ( diff --git a/packages/gossip/server/server.go b/packages/gossip/server/server.go index c3853ac0..6185df51 100644 --- a/packages/gossip/server/server.go +++ b/packages/gossip/server/server.go @@ -12,10 +12,10 @@ import ( "time" "github.com/golang/protobuf/proto" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - pb "github.com/iotaledger/goshimmer/packages/autopeering/server/proto" "github.com/iotaledger/goshimmer/packages/netutil" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" + pb "github.com/iotaledger/hive.go/autopeering/server/proto" "github.com/iotaledger/hive.go/backoff" "go.uber.org/zap" ) diff --git a/packages/gossip/server/server_test.go b/packages/gossip/server/server_test.go index 84155297..d3e95418 100644 --- a/packages/gossip/server/server_test.go +++ b/packages/gossip/server/server_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" "github.com/iotaledger/goshimmer/packages/database/mapdb" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/logger" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/packages/model/meta_transaction/meta_transaction.go b/packages/model/meta_transaction/meta_transaction.go index 45550786..59c546ea 100644 --- a/packages/model/meta_transaction/meta_transaction.go +++ b/packages/model/meta_transaction/meta_transaction.go @@ -56,8 +56,11 @@ func FromTrits(trits trinary.Trits) *MetaTransaction { } } -func FromBytes(bytes []byte) (result *MetaTransaction) { +func FromBytes(bytes []byte) (result *MetaTransaction, err error) { trits := trinary.MustBytesToTrits(bytes) + if len(trits) < MARSHALED_TOTAL_SIZE { + return nil, fmt.Errorf("invalid size %v", len(trits)) + } result = FromTrits(trits[:MARSHALED_TOTAL_SIZE]) result.bytes = bytes diff --git a/packages/model/meta_transaction/meta_transaction_test.go b/packages/model/meta_transaction/meta_transaction_test.go index fa6ec565..7a349f2d 100644 --- a/packages/model/meta_transaction/meta_transaction_test.go +++ b/packages/model/meta_transaction/meta_transaction_test.go @@ -47,7 +47,9 @@ func TestMetaTransaction_SettersGetters(t *testing.T) { assert.Equal(t, tx.IsHead(), head) assert.Equal(t, tx.IsTail(), tail) assert.Equal(t, tx.GetTransactionType(), transactionType) - assert.Equal(t, tx.GetHash(), FromBytes(tx.GetBytes()).GetHash()) + metaTx, err := FromBytes(tx.GetBytes()) + require.NoError(t, err) + assert.Equal(t, tx.GetHash(), metaTx.GetHash()) assert.EqualValues(t, "KKDVHBENVLQUNO9WOWWEJPBBHUSYRSRKIMZWCFCDB9RYZKYWLAYWRIBRQETBFKE9TIVWQPCKFWAMCLCAV", tx.GetHash()) } diff --git a/plugins/analysis/client/plugin.go b/plugins/analysis/client/plugin.go index cbc41e18..fa962360 100644 --- a/plugins/analysis/client/plugin.go +++ b/plugins/analysis/client/plugin.go @@ -6,8 +6,6 @@ import ( "sync" "time" - "github.com/iotaledger/goshimmer/packages/autopeering/discover" - "github.com/iotaledger/goshimmer/packages/autopeering/selection" "github.com/iotaledger/goshimmer/packages/parameter" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/plugins/analysis/types/addnode" @@ -17,6 +15,8 @@ import ( "github.com/iotaledger/goshimmer/plugins/analysis/types/removenode" "github.com/iotaledger/goshimmer/plugins/autopeering" "github.com/iotaledger/goshimmer/plugins/autopeering/local" + "github.com/iotaledger/hive.go/autopeering/discover" + "github.com/iotaledger/hive.go/autopeering/selection" "github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" diff --git a/plugins/autopeering/autopeering.go b/plugins/autopeering/autopeering.go index ca3b3de9..191d8471 100644 --- a/plugins/autopeering/autopeering.go +++ b/plugins/autopeering/autopeering.go @@ -7,17 +7,17 @@ import ( "net" "strings" - "github.com/iotaledger/goshimmer/packages/autopeering/discover" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" - "github.com/iotaledger/goshimmer/packages/autopeering/selection" - "github.com/iotaledger/goshimmer/packages/autopeering/server" - "github.com/iotaledger/goshimmer/packages/autopeering/transport" "github.com/iotaledger/goshimmer/packages/netutil" "github.com/iotaledger/goshimmer/packages/parameter" "github.com/iotaledger/goshimmer/plugins/autopeering/local" "github.com/iotaledger/goshimmer/plugins/cli" "github.com/iotaledger/goshimmer/plugins/gossip" + "github.com/iotaledger/hive.go/autopeering/discover" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" + "github.com/iotaledger/hive.go/autopeering/selection" + "github.com/iotaledger/hive.go/autopeering/server" + "github.com/iotaledger/hive.go/autopeering/transport" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" ) diff --git a/plugins/autopeering/local/local.go b/plugins/autopeering/local/local.go index 65c71331..c9cb2efa 100644 --- a/plugins/autopeering/local/local.go +++ b/plugins/autopeering/local/local.go @@ -8,11 +8,11 @@ import ( "strings" "sync" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" "github.com/iotaledger/goshimmer/packages/database" "github.com/iotaledger/goshimmer/packages/netutil" "github.com/iotaledger/goshimmer/packages/parameter" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/logger" ) @@ -40,7 +40,7 @@ func configureLocal() *peer.Local { } } if !externalIP.IsGlobalUnicast() { - log.Fatalf("IP is not a global unicast address: %s", externalIP.String()) + log.Warnf("IP is not a global unicast address: %s", externalIP.String()) } peeringPort := strconv.Itoa(parameter.NodeConfig.GetInt(CFG_PORT)) diff --git a/plugins/autopeering/plugin.go b/plugins/autopeering/plugin.go index cad1ba41..c8e5003a 100644 --- a/plugins/autopeering/plugin.go +++ b/plugins/autopeering/plugin.go @@ -3,11 +3,11 @@ package autopeering import ( "time" - "github.com/iotaledger/goshimmer/packages/autopeering/discover" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/selection" "github.com/iotaledger/goshimmer/packages/gossip" "github.com/iotaledger/goshimmer/packages/shutdown" + "github.com/iotaledger/hive.go/autopeering/discover" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/selection" "github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" diff --git a/plugins/cli/plugin.go b/plugins/cli/plugin.go index 8fbaeab0..13c03bc5 100644 --- a/plugins/cli/plugin.go +++ b/plugins/cli/plugin.go @@ -2,6 +2,7 @@ package cli import ( "fmt" + "os" "github.com/iotaledger/goshimmer/packages/parameter" "github.com/iotaledger/hive.go/events" @@ -12,7 +13,7 @@ import ( const ( // AppVersion version number - AppVersion = "v0.1.1" + AppVersion = "v0.1.2" // AppName app code name AppName = "GoShimmer" ) @@ -43,10 +44,20 @@ func parseParameters() { } } +func PrintVersion() { + version := flag.BoolP("version", "v", false, "Prints the GoShimmer version") + flag.Parse() + if *version { + fmt.Println(AppName + " " + AppVersion) + os.Exit(0) + } +} + func LoadConfig() { if err := parameter.FetchConfig(false); err != nil { panic(err) } + parseParameters() if err := logger.InitGlobalLogger(parameter.NodeConfig); err != nil { diff --git a/plugins/config/config.go b/plugins/config/config.go new file mode 100644 index 00000000..70513880 --- /dev/null +++ b/plugins/config/config.go @@ -0,0 +1,64 @@ +package config + +import ( + "github.com/iotaledger/hive.go/logger" + "github.com/iotaledger/hive.go/node" + "github.com/iotaledger/hive.go/parameter" + flag "github.com/spf13/pflag" + "github.com/spf13/viper" +) + +var ( + // flags + configName = flag.StringP("config", "c", "config", "Filename of the config file without the file extension") + configDirPath = flag.StringP("config-dir", "d", ".", "Path to the directory containing the config file") + + // viper + NodeConfig *viper.Viper + + // logger + defaultLoggerConfig = logger.Config{ + Level: "info", + DisableCaller: false, + DisableStacktrace: false, + Encoding: "console", + OutputPaths: []string{"goshimmer.log"}, + DisableEvents: false, + } +) + +func init() { + // set the default logger config + NodeConfig = viper.New() + NodeConfig.SetDefault(logger.ViperKey, defaultLoggerConfig) + + if err := Fetch(false); err != nil { + panic(err) + } + parseParameters() +} + +func parseParameters() { + for _, pluginName := range NodeConfig.GetStringSlice(node.CFG_DISABLE_PLUGINS) { + node.DisabledPlugins[node.GetPluginIdentifier(pluginName)] = true + } + for _, pluginName := range NodeConfig.GetStringSlice(node.CFG_ENABLE_PLUGINS) { + node.EnabledPlugins[node.GetPluginIdentifier(pluginName)] = true + } +} + +// Fetch fetches config values from a dir defined via CLI flag --config-dir (or the current working dir if not set). +// +// It automatically reads in a single config file starting with "config" (can be changed via the --config CLI flag) +// and ending with: .json, .toml, .yaml or .yml (in this sequence). +func Fetch(printConfig bool, ignoreSettingsAtPrint ...[]string) error { + err := parameter.LoadConfigFile(NodeConfig, *configDirPath, *configName, true, true) + if err != nil { + return err + } + + if printConfig { + parameter.PrintConfig(NodeConfig, ignoreSettingsAtPrint...) + } + return nil +} diff --git a/plugins/config/plugin.go b/plugins/config/plugin.go new file mode 100644 index 00000000..0626fa17 --- /dev/null +++ b/plugins/config/plugin.go @@ -0,0 +1,12 @@ +package config + +import ( + "github.com/iotaledger/hive.go/node" +) + +// define the plugin as a placeholder, so the init methods get executed accordingly +var PLUGIN = node.NewPlugin("Config", node.Enabled, run) + +func run(ctx *node.Plugin) { + // do nothing; everything is handled in the init method +} diff --git a/plugins/gossip/gossip.go b/plugins/gossip/gossip.go index bdf1cca9..0293370e 100644 --- a/plugins/gossip/gossip.go +++ b/plugins/gossip/gossip.go @@ -6,14 +6,14 @@ import ( "strconv" "sync" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" gp "github.com/iotaledger/goshimmer/packages/gossip" "github.com/iotaledger/goshimmer/packages/gossip/server" "github.com/iotaledger/goshimmer/packages/parameter" "github.com/iotaledger/goshimmer/plugins/autopeering/local" "github.com/iotaledger/goshimmer/plugins/cli" "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/typeutils" "github.com/iotaledger/iota.go/trinary" diff --git a/plugins/gossip/plugin.go b/plugins/gossip/plugin.go index 20e804a1..ff4b335f 100644 --- a/plugins/gossip/plugin.go +++ b/plugins/gossip/plugin.go @@ -1,12 +1,12 @@ package gossip import ( - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/selection" "github.com/iotaledger/goshimmer/packages/gossip" "github.com/iotaledger/goshimmer/packages/model/value_transaction" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/plugins/tangle" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/selection" "github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" diff --git a/plugins/remotelog/plugin.go b/plugins/remotelog/plugin.go index 0ca02c50..97f48981 100644 --- a/plugins/remotelog/plugin.go +++ b/plugins/remotelog/plugin.go @@ -9,20 +9,29 @@ import ( "encoding/json" "fmt" "net" + "os" + "path/filepath" "runtime" "time" "github.com/iotaledger/goshimmer/packages/parameter" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/plugins/autopeering/local" + "github.com/iotaledger/goshimmer/plugins/cli" + "github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/events" "github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/node" "github.com/iotaledger/hive.go/workerpool" + + "gopkg.in/src-d/go-git.v4" ) type logMessage struct { + Version string `json:"version"` + GitHead string `json:"gitHead,omitempty"` + GitBranch string `json:"gitBranch,omitempty"` NodeId string `json:"nodeId"` Level string `json:"level"` Name string `json:"name"` @@ -37,11 +46,13 @@ const ( ) var ( - PLUGIN = node.NewPlugin(PLUGIN_NAME, node.Disabled, configure, run) - log *logger.Logger - conn net.Conn - myID string - workerPool *workerpool.WorkerPool + PLUGIN = node.NewPlugin(PLUGIN_NAME, node.Disabled, configure, run) + log *logger.Logger + conn net.Conn + myID string + myGitHead string + myGitBranch string + workerPool *workerpool.WorkerPool ) func configure(plugin *node.Plugin) { @@ -63,6 +74,8 @@ func configure(plugin *node.Plugin) { myID = hex.EncodeToString(local.GetInstance().ID().Bytes()) } + getGitInfo() + workerPool = workerpool.New(func(task workerpool.Task) { sendLogMsg(task.Param(0).(logger.Level), task.Param(1).(string), task.Param(2).(string)) @@ -87,7 +100,54 @@ func run(plugin *node.Plugin) { } func sendLogMsg(level logger.Level, name string, msg string) { - m := logMessage{myID, level.CapitalString(), name, msg, time.Now()} + m := logMessage{ + cli.AppVersion, + myGitHead, + myGitBranch, + myID, + level.CapitalString(), + name, + msg, + time.Now(), + } b, _ := json.Marshal(m) fmt.Fprint(conn, string(b)) } + +func getGitInfo() { + r, err := git.PlainOpen(getGitDir()) + if err != nil { + log.Debug("Could not open Git repo.") + return + } + + // extract git branch and head + if h, err := r.Head(); err == nil { + myGitBranch = h.Name().String() + myGitHead = h.Hash().String() + } +} + +func getGitDir() string { + var gitDir string + + // this is valid when running an executable, when using "go run" this is a temp path + if ex, err := os.Executable(); err == nil { + temp := filepath.Join(filepath.Dir(ex), ".git") + if _, err := os.Stat(temp); err == nil { + gitDir = temp + } + } + + // when running "go run" from the same directory + if gitDir == "" { + if wd, err := os.Getwd(); err == nil { + temp := filepath.Join(wd, ".git") + if _, err := os.Stat(temp); err == nil { + gitDir = temp + } + } + } + + return gitDir +} diff --git a/plugins/remotelog/server/docker-compose.yml b/plugins/remotelog/server/docker-compose.yml index f3c9db88..69ed87d8 100644 --- a/plugins/remotelog/server/docker-compose.yml +++ b/plugins/remotelog/server/docker-compose.yml @@ -13,7 +13,7 @@ services: source: elasticsearch target: /usr/share/elasticsearch/data environment: - ES_JAVA_OPTS: "-Xmx256m -Xms256m" + ES_JAVA_OPTS: "-Xmx2g -Xms2g" # Use single node discovery in order to disable production mode and avoid bootstrap checks # see https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html discovery.type: single-node @@ -35,7 +35,7 @@ services: ports: - "5213:5213/udp" environment: - LS_JAVA_OPTS: "-Xmx256m -Xms256m" + LS_JAVA_OPTS: "-Xmx1g -Xms1g" networks: - elk depends_on: diff --git a/plugins/spa/plugin.go b/plugins/spa/plugin.go index 829d3609..7141a88f 100644 --- a/plugins/spa/plugin.go +++ b/plugins/spa/plugin.go @@ -7,7 +7,6 @@ import ( "time" "github.com/gorilla/websocket" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" "github.com/iotaledger/goshimmer/packages/parameter" "github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/plugins/autopeering" @@ -15,6 +14,7 @@ import ( "github.com/iotaledger/goshimmer/plugins/cli" "github.com/iotaledger/goshimmer/plugins/gossip" "github.com/iotaledger/goshimmer/plugins/metrics" + "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/labstack/echo" "github.com/labstack/echo/middleware" diff --git a/plugins/tangle/solidifier.go b/plugins/tangle/solidifier.go index 02d84440..0c274c49 100644 --- a/plugins/tangle/solidifier.go +++ b/plugins/tangle/solidifier.go @@ -42,8 +42,12 @@ func configureSolidifier() { requestedTxs = NewUnsolidTxs() gossip.Events.TransactionReceived.Attach(events.NewClosure(func(ev *gossip.TransactionReceivedEvent) { - metaTx := meta_transaction.FromBytes(ev.Data) - if err := metaTx.Validate(); err != nil { + metaTx, err := meta_transaction.FromBytes(ev.Data) + if err != nil { + log.Warnf("invalid transaction: %s", err) + return + } + if err = metaTx.Validate(); err != nil { log.Warnf("invalid transaction: %s", err) return } diff --git a/plugins/tangle/solidifier_test.go b/plugins/tangle/solidifier_test.go index 28a63bc2..9cd09962 100644 --- a/plugins/tangle/solidifier_test.go +++ b/plugins/tangle/solidifier_test.go @@ -131,7 +131,11 @@ func TestTangle(t *testing.T) { // transactionReceived mocks the TransactionReceived event by allowing lower mwm func transactionReceived(ev *gossip.TransactionReceivedEvent) { - metaTx := meta_transaction.FromBytes(ev.Data) + metaTx, err := meta_transaction.FromBytes(ev.Data) + if err != nil { + log.Warnf("invalid transaction: %s", err) + return + } if metaTx.GetWeightMagnitude() < testMWM { log.Warnf("invalid weight magnitude: %d / %d", metaTx.GetWeightMagnitude(), testMWM) return diff --git a/plugins/webapi/getNeighbors/plugin.go b/plugins/webapi/getNeighbors/plugin.go index 1ac033ce..fe9c3971 100644 --- a/plugins/webapi/getNeighbors/plugin.go +++ b/plugins/webapi/getNeighbors/plugin.go @@ -4,10 +4,10 @@ import ( "encoding/base64" "net/http" - "github.com/iotaledger/goshimmer/packages/autopeering/peer" - "github.com/iotaledger/goshimmer/packages/autopeering/peer/service" "github.com/iotaledger/goshimmer/plugins/autopeering" "github.com/iotaledger/goshimmer/plugins/webapi" + "github.com/iotaledger/hive.go/autopeering/peer" + "github.com/iotaledger/hive.go/autopeering/peer/service" "github.com/iotaledger/hive.go/node" "github.com/labstack/echo" ) -- GitLab