From e47af4ca398c472a9d754c0480aa69d767cdf6d3 Mon Sep 17 00:00:00 2001 From: Brandon <32753167+btoms20@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:40:16 -0800 Subject: [PATCH 1/7] Added license, ignore and template files --- .license_header_template | 13 +++++++++++++ .licenseignore | 10 ++++++++++ LICENSE.md | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 .license_header_template create mode 100644 .licenseignore create mode 100644 LICENSE.md diff --git a/.license_header_template b/.license_header_template new file mode 100644 index 0000000..31a6e18 --- /dev/null +++ b/.license_header_template @@ -0,0 +1,13 @@ +@@===----------------------------------------------------------------------===@@ +@@ +@@ This source file is part of the swift-libp2p open source project +@@ +@@ Copyright (c) YEARS swift-libp2p project authors +@@ Licensed under MIT +@@ +@@ See LICENSE for license information +@@ See CONTRIBUTORS for the list of swift-libp2p project authors +@@ +@@ SPDX-License-Identifier: MIT +@@ +@@===----------------------------------------------------------------------===@@ diff --git a/.licenseignore b/.licenseignore new file mode 100644 index 0000000..e78e0b8 --- /dev/null +++ b/.licenseignore @@ -0,0 +1,10 @@ +.github/workflows/configs/.flake8 +**/*.yml +*.md +*.txt +Package.swift +Package.resolved +.gitignore +.swift-format +.licenseignore +.license_header_template diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..55aaec7 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,19 @@ +Copyright (c) 2025 swift-libp2p + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. From a71a1cb112d1164a3637b958e23f1d3e4303d727 Mon Sep 17 00:00:00 2001 From: Brandon <32753167+btoms20@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:40:28 -0800 Subject: [PATCH 2/7] Added swift-format file --- .swift-format | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 .swift-format diff --git a/.swift-format b/.swift-format new file mode 100644 index 0000000..7e8ae73 --- /dev/null +++ b/.swift-format @@ -0,0 +1,68 @@ +{ + "version" : 1, + "indentation" : { + "spaces" : 4 + }, + "tabWidth" : 4, + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "spacesAroundRangeFormationOperators" : false, + "indentConditionalCompilationBlocks" : false, + "indentSwitchCaseLabels" : false, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : true, + "lineBreakBeforeEachGenericRequirement" : true, + "lineLength" : 120, + "maximumBlankLines" : 1, + "respectsExistingLineBreaks" : true, + "prioritizeKeepingFunctionOutputTogether" : true, + "noAssignmentInExpressions" : { + "allowedFunctions" : [ + "XCTAssertNoThrow", + "XCTAssertThrowsError" + ] + }, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : false, + "AmbiguousTrailingClosureOverload" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : true, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : false, + "UseLetInEveryBoundCaseVariable" : false, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : false, + "UseSynthesizedInitializer" : false, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + } +} From 65f23fae6a9065545c01b8c075df269da325bc49 Mon Sep 17 00:00:00 2001 From: Brandon <32753167+btoms20@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:44:29 -0800 Subject: [PATCH 3/7] Updated license header --- Package.swift | 14 +++++++++++++- Sources/LibP2PIdentify/Application+Identify.swift | 13 ++++++++++--- .../PartialIdentifyMessageHandler.swift | 13 ++++++++++--- Sources/LibP2PIdentify/LibP2PIdentify.swift | 14 ++++++++++++++ Sources/LibP2PIdentify/Protobufs/Identify.pb.swift | 14 ++++++++++++++ Sources/LibP2PIdentify/Protobufs/Identify.proto | 12 ++++++++++-- Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift | 13 ++++++++++--- .../RouteHandlers/IPFS_ID_Delta.swift | 13 ++++++++++--- .../RouteHandlers/IPFS_ID_Push.swift | 13 ++++++++++--- .../LibP2PIdentify/RouteHandlers/IPFS_Ping.swift | 13 ++++++++++--- Sources/LibP2PIdentify/routes.swift | 13 ++++++++++--- .../LibP2PIdentifyTests/LibP2PIdentifyTests.swift | 14 ++++++++++++++ 12 files changed, 135 insertions(+), 24 deletions(-) diff --git a/Package.swift b/Package.swift index 77502e9..d33fd15 100644 --- a/Package.swift +++ b/Package.swift @@ -1,5 +1,17 @@ // swift-tools-version:5.4 -// The swift-tools-version declares the minimum version of Swift required to build this package. +//===----------------------------------------------------------------------===// +// +// This source file is part of the swift-libp2p open source project +// +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// import PackageDescription diff --git a/Sources/LibP2PIdentify/Application+Identify.swift b/Sources/LibP2PIdentify/Application+Identify.swift index b622801..4171496 100644 --- a/Sources/LibP2PIdentify/Application+Identify.swift +++ b/Sources/LibP2PIdentify/Application+Identify.swift @@ -1,9 +1,16 @@ +//===----------------------------------------------------------------------===// // -// Application+Identify.swift -// +// This source file is part of the swift-libp2p open source project // -// Created by Brandon Toms on 4/13/22. +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT // +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// import LibP2P diff --git a/Sources/LibP2PIdentify/ChannelHandlers/PartialIdentifyMessageHandler.swift b/Sources/LibP2PIdentify/ChannelHandlers/PartialIdentifyMessageHandler.swift index 86ae7d0..b35527d 100644 --- a/Sources/LibP2PIdentify/ChannelHandlers/PartialIdentifyMessageHandler.swift +++ b/Sources/LibP2PIdentify/ChannelHandlers/PartialIdentifyMessageHandler.swift @@ -1,9 +1,16 @@ +//===----------------------------------------------------------------------===// // -// PartialIdentifyMessageDecoder.swift -// +// This source file is part of the swift-libp2p open source project // -// Created by Brandon Toms on 4/13/22. +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT // +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// import LibP2P import NIO diff --git a/Sources/LibP2PIdentify/LibP2PIdentify.swift b/Sources/LibP2PIdentify/LibP2PIdentify.swift index c7e3214..41c74bb 100644 --- a/Sources/LibP2PIdentify/LibP2PIdentify.swift +++ b/Sources/LibP2PIdentify/LibP2PIdentify.swift @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the swift-libp2p open source project +// +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// + import LibP2P import LibP2PCrypto import CoreFoundation diff --git a/Sources/LibP2PIdentify/Protobufs/Identify.pb.swift b/Sources/LibP2PIdentify/Protobufs/Identify.pb.swift index 189d552..a49fba6 100644 --- a/Sources/LibP2PIdentify/Protobufs/Identify.pb.swift +++ b/Sources/LibP2PIdentify/Protobufs/Identify.pb.swift @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the swift-libp2p open source project +// +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// + // DO NOT EDIT. // swift-format-ignore-file // diff --git a/Sources/LibP2PIdentify/Protobufs/Identify.proto b/Sources/LibP2PIdentify/Protobufs/Identify.proto index eb12edd..9a88b80 100644 --- a/Sources/LibP2PIdentify/Protobufs/Identify.proto +++ b/Sources/LibP2PIdentify/Protobufs/Identify.proto @@ -1,8 +1,16 @@ +//===----------------------------------------------------------------------===// // -// IdentifyProto.proto +// This source file is part of the swift-libp2p open source project // +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT // -// Created by Brandon Toms on 3/23/21. +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// // // https://github.com/libp2p/specs/blob/master/identify/README.md diff --git a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift index e65fb34..eee2899 100644 --- a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift +++ b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift @@ -1,9 +1,16 @@ +//===----------------------------------------------------------------------===// // -// IPFS_ID.swift -// +// This source file is part of the swift-libp2p open source project // -// Created by Brandon Toms on 5/1/22. +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT // +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// import LibP2P diff --git a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Delta.swift b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Delta.swift index b496270..448bc82 100644 --- a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Delta.swift +++ b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Delta.swift @@ -1,9 +1,16 @@ +//===----------------------------------------------------------------------===// // -// IPFS_ID_Delta.swift -// +// This source file is part of the swift-libp2p open source project // -// Created by Brandon Toms on 5/1/22. +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT // +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// import LibP2P diff --git a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Push.swift b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Push.swift index 370a1b9..b4d0209 100644 --- a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Push.swift +++ b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Push.swift @@ -1,9 +1,16 @@ +//===----------------------------------------------------------------------===// // -// IPFS_ID_Push.swift -// +// This source file is part of the swift-libp2p open source project // -// Created by Brandon Toms on 5/1/22. +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT // +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// import LibP2P diff --git a/Sources/LibP2PIdentify/RouteHandlers/IPFS_Ping.swift b/Sources/LibP2PIdentify/RouteHandlers/IPFS_Ping.swift index cc87600..1edd09a 100644 --- a/Sources/LibP2PIdentify/RouteHandlers/IPFS_Ping.swift +++ b/Sources/LibP2PIdentify/RouteHandlers/IPFS_Ping.swift @@ -1,9 +1,16 @@ +//===----------------------------------------------------------------------===// // -// IPFS_Ping.swift -// +// This source file is part of the swift-libp2p open source project // -// Created by Brandon Toms on 5/1/22. +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT // +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// import LibP2P diff --git a/Sources/LibP2PIdentify/routes.swift b/Sources/LibP2PIdentify/routes.swift index b0426f3..826b699 100644 --- a/Sources/LibP2PIdentify/routes.swift +++ b/Sources/LibP2PIdentify/routes.swift @@ -1,9 +1,16 @@ +//===----------------------------------------------------------------------===// // -// routes.swift -// +// This source file is part of the swift-libp2p open source project // -// Created by Brandon Toms on 3/30/22. +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT // +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// import LibP2P diff --git a/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift b/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift index 88e4e20..dead37c 100644 --- a/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift +++ b/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the swift-libp2p open source project +// +// Copyright (c) 2022-2025 swift-libp2p project authors +// Licensed under MIT +// +// See LICENSE for license information +// See CONTRIBUTORS for the list of swift-libp2p project authors +// +// SPDX-License-Identifier: MIT +// +//===----------------------------------------------------------------------===// + import LibP2PMPLEX import LibP2PNoise import XCTest From d29001228cfd7d24db616218a64edd50ae03758c Mon Sep 17 00:00:00 2001 From: Brandon <32753167+btoms20@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:45:55 -0800 Subject: [PATCH 4/7] formatted --- Package.swift | 16 +- .../PartialIdentifyMessageHandler.swift | 52 +-- Sources/LibP2PIdentify/LibP2PIdentify.swift | 317 ++++++++++++------ .../LibP2PIdentify/Protobufs/Identify.proto | 181 +++++----- .../RouteHandlers/IPFS_ID.swift | 34 +- .../RouteHandlers/IPFS_ID_Delta.swift | 22 +- .../RouteHandlers/IPFS_ID_Push.swift | 18 +- .../RouteHandlers/IPFS_Ping.swift | 18 +- Sources/LibP2PIdentify/routes.swift | 26 +- .../LibP2PIdentifyTests.swift | 155 ++++++--- 10 files changed, 515 insertions(+), 324 deletions(-) diff --git a/Package.swift b/Package.swift index d33fd15..12fbbf2 100644 --- a/Package.swift +++ b/Package.swift @@ -24,14 +24,14 @@ let package = Package( // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "LibP2PIdentify", - targets: ["LibP2PIdentify"]), + targets: ["LibP2PIdentify"] + ) ], dependencies: [ // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), .package(url: "https://github.com/swift-libp2p/swift-libp2p.git", .upToNextMajor(from: "0.1.0")), .package(url: "https://github.com/swift-libp2p/swift-libp2p-mplex.git", .upToNextMajor(from: "0.1.0")), - .package(url: "https://github.com/swift-libp2p/swift-libp2p-noise.git", .upToNextMajor(from: "0.1.0")) + .package(url: "https://github.com/swift-libp2p/swift-libp2p-noise.git", .upToNextMajor(from: "0.1.0")), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -42,14 +42,16 @@ let package = Package( .product(name: "LibP2P", package: "swift-libp2p") ], resources: [ - .copy("Protobufs/Identify.proto") - ]), + .copy("Protobufs/Identify.proto") + ] + ), .testTarget( name: "LibP2PIdentifyTests", dependencies: [ "LibP2PIdentify", .product(name: "LibP2PNoise", package: "swift-libp2p-noise"), - .product(name: "LibP2PMPLEX", package: "swift-libp2p-mplex") - ]), + .product(name: "LibP2PMPLEX", package: "swift-libp2p-mplex"), + ] + ), ] ) diff --git a/Sources/LibP2PIdentify/ChannelHandlers/PartialIdentifyMessageHandler.swift b/Sources/LibP2PIdentify/ChannelHandlers/PartialIdentifyMessageHandler.swift index b35527d..3ca41df 100644 --- a/Sources/LibP2PIdentify/ChannelHandlers/PartialIdentifyMessageHandler.swift +++ b/Sources/LibP2PIdentify/ChannelHandlers/PartialIdentifyMessageHandler.swift @@ -16,14 +16,14 @@ import LibP2P import NIO extension Application.ChildChannelHandlers.Provider { - + /// Loggers installs a set of inbound and outbound logging handlers that simply dump all data flowing through the pipeline out to the console for debugging purposes internal static var partialIdentifyMessageHandler: Self { .init { connection -> [ChannelHandler] in [ByteToMessageHandler(PartialIdentifyMessageDecoder())] } } - + } /// Sometimes we receive an `IdentifyMessage` without the signed peer record. @@ -31,12 +31,12 @@ extension Application.ChildChannelHandlers.Provider { public class PartialIdentifyMessageDecoder: ByteToMessageDecoder { public typealias InboundOut = ByteBuffer - private var partialIdentify:IdentifyMessage? = nil + private var partialIdentify: IdentifyMessage? = nil public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { // Make sure there's data to be read guard buffer.readableBytes > 0 else { return .needMoreData } - + //Try and decode the Identity Reponse guard var remoteIdentify = try? IdentifyMessage(contiguousBytes: Data(buffer.readableBytesView)) else { return .needMoreData @@ -45,63 +45,71 @@ public class PartialIdentifyMessageDecoder: ByteToMessageDecoder { if !remoteIdentify.publicKey.isEmpty && !remoteIdentify.signedPeerRecord.isEmpty { // Send the message's bytes up the pipeline to the next handler. context.fireChannelRead(self.wrapInboundOut(buffer)) - + // Consume the bytes buffer.moveReaderIndex(forwardBy: buffer.readableBytes) - + // We can keep going if you have more data. return .continue - + } else { // We received a partial identify message... if !remoteIdentify.publicKey.isEmpty && remoteIdentify.signedPeerRecord.isEmpty { // If this message contains the pubkey without the signature then store it in our cache self.partialIdentify = remoteIdentify - + // Consume the bytes buffer.moveReaderIndex(forwardBy: buffer.readableBytes) - + // Wait for the remainder of the IdentifyMessage to come in... return .needMoreData - } else if !remoteIdentify.signedPeerRecord.isEmpty && remoteIdentify.publicKey.isEmpty, var cachedIdentify = self.partialIdentify { + } else if !remoteIdentify.signedPeerRecord.isEmpty && remoteIdentify.publicKey.isEmpty, + var cachedIdentify = self.partialIdentify + { // If this message contains the signature without the pubkey, append the sig to the cached entry and attempt to validate cachedIdentify.signedPeerRecord = remoteIdentify.signedPeerRecord // Swap the remote identify message with the cached version and append the signedPeerRecord remoteIdentify = cachedIdentify - + // Consume the bytes buffer.moveReaderIndex(forwardBy: buffer.readableBytes) - + // Send the message's bytes up the pipeline to the next handler. - context.fireChannelRead(self.wrapInboundOut(ByteBuffer(bytes: try remoteIdentify.serializedData().bytes))) - + context.fireChannelRead( + self.wrapInboundOut(ByteBuffer(bytes: try remoteIdentify.serializedData().bytes)) + ) + // We can keep going if you have more data. return .continue - + } else { //print("PartialIdentifyMessageHandler:SignedPeerRecord is nil: \(remoteIdentify.signedPeerRecord.isEmpty)") //print("PartialIdentifyMessageHandler:PublicKey is nil: \(remoteIdentify.publicKey.isEmpty)") //print(remoteIdentify) - + // Partial identify message received and we're not sure what to do with it... context.fireErrorCaught(Errors.invalidPartialIdentifyMessage) - + // Consume the bytes buffer.moveReaderIndex(forwardBy: buffer.readableBytes) - + // We can keep going if you have more data. return .continue } } } - public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { - return try decode(context: context, buffer: &buffer) + public func decodeLast( + context: ChannelHandlerContext, + buffer: inout ByteBuffer, + seenEOF: Bool + ) throws -> DecodingState { + try decode(context: context, buffer: &buffer) } - - public enum Errors:Error { + + public enum Errors: Error { case invalidPartialIdentifyMessage case invalidIdentifyMessage } diff --git a/Sources/LibP2PIdentify/LibP2PIdentify.swift b/Sources/LibP2PIdentify/LibP2PIdentify.swift index 41c74bb..773205c 100644 --- a/Sources/LibP2PIdentify/LibP2PIdentify.swift +++ b/Sources/LibP2PIdentify/LibP2PIdentify.swift @@ -12,103 +12,104 @@ // //===----------------------------------------------------------------------===// +import CoreFoundation import LibP2P import LibP2PCrypto -import CoreFoundation - /// Identify V1.0.0 /// [Spec](https://github.com/libp2p/specs/tree/master/identify) public final class Identify: IdentityManager, CustomStringConvertible { weak var application: Application? - var localPeerID:PeerID - private var logger:Logger - - private let el:EventLoop - - public enum Errors:Error { + var localPeerID: PeerID + private var logger: Logger + + private let el: EventLoop + + public enum Errors: Error { case timedOut } - + internal struct PendingPing { - let peer:String - let startTime:UInt64 - let promise:EventLoopPromise? - - init(peer:String, startTime:UInt64, promise:EventLoopPromise? = nil) { + let peer: String + let startTime: UInt64 + let promise: EventLoopPromise? + + init(peer: String, startTime: UInt64, promise: EventLoopPromise? = nil) { self.peer = peer self.startTime = startTime self.promise = promise } } - internal var pingCache:[[UInt8]:PendingPing] = [:] - + internal var pingCache: [[UInt8]: PendingPing] = [:] + public struct Multicodecs { static let PING = "/ipfs/ping/1.0.0" static let DELTA = "/ipfs/id/delta/1.0.0" static let PUSH = "/ipfs/id/push/1.0.0" static let ID = "/ipfs/id/1.0.0" } - + public var description: String { - return "IPFS Identify[\(localPeerID.description)]" + "IPFS Identify[\(localPeerID.description)]" } - public init(application:Application) { + public init(application: Application) { self.application = application self.localPeerID = application.peerID self.logger = application.logger self.el = application.eventLoopGroup.next() - + /// Register our protocol route handler on the application... try! routes(application) - + self.logger[metadataKey: "Identify"] = .string("\(UUID().uuidString.prefix(5))") - + /// Register our event listeners application.events.on(self, event: .upgraded(onNewConnection)) application.events.on(self, event: .disconnected(onDisconnected)) - + self.logger.trace("Initialized!") } - + deinit { self.logger.trace("Deinitialized") } - + public func register() { self.logger.warning("TODO::Register Self!") } - - public func ping(peer:PeerID) -> EventLoopFuture { - return application!.eventLoopGroup.next().flatSubmit { //} .flatScheduleTask(deadline: .now() + .seconds(3)) { + + public func ping(peer: PeerID) -> EventLoopFuture { + application!.eventLoopGroup.next().flatSubmit { //} .flatScheduleTask(deadline: .now() + .seconds(3)) { self.application!.logger.trace("Identify::Attempting to ping \(peer)") return self.initiateOutboundPingTo(peer: peer) } } - - public func ping(addr:Multiaddr) -> EventLoopFuture { - return application!.eventLoopGroup.next().flatSubmit { //ScheduleTask(deadline: .now() + .seconds(3)) { + + public func ping(addr: Multiaddr) -> EventLoopFuture { + application!.eventLoopGroup.next().flatSubmit { //ScheduleTask(deadline: .now() + .seconds(3)) { self.application!.logger.trace("Identify::Attempting to ping \(addr)") return self.initiateOutboundPingTo(addr: addr) - } //.futureResult + } //.futureResult//.futureResult } - - internal func onNewConnection(_ connection:Connection) -> Void { + + internal func onNewConnection(_ connection: Connection) { // Take this opportunity to request an Identify Message from the remote peer... connection.logger.trace("Identify::New Upgraded Connection, Attempting to Identify Remote Peer...") // Open a new stream requesting the remote peer send us an Identify message // Calling newStream() without a closure/handler defaults to our registered route responder connection.newStream(forProtocol: "/ipfs/id/1.0.0") } - + /// Called when an existing connection has been closed /// userInfo should include... /// - the peers remoteAddress ?? and PeerID / PublicKey /// - reference to the channel ?? No reference to channel, this notification gets fired after the channel has been closed... - internal func onDisconnected(_ connection: Connection, _ remotePeerID:PeerID?) -> Void { + internal func onDisconnected(_ connection: Connection, _ remotePeerID: PeerID?) { // Take this opportunity to do any finalization / cleanup work regarding this peer... - connection.logger.trace("Identify::Connection to peer was closed, clean up / finalize any outstanding Identify data") + connection.logger.trace( + "Identify::Connection to peer was closed, clean up / finalize any outstanding Identify data" + ) } } @@ -117,26 +118,36 @@ extension Identify { /// /// - Ensures the message is signed by the correct / expected remote peer /// - Updates our peerstore with the metadata within the peer record - internal func consumeIdentifyMessage(payload:Data, id:String?, connection: Connection) { - + internal func consumeIdentifyMessage(payload: Data, id: String?, connection: Connection) { + do { /// Ensure the Payload is an IdentifyMessage let remoteIdentify = try IdentifyMessage(contiguousBytes: payload) /// and that is valid - let signedEnvelope = try SealedEnvelope(marshaledEnvelope: remoteIdentify.signedPeerRecord.bytes, verifiedWithPublicKey: remoteIdentify.publicKey.bytes) - let peerRecord = try PeerRecord(marshaledData: Data(signedEnvelope.rawPayload), withPublicKey: remoteIdentify.publicKey) - + let signedEnvelope = try SealedEnvelope( + marshaledEnvelope: remoteIdentify.signedPeerRecord.bytes, + verifiedWithPublicKey: remoteIdentify.publicKey.bytes + ) + let peerRecord = try PeerRecord( + marshaledData: Data(signedEnvelope.rawPayload), + withPublicKey: remoteIdentify.publicKey + ) + connection.logger.debug("Identify::\n\(signedEnvelope)") connection.logger.debug("Identify::\n\(peerRecord)") - + connection.logger.trace("Identify::Updating PeerStore with Identified Peer") self.updateIdentifiedPeerInPeerStore(peerRecord, identifyMessage: remoteIdentify, connection: connection) - + /// Publish the identifiedPeer event - self.application?.events.post(.identifiedPeer(IdentifiedPeer(peer: peerRecord.peerID, identity: try! remoteIdentify.serializedData().bytes))) - + self.application?.events.post( + .identifiedPeer( + IdentifiedPeer(peer: peerRecord.peerID, identity: try! remoteIdentify.serializedData().bytes) + ) + ) + connection.logger.trace("Identify::Successfully Identified Remote Peer using the Identify Protocol") - + return } catch { connection.logger.warning("Identify::Failed to consume Remote IdentifyMessage -> \(error)") @@ -151,13 +162,19 @@ extension Identify { /// /// - Ensures the message is signed by the correct / expected remote peer /// - Updates our peerstore with the metadata within the peer record - internal func consumePushIdentifyMessage(payload:Data, id:String?, connection: Connection) { + internal func consumePushIdentifyMessage(payload: Data, id: String?, connection: Connection) { do { /// Ensure the Payload is an IdentifyMessage let remoteIdentify = try IdentifyMessage(contiguousBytes: payload) /// and that is valid - let signedEnvelope = try SealedEnvelope(marshaledEnvelope: remoteIdentify.signedPeerRecord.bytes, verifiedWithPublicKey: remoteIdentify.publicKey.bytes) - let peerRecord = try PeerRecord(marshaledData: Data(signedEnvelope.rawPayload), withPublicKey: remoteIdentify.publicKey) + let signedEnvelope = try SealedEnvelope( + marshaledEnvelope: remoteIdentify.signedPeerRecord.bytes, + verifiedWithPublicKey: remoteIdentify.publicKey.bytes + ) + let peerRecord = try PeerRecord( + marshaledData: Data(signedEnvelope.rawPayload), + withPublicKey: remoteIdentify.publicKey + ) connection.logger.debug("Identify::Push::\n\(signedEnvelope)") connection.logger.debug("Identify::Push::\n\(peerRecord)") @@ -165,7 +182,9 @@ extension Identify { connection.logger.trace("Identify::Push::Updating PeerStore with Identified Peer") self.updateIdentifiedPeerInPeerStore(peerRecord, identifyMessage: remoteIdentify, connection: connection) - connection.logger.trace("Identify::Push::Successfully Updated Identified Remote Peer using the Identify Push Protocol") + connection.logger.trace( + "Identify::Push::Successfully Updated Identified Remote Peer using the Identify Push Protocol" + ) return } catch { @@ -176,22 +195,25 @@ extension Identify { } } - extension Identify { /// Constructs an IdentifyMessage that represents our applications current state. /// /// - This message is ready to be sent to a remote peer who's opened a new `/ipfs/id/1.0.0` stream on our connection - internal func constructIdentifyMessage(req:Request) throws -> [UInt8] { + internal func constructIdentifyMessage(req: Request) throws -> [UInt8] { //Construct our Local Nodes Identify Message - let listenAddrs:[Multiaddr] + let listenAddrs: [Multiaddr] if req.addr.isInternalAddress { /// A computer on our network is reaching out to us, respond with internal addresses... - req.logger.trace("Identify::A computer on our network is reaching out to us, responding with internal addresses...") + req.logger.trace( + "Identify::A computer on our network is reaching out to us, responding with internal addresses..." + ) listenAddrs = req.application.listenAddresses } else { /// A computer outside of our network is asking for our ID, respond with externally reachable addresses only... - req.logger.trace("Identify::A computer outside of our network is asking for our ID, responding with externally reachable addresses only...") + req.logger.trace( + "Identify::A computer outside of our network is asking for our ID, responding with externally reachable addresses only..." + ) listenAddrs = req.application.listenAddresses.stripInternalAddresses() } @@ -200,8 +222,8 @@ extension Identify { //let registeredProtos = self.libp2p?.registeredProtocols.compactMap { $0.protocolString() } ?? [] let registeredProtos = req.application.routes.all.compactMap { $0.description } id.protocols = registeredProtos - id.protocolVersion = "ipfs/0.1.0" //req.application.core.protocolVersion - id.agentVersion = "swift-ipfs/0.1.0" //req.application.core.agentVersion + id.protocolVersion = "ipfs/0.1.0" //req.application.core.protocolVersion + id.agentVersion = "swift-ipfs/0.1.0" //req.application.core.agentVersion id.observedAddr = try req.remoteAddress?.toMultiaddr().binaryPacked() ?? Data() id.listenAddrs = try listenAddrs.map { guard !$0.protocols().contains(.p2p) else { return try $0.binaryPacked() } @@ -209,7 +231,9 @@ extension Identify { } //Construct our PeerRecord and sign it with out PeerID private key - let peerRecordEnvelope = try PeerRecord(peerID: self.localPeerID, multiaddrs: listenAddrs).seal(withPrivateKey: self.localPeerID) + let peerRecordEnvelope = try PeerRecord(peerID: self.localPeerID, multiaddrs: listenAddrs).seal( + withPrivateKey: self.localPeerID + ) id.signedPeerRecord = try Data(peerRecordEnvelope.marshal()) // Marshal the Identify message and prepare for sending.. @@ -221,17 +245,24 @@ extension Identify { /// PeerStore Update Methods extension Identify { - private func updateIdentifiedPeerInPeerStore(_ peerRecord:PeerRecord, identifyMessage:IdentifyMessage, connection:Connection) -> Void { - guard let application = application else { connection.logger.error("Identify::Lost reference to our Application"); return } + private func updateIdentifiedPeerInPeerStore( + _ peerRecord: PeerRecord, + identifyMessage: IdentifyMessage, + connection: Connection + ) { + guard let application = application else { + connection.logger.error("Identify::Lost reference to our Application") + return + } let identifiedPeer = peerRecord.peerID guard identifiedPeer != application.peerID else { return } connection.logger.trace("Identify::Identified Remote Peer") - - var tasks:[EventLoopFuture] = [] - + + var tasks: [EventLoopFuture] = [] + // This call to add key will only update/upgrade the PeerID in the PeerStore, it wont 'downgrade' an existing PeerID tasks.append(application.peers.add(key: identifiedPeer, on: connection.channel.eventLoop)) - + // Update our peers listening addresses let listeningAddresses = identifyMessage.listenAddrs.compactMap { multiaddrData -> Multiaddr? in if let ma = try? Multiaddr(multiaddrData) { @@ -243,48 +274,95 @@ extension Identify { } return nil } - tasks.append(application.peers.add(addresses: listeningAddresses, toPeer: identifiedPeer, on: connection.channel.eventLoop)) - + tasks.append( + application.peers.add( + addresses: listeningAddresses, + toPeer: identifiedPeer, + on: connection.channel.eventLoop + ) + ) + // Update our peers known protocols let protocols = identifyMessage.protocols.compactMap { SemVerProtocol($0) } connection.logger.trace("Identify::Adding known protocols to peer \(identifiedPeer.b58String)") connection.logger.trace("Identify::\(protocols.map({ $0.stringValue }).joined(separator: ","))") - tasks.append(application.peers.add(protocols: protocols, toPeer: identifiedPeer, on: connection.channel.eventLoop)) - + tasks.append( + application.peers.add(protocols: protocols, toPeer: identifiedPeer, on: connection.channel.eventLoop) + ) + // Add the PeerRecord to our Records list tasks.append(application.peers.add(record: peerRecord, on: connection.channel.eventLoop)) - + // Update our peers metadata (agent version, protocol version, etc.. maybe include a verified attribute (the signed peer record)) connection.logger.trace("Identify::Adding Metadata to peer \(identifiedPeer.b58String)") connection.logger.trace("Identify::AgentVersion: \(identifyMessage.agentVersion)") if identifyMessage.hasAgentVersion, let agentVersion = identifyMessage.agentVersion.data(using: .utf8) { - tasks.append(application.peers.add(metaKey: .AgentVersion, data: agentVersion.bytes, toPeer: identifiedPeer, on: connection.channel.eventLoop)) + tasks.append( + application.peers.add( + metaKey: .AgentVersion, + data: agentVersion.bytes, + toPeer: identifiedPeer, + on: connection.channel.eventLoop + ) + ) } connection.logger.trace("Identify::ProtocolVersion: \(identifyMessage.protocolVersion)") - if identifyMessage.hasProtocolVersion, let protocolVersion = identifyMessage.protocolVersion.data(using: .utf8) { - tasks.append(application.peers.add(metaKey: .ProtocolVersion, data: protocolVersion.bytes, toPeer: identifiedPeer, on: connection.channel.eventLoop)) + if identifyMessage.hasProtocolVersion, let protocolVersion = identifyMessage.protocolVersion.data(using: .utf8) + { + tasks.append( + application.peers.add( + metaKey: .ProtocolVersion, + data: protocolVersion.bytes, + toPeer: identifiedPeer, + on: connection.channel.eventLoop + ) + ) } - connection.logger.trace("Identify::ObservedAddress: \((try? Multiaddr(identifyMessage.observedAddr).description) ?? "NIL")") - if identifyMessage.hasObservedAddr, let ma = try? Multiaddr(identifyMessage.observedAddr).description.data(using: .utf8) { - tasks.append(application.peers.add(metaKey: .ObservedAddress, data: ma.bytes, toPeer: identifiedPeer, on: connection.channel.eventLoop)) + connection.logger.trace( + "Identify::ObservedAddress: \((try? Multiaddr(identifyMessage.observedAddr).description) ?? "NIL")" + ) + if identifyMessage.hasObservedAddr, + let ma = try? Multiaddr(identifyMessage.observedAddr).description.data(using: .utf8) + { + tasks.append( + application.peers.add( + metaKey: .ObservedAddress, + data: ma.bytes, + toPeer: identifiedPeer, + on: connection.channel.eventLoop + ) + ) } - + // -TODO: Our Connection should do this when we complete our security handshake, also we should remove this here... - tasks.append(application.peers.add(metaKey: .LastHandshake, data: String(Date().timeIntervalSince1970).bytes, toPeer: identifiedPeer, on: connection.channel.eventLoop)) - + tasks.append( + application.peers.add( + metaKey: .LastHandshake, + data: String(Date().timeIntervalSince1970).bytes, + toPeer: identifiedPeer, + on: connection.channel.eventLoop + ) + ) + // Wait for the metadata to be updated then alert the application of the changes... tasks.flatten(on: connection.channel.eventLoop).whenComplete { _ in - connection.logger.trace("Identify::Done Adding Metadata to PeerStore. Alerting Application to Remote Peer Protocol Change.") - application.events.post(.remotePeerProtocolChange(RemotePeerProtocolChange(peer: identifiedPeer, protocols: protocols, connection: connection))) + connection.logger.trace( + "Identify::Done Adding Metadata to PeerStore. Alerting Application to Remote Peer Protocol Change." + ) + application.events.post( + .remotePeerProtocolChange( + RemotePeerProtocolChange(peer: identifiedPeer, protocols: protocols, connection: connection) + ) + ) } } } /// Ping Methods extension Identify { - + // - TODO: This doesn't handle multiple parallel outbound pings to the same peer - func initiateOutboundPingTo(peer:PeerID) -> EventLoopFuture { + func initiateOutboundPingTo(peer: PeerID) -> EventLoopFuture { el.flatSubmit { if let outstandingPing = self.pingCache[peer.bytes] { // If the outstanding ping has been in flight for more than 3 seconds, fail the promise @@ -299,14 +377,18 @@ extension Identify { } //guard self.pingCache[peer.bytes] == nil else { return application!.eventLoopGroup.next().makeFailedFuture(Errors.timedOut) } let promise = self.application!.eventLoopGroup.next().makePromise(of: TimeAmount.self) - self.pingCache[peer.bytes] = PendingPing(peer: "", startTime: DispatchTime.now().uptimeNanoseconds, promise: promise) + self.pingCache[peer.bytes] = PendingPing( + peer: "", + startTime: DispatchTime.now().uptimeNanoseconds, + promise: promise + ) try! self.application!.newStream(to: peer, forProtocol: Identify.Multicodecs.PING) return promise.futureResult } } - + // - TODO: This doesn't handle multiple parallel outbound pings to the same peer - func initiateOutboundPingTo(addr:Multiaddr) -> EventLoopFuture { + func initiateOutboundPingTo(addr: Multiaddr) -> EventLoopFuture { el.flatSubmit { guard let cid = addr.getPeerID(), let peer = try? PeerID(cid: cid) else { self.logger.warning("Identify::Failed to ping addr `\(addr)`. A valid peerID is neccessary") @@ -325,50 +407,67 @@ extension Identify { } //guard self.pingCache[peer.bytes] == nil else { return application!.eventLoopGroup.next().makeFailedFuture(Errors.timedOut) } let promise = self.application!.eventLoopGroup.next().makePromise(of: TimeAmount.self) - self.pingCache[peer.bytes] = PendingPing(peer: "", startTime: DispatchTime.now().uptimeNanoseconds, promise: promise) + self.pingCache[peer.bytes] = PendingPing( + peer: "", + startTime: DispatchTime.now().uptimeNanoseconds, + promise: promise + ) try! self.application!.newStream(to: addr, forProtocol: Identify.Multicodecs.PING) return promise.futureResult } } - - func handleOutboundPing(_ req:Request) -> ByteBuffer? { - guard let remotePeer = req.remotePeer else { req.logger.error("Identify::Outbound Ping failed due to unauthenticated stream"); req.shouldClose(); return nil } - let bytes:[UInt8] = try! LibP2PCrypto.randomBytes(length: 32) + + func handleOutboundPing(_ req: Request) -> ByteBuffer? { + guard let remotePeer = req.remotePeer else { + req.logger.error("Identify::Outbound Ping failed due to unauthenticated stream") + req.shouldClose() + return nil + } + let bytes: [UInt8] = try! LibP2PCrypto.randomBytes(length: 32) let startTime = DispatchTime.now().uptimeNanoseconds /// Check to see if this ping was initiated by our IndetifyManager... el.execute { if let initiatedPing = self.pingCache.removeValue(forKey: remotePeer.bytes) { - self.pingCache[bytes] = PendingPing(peer: remotePeer.b58String, startTime: startTime, promise: initiatedPing.promise) - } else { /// Otherwise just perform the ping for metrics... + self.pingCache[bytes] = PendingPing( + peer: remotePeer.b58String, + startTime: startTime, + promise: initiatedPing.promise + ) + } else { + /// Otherwise just perform the ping for metrics... self.pingCache[bytes] = .init(peer: remotePeer.b58String, startTime: startTime) } } return req.allocator.buffer(bytes: bytes) } - - func handleOutboundPingResponse(_ req:Request, pingResponse:[UInt8]) { + + func handleOutboundPingResponse(_ req: Request, pingResponse: [UInt8]) { el.execute { guard let pendingPing = self.pingCache.removeValue(forKey: pingResponse) else { req.logger.error("Identify::Unknown PendingPing Response") return } - + /// Determine to total round trip time in nanoseconds let toc = DispatchTime.now().uptimeNanoseconds - pendingPing.startTime - + /// Succeed pending promise if one exists... - pendingPing.promise?.succeed(.nanoseconds( toc > Int64.max ? Int64.max : Int64(toc))) - + pendingPing.promise?.succeed(.nanoseconds(toc > Int64.max ? Int64.max : Int64(toc))) + /// A not so nice hack to determine if the ping established a new connection or not - let isConnection:Bool = (toc / 1_000_000_000) >= 1 ? true : false - + let isConnection: Bool = (toc / 1_000_000_000) >= 1 ? true : false + req.logger.trace("Identify::Ping updating \(isConnection ? "connection" : "stream") latency") - + /// Update our peers metadata req.application.peers.getMetadata(forPeer: req.remotePeer!).flatMap { metadata -> EventLoopFuture in - let new:MetadataBook.LatencyMetadata + let new: MetadataBook.LatencyMetadata if let existingLatencyData = metadata[MetadataBook.Keys.Latency.rawValue], - var latencyData = try? JSONDecoder().decode(MetadataBook.LatencyMetadata.self, from: Data(existingLatencyData)) { + var latencyData = try? JSONDecoder().decode( + MetadataBook.LatencyMetadata.self, + from: Data(existingLatencyData) + ) + { if isConnection { latencyData.newConnectionLatencyValue(toc) } else { @@ -393,12 +492,16 @@ extension Identify { ) } } - + /// Encode New Latency Data and store it... let newData = try! JSONEncoder().encode(new) - + /// Store it! - return req.application.peers.add(metaKey: MetadataBook.Keys.Latency, data: newData.bytes, toPeer: req.remotePeer!) + return req.application.peers.add( + metaKey: MetadataBook.Keys.Latency, + data: newData.bytes, + toPeer: req.remotePeer! + ) }.whenComplete({ _ in req.logger.trace("Identify::Ping Time to Peer<\(pendingPing.peer.prefix(7))> == \(toc)ns") }) diff --git a/Sources/LibP2PIdentify/Protobufs/Identify.proto b/Sources/LibP2PIdentify/Protobufs/Identify.proto index 9a88b80..871288a 100644 --- a/Sources/LibP2PIdentify/Protobufs/Identify.proto +++ b/Sources/LibP2PIdentify/Protobufs/Identify.proto @@ -14,99 +14,104 @@ // // https://github.com/libp2p/specs/blob/master/identify/README.md -/* - -There are two variations of the identify protocol, identify and identify/push. - -1) identify - - The identify protocol has the protocol id /ipfs/id/1.0.0, and it is used to query remote peers for their information. - - The protocol works by opening a stream to the remote peer you want to query, using /ipfs/id/1.0.0 as the protocol id string. The peer being identified responds by returning an Identify message and closes the stream. - -2) identify/push - - The identify/push protocol has the protocol id /ipfs/id/push/1.0.0, and it is used to inform known peers about changes that occur at runtime. - - When a peer's basic information changes, for example, because they've obtained a new public listen address, they can use identify/push to inform others about the new information. - - The push variant works by opening a stream to each remote peer you want to update, using /ipfs/id/push/1.0.0 as the protocol id string. When the remote peer accepts the stream, the local peer will send an Identify message and close the stream. - - Upon recieving the pushed Identify message, the remote peer should update their local metadata repository with the information from the message. Note that missing fields should be ignored, as peers may choose to send partial updates containing only the fields whose values have changed. - - - Parameters - - - protocolVersion - - The protocol version identifies the family of protocols used by the peer. The current protocol version is ipfs/0.1.0; if the protocol major or minor version does not match the protocol used by the initiating peer, then the connection is considered unusable and the peer must close the connection. - - - agentVersion - - This is a free-form string, identifying the implementation of the peer. The usual format is agent-name/version, where agent-name is the name of the program or library and version is its semantic version. - - - publicKey - - This is the public key of the peer, marshalled in binary form as specicfied in peer-ids. - - - listenAddrs - - These are the addresses on which the peer is listening as multi-addresses. - - - observedAddr - - This is the connection source address of the stream initiating peer as observed by the peer being identified; it is a multi-address. The initiator can use this address to infer the existence of a NAT and its public address. - - For example, in the case of a TCP/IP transport the observed addresses will be of the form /ip4/x.x.x.x/tcp/xx. In the case of a circuit relay connection, the observed address will be of the form /p2p/QmRelay/p2p-circuit. In the case of onion transport, there is no observable source address. - - - protocols - - This is a list of protocols supported by the peer. - - */ +/// There are two variations of the identify protocol, identify and +/// identify/push. 1) identify +/// The identify protocol has the protocol id /ipfs/id/1.0.0, and it is used +/// to query remote peers for their information. The protocol works by +/// opening a stream to the remote peer you want to query, using +/// /ipfs/id/1.0.0 as the protocol id string. The peer being identified +/// responds by returning an Identify message and closes the stream. +/// 2) identify/push +/// The identify/push protocol has the protocol id /ipfs/id/push/1.0.0, and +/// it is used to inform known peers about changes that occur at runtime. +/// When a peer's basic information changes, for example, because they've +/// obtained a new public listen address, they can use identify/push to +/// inform others about the new information. The push variant works by +/// opening a stream to each remote peer you want to update, using +/// /ipfs/id/push/1.0.0 as the protocol id string. When the remote peer +/// accepts the stream, the local peer will send an Identify message and +/// close the stream. Upon recieving the pushed Identify message, the remote +/// peer should update their local metadata repository with the information +/// from the message. Note that missing fields should be ignored, as peers +/// may choose to send partial updates containing only the fields whose +/// values have changed. +/// +/// Parameters +/// +/// - protocolVersion +/// +/// The protocol version identifies the family of protocols used by the +/// peer. The current protocol version is ipfs/0.1.0; if the protocol major +/// or minor version does not match the protocol used by the initiating +/// peer, then the connection is considered unusable and the peer must close +/// the connection. +/// - agentVersion +/// This is a free-form string, identifying the implementation of the peer. +/// The usual format is agent-name/version, where agent-name is the name of +/// the program or library and version is its semantic version. +/// - publicKey +/// This is the public key of the peer, marshalled in binary form as +/// specicfied in peer-ids. +/// - listenAddrs +/// These are the addresses on which the peer is listening as +/// multi-addresses. +/// - observedAddr +/// This is the connection source address of the stream initiating peer as +/// observed by the peer being identified; it is a multi-address. The +/// initiator can use this address to infer the existence of a NAT and its +/// public address. For example, in the case of a TCP/IP transport the +/// observed addresses will be of the form /ip4/x.x.x.x/tcp/xx. In the case +/// of a circuit relay connection, the observed address will be of the form +/// /p2p/QmRelay/p2p-circuit. In the case of onion transport, there is no +/// observable source address. +/// - protocols +/// This is a list of protocols supported by the peer. syntax = "proto2"; message Delta { - // new protocols now serviced by the peer. - repeated string added_protocols = 1; - // protocols dropped by the peer. - repeated string rm_protocols = 2; + // new protocols now serviced by the peer. + repeated string added_protocols = 1; + // protocols dropped by the peer. + repeated string rm_protocols = 2; } message Identify { - // protocolVersion determines compatibility between peers - optional string protocolVersion = 5; // e.g. ipfs/1.0.0 - - // agentVersion is like a UserAgent string in browsers, or client version in bittorrent - // includes the client name and client. - optional string agentVersion = 6; // e.g. go-ipfs/0.1.0 - - // publicKey is this node's public key (which also gives its node.ID) - // - may not need to be sent, as secure channel implies it has been sent. - // - then again, if we change / disable secure channel, may still want it. - optional bytes publicKey = 1; - - // listenAddrs are the multiaddrs the sender node listens for open connections on - repeated bytes listenAddrs = 2; - - // oservedAddr is the multiaddr of the remote endpoint that the sender node perceives - // this is useful information to convey to the other side, as it helps the remote endpoint - // determine whether its connection to the local peer goes through NAT. - optional bytes observedAddr = 4; - - // protocols are the services this node is running - repeated string protocols = 3; - - // a delta update is incompatible with everything else. If this field is included, none of the others can appear. - optional Delta delta = 7; - - // signedPeerRecord contains a serialized SignedEnvelope containing a PeerRecord, - // signed by the sending node. It contains the same addresses as the listenAddrs field, but - // in a form that lets us share authenticated addrs with other peers. - // see github.com/libp2p/go-libp2p-core/record/pb/envelope.proto and - // github.com/libp2p/go-libp2p-core/peer/pb/peer_record.proto for message definitions. - optional bytes signedPeerRecord = 8; + // protocolVersion determines compatibility between peers + optional string protocolVersion = 5; // e.g. ipfs/1.0.0 + + // agentVersion is like a UserAgent string in browsers, or client version in + // bittorrent includes the client name and client. + optional string agentVersion = 6; // e.g. go-ipfs/0.1.0 + + // publicKey is this node's public key (which also gives its node.ID) + // - may not need to be sent, as secure channel implies it has been sent. + // - then again, if we change / disable secure channel, may still want it. + optional bytes publicKey = 1; + + // listenAddrs are the multiaddrs the sender node listens for open + // connections on + repeated bytes listenAddrs = 2; + + // oservedAddr is the multiaddr of the remote endpoint that the sender node + // perceives this is useful information to convey to the other side, as it + // helps the remote endpoint determine whether its connection to the local + // peer goes through NAT. + optional bytes observedAddr = 4; + + // protocols are the services this node is running + repeated string protocols = 3; + + // a delta update is incompatible with everything else. If this field is + // included, none of the others can appear. + optional Delta delta = 7; + + // signedPeerRecord contains a serialized SignedEnvelope containing a + // PeerRecord, signed by the sending node. It contains the same addresses as + // the listenAddrs field, but in a form that lets us share authenticated + // addrs with other peers. see + // github.com/libp2p/go-libp2p-core/record/pb/envelope.proto and + // github.com/libp2p/go-libp2p-core/peer/pb/peer_record.proto for message + // definitions. + optional bytes signedPeerRecord = 8; } - - diff --git a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift index eee2899..1aa32cf 100644 --- a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift +++ b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID.swift @@ -14,56 +14,62 @@ import LibP2P -internal func handleIDRequest(_ req:Request) -> Response { +internal func handleIDRequest(_ req: Request) -> Response { switch req.event { case .ready: guard req.streamDirection == .inbound else { - req.logger.trace("Identify::The remote peer supports the /ipfs/id/1.0.0 protocol, we should receive an identify message shortly.") + req.logger.trace( + "Identify::The remote peer supports the /ipfs/id/1.0.0 protocol, we should receive an identify message shortly." + ) return .stayOpen } - + // Respond with Identity Peer Record req.logger.trace("Identify::/ipfs/id/1.0.0 => New Stream Ready...") - + // Construct and send our outbound Identify message if let res = handleOutboundIdentifyMessage(req) { return .respondThenClose(res) } else { - return .close // TODO: Should be reset... + return .close // TODO: Should be reset... } - + case .data(let payload): guard req.streamDirection == .outbound else { req.logger.warning("Identify::We received data on an inbound request") return .close } - + // Parse the inbound Identify message, updating this Peer's metadata and alerting our application of any new data handleInboundIdentifyMessage(req, payload: payload) - + return .close - + default: req.logger.trace("Identify::\(req.event)") } - + return .stayOpen } -private func handleInboundIdentifyMessage(_ req:Request, payload:ByteBuffer) { +private func handleInboundIdentifyMessage(_ req: Request, payload: ByteBuffer) { guard let manager = req.application.identify as? Identify else { req.logger.error("Identify::Unknown IdentityManager. Unable to contruct identify message") return } - + // Consume the identify message... req.logger.trace("Identify::Consuming Inbound Identify Message") - manager.consumeIdentifyMessage(payload: Data(payload.readableBytesView), id: req.remotePeer?.b58String, connection: req.connection) + manager.consumeIdentifyMessage( + payload: Data(payload.readableBytesView), + id: req.remotePeer?.b58String, + connection: req.connection + ) return } -private func handleOutboundIdentifyMessage(_ req:Request) -> ByteBuffer? { +private func handleOutboundIdentifyMessage(_ req: Request) -> ByteBuffer? { //Send the identify message do { /// TODO: Fix this! We need to cast to Identify in order to construct our message becuase Request isn't part of LibP2PCore diff --git a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Delta.swift b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Delta.swift index 448bc82..b86599a 100644 --- a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Delta.swift +++ b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Delta.swift @@ -14,7 +14,7 @@ import LibP2P -internal func handleDeltaRequest(_ req:Request) -> Response { +internal func handleDeltaRequest(_ req: Request) -> Response { guard req.streamDirection == .inbound else { req.logger.error("Identify::Delta::Error - We dont support outbound /ipfs/id/delta messages on this handler") return .close @@ -31,26 +31,26 @@ internal func handleDeltaRequest(_ req:Request) -> Response { return .close } -private func handleDeltaMessage(_ req:Request) { - guard let message = try? IdentifyMessage(contiguousBytes: Array(req.payload.readableBytesView)) else { +private func handleDeltaMessage(_ req: Request) { + guard let message = try? IdentifyMessage(contiguousBytes: [UInt8](req.payload.readableBytesView)) else { req.logger.error("Identify::Delta::Failed to decode Delta IdentifyMessage") return } - + guard message.hasDelta else { req.logger.error("Identify::Delta::No Delta present within IdentifyMessage") return } - + let delta = message.delta - + guard !delta.addedProtocols.isEmpty && !delta.rmProtocols.isEmpty else { req.logger.error("Identify::Delta::Empty Delta message, nothing to do...") return } - - var tasks:[EventLoopFuture] = [] - + + var tasks: [EventLoopFuture] = [] + // Remove old protocols if !delta.rmProtocols.isEmpty { tasks.append( @@ -63,7 +63,7 @@ private func handleDeltaMessage(_ req:Request) { ) ) } - + // Add new protocols if !delta.addedProtocols.isEmpty { tasks.append( @@ -76,7 +76,7 @@ private func handleDeltaMessage(_ req:Request) { ) ) } - + // Get new set of supported protocols tasks.flatten(on: req.eventLoop).flatMap { Void -> EventLoopFuture<[SemVerProtocol]> in req.application.peers.getProtocols(forPeer: req.remotePeer!, on: req.eventLoop) diff --git a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Push.swift b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Push.swift index b4d0209..d58d77b 100644 --- a/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Push.swift +++ b/Sources/LibP2PIdentify/RouteHandlers/IPFS_ID_Push.swift @@ -14,30 +14,34 @@ import LibP2P -internal func handlePushRequest(_ req:Request) -> Response { +internal func handlePushRequest(_ req: Request) -> Response { guard req.streamDirection == .inbound else { req.logger.error("Identify::Push::Error - We dont support outbound /ipfs/id/push messages on this handler") return .close } - + switch req.event { case .ready: return .stayOpen - + case .data(let payload): guard let manager = req.application.identify as? Identify else { req.logger.error("Identify::Unknown IdentityManager. Unable to contruct identify message") return .close } - + /// Update values that are present... req.logger.warning("Identify::Push::We haven't tested this yet!") - manager.consumePushIdentifyMessage(payload: Data(payload.readableBytesView), id: req.remotePeer!.b58String, connection: req.connection) + manager.consumePushIdentifyMessage( + payload: Data(payload.readableBytesView), + id: req.remotePeer!.b58String, + connection: req.connection + ) return .close - + default: break } - + return .close } diff --git a/Sources/LibP2PIdentify/RouteHandlers/IPFS_Ping.swift b/Sources/LibP2PIdentify/RouteHandlers/IPFS_Ping.swift index 1edd09a..bb4a06d 100644 --- a/Sources/LibP2PIdentify/RouteHandlers/IPFS_Ping.swift +++ b/Sources/LibP2PIdentify/RouteHandlers/IPFS_Ping.swift @@ -14,7 +14,7 @@ import LibP2P -internal func handlePingRequest(_ req:Request) -> Response { +internal func handlePingRequest(_ req: Request) -> Response { switch req.streamDirection { case .inbound: switch req.event { @@ -26,7 +26,7 @@ internal func handlePingRequest(_ req:Request) -> Response { default: return .close } - + case .outbound: switch req.event { case .ready: @@ -35,31 +35,31 @@ internal func handlePingRequest(_ req:Request) -> Response { } else { return .close } - + case .data(let pingResponse): - handleOutboundPingResponse(req, pingResponse: Array(pingResponse.readableBytesView)) + handleOutboundPingResponse(req, pingResponse: [UInt8](pingResponse.readableBytesView)) return .close - + default: return .close } } } -private func handleOutboundPing(_ req:Request) -> ByteBuffer? { +private func handleOutboundPing(_ req: Request) -> ByteBuffer? { guard let manager = req.application.identify as? Identify else { req.logger.error("Identify::Unknown IdentityManager. Unable to contruct ping message") return nil } - + return manager.handleOutboundPing(req) } -private func handleOutboundPingResponse(_ req:Request, pingResponse:[UInt8]) { +private func handleOutboundPingResponse(_ req: Request, pingResponse: [UInt8]) { guard let manager = req.application.identify as? Identify else { req.logger.error("Identify::Unknown IdentityManager. Unable to contruct ping message") return } - + manager.handleOutboundPingResponse(req, pingResponse: pingResponse) } diff --git a/Sources/LibP2PIdentify/routes.swift b/Sources/LibP2PIdentify/routes.swift index 826b699..6cf82a7 100644 --- a/Sources/LibP2PIdentify/routes.swift +++ b/Sources/LibP2PIdentify/routes.swift @@ -21,45 +21,45 @@ import LibP2P /// - /ipfs/id/push/1.0.0 /// - /ipfs/ping/1.0.0 func routes(_ app: Application) throws { - + // ipfs/... app.group("ipfs") { ipfs in - + // Route group: ipfs/id/... // Handlers: .varIntLengthPrefix is applied to all routes within `id` ipfs.group("id", handlers: [.varIntLengthPrefixed]) { id in - + // Route Endpoint: ipfs/id/1.0.0 // Handlers: .partialIdentifyMessageHandler used to accumulate partial IdentifyMessages before triggering our handler id.on("1.0.0", handlers: [.partialIdentifyMessageHandler]) { req -> Response in - return handleIDRequest(req) + handleIDRequest(req) } - + // Route Group: ipfs/id/delta/... id.group("delta") { delta in - + // Route Endpoint: ipfs/id/delta/1.0.0 delta.on("1.0.0") { req -> Response in - return handleDeltaRequest(req) + handleDeltaRequest(req) } } - + // Route Group: ipfs/id/push/... id.group("push") { push in - + // Route Endpoint: ipfs/id/push/1.0.0 push.on("1.0.0") { req -> Response in - return handlePushRequest(req) + handlePushRequest(req) } } } - + // Route Group: /ipfs/ping/... ipfs.group("ping") { ping in - + // Route Enpoint: /ipfs/ping/1.0.0 ping.on("1.0.0") { req -> Response in - return handlePingRequest(req) + handlePingRequest(req) } } } diff --git a/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift b/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift index dead37c..c0738d1 100644 --- a/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift +++ b/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift @@ -15,15 +15,17 @@ import LibP2PMPLEX import LibP2PNoise import XCTest + @testable import LibP2P @testable import LibP2PIdentify final class LibP2PIdentifyTests: XCTestCase { func testIPFSIdentifyPayload() throws { - let payloadString = "0aab04080012a60430820222300d06092a864886f70d01010105000382020f003082020a0282020100a1f5c0e7c0d5e556afc0e84566f8c565773adb548ddc219ca9688613a0096c2dfd069804c84968545b9c9df19dd131cc8408b7781df7ddfaf208a42a821523ce03955164a62dcab6bd10dd26f8507517567ca128f00a056d8636b9549ddb59ca727628775c90bd91d6251adbdfd36bf68a09c3bfe69e1b1587e8f31a4b55afc8095e7b6f6683165f9c0ef0ad1b22d8b73749ee02aa46566cd5f7a9ff6eb1099fe36b363abd4e1293108a6d473a349e77aca15e49b20ffe61b4222eb3a634e8481d71a7fdceea88a2044fa5cedde1dee314e27880bc713ca578814684e85e0d21cff40e23c341f13ee1a06452f284664999862973e51d692b578cd9b7de89d786ad6baebcf8dfc343db8eda434a15929591917c52bf16741359149d0e7092bc919928f1d5b25cb48b0f90a7a05b0eb29adca993f893c6fb137a53a5c470a8a309b574bb4fd80879bde7dcc237eaf2ce9a17b9193032df99c8bf551987561ee264a09730f9029610571625e0d0e1e2a7f90469a6a480ed08cf9b4c3af0567bfe9abf470079d8cc7d7f22efc83598f86c9e0678caf79e2299a99c47c8d057e7f3b8af40185c8dd499a1c167c358d7ab83af6581944ce0b8b6bd2cfe4bf80c8c9e7f61fe94816df79e12ae5e82c588f894b86fd599da5912f8754de2a23f2d1529845a5570a72d8d8537325b95dd3c69d9ca30b8186c20170d10955b7da216822c73020301000112080468838352060fa1120b046883835291020fa1cc03120b046883835291020fa1cd031257046883835291020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e1257046883835291020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e1a0d2f697066732f626974737761701a132f697066732f626974737761702f312e302e301a132f697066732f626974737761702f312e312e301a132f697066732f626974737761702f312e322e301a0e2f697066732f69642f312e302e301a132f697066732f69642f707573682f312e302e301a0f2f697066732f6b61642f312e302e301a132f697066732f6c616e2f6b61642f312e302e301a102f697066732f70696e672f312e302e301a152f6c69627032702f6175746f6e61742f312e302e301a1f2f6c69627032702f636972637569742f72656c61792f302e322e302f686f701a202f6c69627032702f636972637569742f72656c61792f302e322e302f73746f701a0d2f6c69627032702f64637574721a032f782f2208044caa3c8506e78a2a0a697066732f302e312e3032196b75626f2f302e32312e302d7263332f39663231636635333842cb0b0aab04080012a60430820222300d06092a864886f70d01010105000382020f003082020a0282020100a1f5c0e7c0d5e556afc0e84566f8c565773adb548ddc219ca9688613a0096c2dfd069804c84968545b9c9df19dd131cc8408b7781df7ddfaf208a42a821523ce03955164a62dcab6bd10dd26f8507517567ca128f00a056d8636b9549ddb59ca727628775c90bd91d6251adbdfd36bf68a09c3bfe69e1b1587e8f31a4b55afc8095e7b6f6683165f9c0ef0ad1b22d8b73749ee02aa46566cd5f7a9ff6eb1099fe36b363abd4e1293108a6d473a349e77aca15e49b20ffe61b4222eb3a634e8481d71a7fdceea88a2044fa5cedde1dee314e27880bc713ca578814684e85e0d21cff40e23c341f13ee1a06452f284664999862973e51d692b578cd9b7de89d786ad6baebcf8dfc343db8eda434a15929591917c52bf16741359149d0e7092bc919928f1d5b25cb48b0f90a7a05b0eb29adca993f893c6fb137a53a5c470a8a309b574bb4fd80879bde7dcc237eaf2ce9a17b9193032df99c8bf551987561ee264a09730f9029610571625e0d0e1e2a7f90469a6a480ed08cf9b4c3af0567bfe9abf470079d8cc7d7f22efc83598f86c9e0678caf79e2299a99c47c8d057e7f3b8af40185c8dd499a1c167c358d7ab83af6581944ce0b8b6bd2cfe4bf80c8c9e7f61fe94816df79e12ae5e82c588f894b86fd599da5912f8754de2a23f2d1529845a5570a72d8d8537325b95dd3c69d9ca30b8186c20170d10955b7da216822c730203010001120203011a93030a221220b04a57d40eca138809f139a76b12044333c3740391c9bf1ce9d8e21a79210bfd10b38ba6afbab1bab9171a0a0a080468838352060fa11a0d0a0b046883835291020fa1cc031a0d0a0b046883835291020fa1cd031a590a57046883835291020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e1a590a57046883835291020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e1a0a0a08047f000001060fa11a0d0a0b047f00000191020fa1cc031a0d0a0b047f00000191020fa1cd031a590a57047f00000191020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e2a8004089547ae16ad9517e74d09c8280fcb8e305875cd5614964e95629660de26415a41a20ce232d448d5cbb2c245c6646c03e8ec5082277fcb612dc44d11efff4a3443b2690fd8abe011534e325bac89c9aeb5b832011c0ace99e29307bf37e790201625e33d11697c81d33e7e703d613d787cef34634b1870a4209b50b0bdf7e93bee1d954ab86e767373d524e59971da4ed63a1b330ca95ebaa0f4ba3667407e8f1e48f28008d10d510a03f79bece87b8a9b540ecb00a7cca06165ecd669d7ca51a3aa47436c77f53933e573fecee192519275638dcb068f4271cfc821684e2050969b3603473404cdb510b9cba00dfa7d74ec0bbd60e2898cd0efdd167ca137468ebeccb81d3e18725ea63c747c04de5a49cf974d36c4380bf16643b3b232e5f204d48494c18d82fb891d02416344b9f82d9e02909b7658bac1451dd685b2baf89e11e2ad4629f9b6ed0f791ae483a11f312b0370d73c008ed919addc22247e2935679868b4f80414c268b2756e6a65eb14e3de43b4213acdd676b1ba16ad168ead8500ce8c21074d5288672a3c3e600db5e1bb834f818e719752f09a2219fcaf8db2df74903429590cfdd59ccfed4e08baf47812261ae789437d876a7e8b252862aaed6dabf8484b25bc318b0ac8261833fa4599abb5b0fbc1c0755badaec2adbe09ae3444a02cbc09f2c6a4f8f3ea977af8c1ec0c1c015faaaf63bf7e7b14ff" + let payloadString = + "0aab04080012a60430820222300d06092a864886f70d01010105000382020f003082020a0282020100a1f5c0e7c0d5e556afc0e84566f8c565773adb548ddc219ca9688613a0096c2dfd069804c84968545b9c9df19dd131cc8408b7781df7ddfaf208a42a821523ce03955164a62dcab6bd10dd26f8507517567ca128f00a056d8636b9549ddb59ca727628775c90bd91d6251adbdfd36bf68a09c3bfe69e1b1587e8f31a4b55afc8095e7b6f6683165f9c0ef0ad1b22d8b73749ee02aa46566cd5f7a9ff6eb1099fe36b363abd4e1293108a6d473a349e77aca15e49b20ffe61b4222eb3a634e8481d71a7fdceea88a2044fa5cedde1dee314e27880bc713ca578814684e85e0d21cff40e23c341f13ee1a06452f284664999862973e51d692b578cd9b7de89d786ad6baebcf8dfc343db8eda434a15929591917c52bf16741359149d0e7092bc919928f1d5b25cb48b0f90a7a05b0eb29adca993f893c6fb137a53a5c470a8a309b574bb4fd80879bde7dcc237eaf2ce9a17b9193032df99c8bf551987561ee264a09730f9029610571625e0d0e1e2a7f90469a6a480ed08cf9b4c3af0567bfe9abf470079d8cc7d7f22efc83598f86c9e0678caf79e2299a99c47c8d057e7f3b8af40185c8dd499a1c167c358d7ab83af6581944ce0b8b6bd2cfe4bf80c8c9e7f61fe94816df79e12ae5e82c588f894b86fd599da5912f8754de2a23f2d1529845a5570a72d8d8537325b95dd3c69d9ca30b8186c20170d10955b7da216822c73020301000112080468838352060fa1120b046883835291020fa1cc03120b046883835291020fa1cd031257046883835291020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e1257046883835291020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e1a0d2f697066732f626974737761701a132f697066732f626974737761702f312e302e301a132f697066732f626974737761702f312e312e301a132f697066732f626974737761702f312e322e301a0e2f697066732f69642f312e302e301a132f697066732f69642f707573682f312e302e301a0f2f697066732f6b61642f312e302e301a132f697066732f6c616e2f6b61642f312e302e301a102f697066732f70696e672f312e302e301a152f6c69627032702f6175746f6e61742f312e302e301a1f2f6c69627032702f636972637569742f72656c61792f302e322e302f686f701a202f6c69627032702f636972637569742f72656c61792f302e322e302f73746f701a0d2f6c69627032702f64637574721a032f782f2208044caa3c8506e78a2a0a697066732f302e312e3032196b75626f2f302e32312e302d7263332f39663231636635333842cb0b0aab04080012a60430820222300d06092a864886f70d01010105000382020f003082020a0282020100a1f5c0e7c0d5e556afc0e84566f8c565773adb548ddc219ca9688613a0096c2dfd069804c84968545b9c9df19dd131cc8408b7781df7ddfaf208a42a821523ce03955164a62dcab6bd10dd26f8507517567ca128f00a056d8636b9549ddb59ca727628775c90bd91d6251adbdfd36bf68a09c3bfe69e1b1587e8f31a4b55afc8095e7b6f6683165f9c0ef0ad1b22d8b73749ee02aa46566cd5f7a9ff6eb1099fe36b363abd4e1293108a6d473a349e77aca15e49b20ffe61b4222eb3a634e8481d71a7fdceea88a2044fa5cedde1dee314e27880bc713ca578814684e85e0d21cff40e23c341f13ee1a06452f284664999862973e51d692b578cd9b7de89d786ad6baebcf8dfc343db8eda434a15929591917c52bf16741359149d0e7092bc919928f1d5b25cb48b0f90a7a05b0eb29adca993f893c6fb137a53a5c470a8a309b574bb4fd80879bde7dcc237eaf2ce9a17b9193032df99c8bf551987561ee264a09730f9029610571625e0d0e1e2a7f90469a6a480ed08cf9b4c3af0567bfe9abf470079d8cc7d7f22efc83598f86c9e0678caf79e2299a99c47c8d057e7f3b8af40185c8dd499a1c167c358d7ab83af6581944ce0b8b6bd2cfe4bf80c8c9e7f61fe94816df79e12ae5e82c588f894b86fd599da5912f8754de2a23f2d1529845a5570a72d8d8537325b95dd3c69d9ca30b8186c20170d10955b7da216822c730203010001120203011a93030a221220b04a57d40eca138809f139a76b12044333c3740391c9bf1ce9d8e21a79210bfd10b38ba6afbab1bab9171a0a0a080468838352060fa11a0d0a0b046883835291020fa1cc031a0d0a0b046883835291020fa1cd031a590a57046883835291020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e1a590a57046883835291020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e1a0a0a08047f000001060fa11a0d0a0b047f00000191020fa1cc031a0d0a0b047f00000191020fa1cd031a590a57047f00000191020fa1cd03d103d203221220a78c594f830726e17fba30224d448d5c4a4434e9e5a14f24b3822d14da46d19bd203221220855beff35231e37b3c4970b3e16e0e100eba09adc3e1ad5473a16c97f258b61e2a8004089547ae16ad9517e74d09c8280fcb8e305875cd5614964e95629660de26415a41a20ce232d448d5cbb2c245c6646c03e8ec5082277fcb612dc44d11efff4a3443b2690fd8abe011534e325bac89c9aeb5b832011c0ace99e29307bf37e790201625e33d11697c81d33e7e703d613d787cef34634b1870a4209b50b0bdf7e93bee1d954ab86e767373d524e59971da4ed63a1b330ca95ebaa0f4ba3667407e8f1e48f28008d10d510a03f79bece87b8a9b540ecb00a7cca06165ecd669d7ca51a3aa47436c77f53933e573fecee192519275638dcb068f4271cfc821684e2050969b3603473404cdb510b9cba00dfa7d74ec0bbd60e2898cd0efdd167ca137468ebeccb81d3e18725ea63c747c04de5a49cf974d36c4380bf16643b3b232e5f204d48494c18d82fb891d02416344b9f82d9e02909b7658bac1451dd685b2baf89e11e2ad4629f9b6ed0f791ae483a11f312b0370d73c008ed919addc22247e2935679868b4f80414c268b2756e6a65eb14e3de43b4213acdd676b1ba16ad168ead8500ce8c21074d5288672a3c3e600db5e1bb834f818e719752f09a2219fcaf8db2df74903429590cfdd59ccfed4e08baf47812261ae789437d876a7e8b252862aaed6dabf8484b25bc318b0ac8261833fa4599abb5b0fbc1c0755badaec2adbe09ae3444a02cbc09f2c6a4f8f3ea977af8c1ec0c1c015faaaf63bf7e7b14ff" - let payload = Array(hex: payloadString) + let payload = [UInt8](hex: payloadString) let identifyProto = try LibP2PIdentify.IdentifyMessage(contiguousBytes: payload) @@ -40,13 +42,16 @@ final class LibP2PIdentifyTests: XCTestCase { func testIPFSIdentifyPushPayloadJSClient() throws { /// An example /ipfs/id/push/1.0.0 payload from a JS Client - let payloadString = "a7061208047f00000106c223120804c0a8011706c223120804c0a8011606c2231208044caa3c850688fc1208044caa3c850686601a1b2f6c69627032702f636972637569742f72656c61792f302e312e301a0e2f697066732f69642f312e302e301a132f697066732f69642f707573682f312e302e301a102f697066732f70696e672f312e302e30429e050aab02080012a60230820122300d06092a864886f70d01010105000382010f003082010a02820101009a3520ce8cfcfa4fc1d9b1fecb0e9c6241188dd8e8dec881d44b4e69f1058eaf710550216c0b7d51e26a22844e4737e17135a954cb215953fff28dfd6976794c26aad507225231afb2db2e31d85b9ca680803bded3c7e896cf0959d945c451733563cd6684f6de597cbec0fdb11254e02044744ec9ffb61a00d120f6bbdc09b95bccedd07b701707626a95e891fe29609e7514ee9ba3b506cb2a3ffe0b6e6dbeae4adb678fa8551a14d8344ba0584aab0a8bb7296b6ee8f85ce2375f290c5d5e7eb905f3d49cee6cd381f65b1ce8af9e442fb0f218610ee14c833919e2aa260a7c77ba13baeca68809df32aaa05ae8f27ff9f04ce57938863c91e346f071fe350203010001120203011a670a221220add8ac59b1fe19f20dfaa2228c239e3c131d73e0a7848ac869d5eb959a27ec6c10a5d9c6f28b301a0a0a08047f00000106c2231a0a0a0804c0a8011706c2231a0a0a0804c0a8011606c2231a0a0a08044caa3c850688fc1a0a0a08044caa3c850686602a800228a3216af2fd7701d4cc0014d5fc410a8423a21170b2784516d7740405c7bf903cd7ed837725e857680c0cb0dbaf6fc4f95d7fe692872f16417e8fcfaad3cdd3b529e8fb224ba3a0cabdc36bee1f77ab013aa35beae0e46c4bf4e438ffad4a2fa558b43323a2526547bb47935ea1f3ea9fa67dc8bd51160f4a9875717ec146f0ef293cd4892e0395b042e6e7f0fd0af9babb06e1527ee0f11b50671d7a190877d354abe11e32c607c7492598b37a29c35925c43b89f5a4e92e97b1973ecaa1027714e2439ffe27b390e31d4763d6b5fe637c95848e8832f1bb7ec4de20ee9b767286661a8d0aeeedf5cdb97e639fa530b237a2914459e173fb2aad3363d8bb08" + let payloadString = + "a7061208047f00000106c223120804c0a8011706c223120804c0a8011606c2231208044caa3c850688fc1208044caa3c850686601a1b2f6c69627032702f636972637569742f72656c61792f302e312e301a0e2f697066732f69642f312e302e301a132f697066732f69642f707573682f312e302e301a102f697066732f70696e672f312e302e30429e050aab02080012a60230820122300d06092a864886f70d01010105000382010f003082010a02820101009a3520ce8cfcfa4fc1d9b1fecb0e9c6241188dd8e8dec881d44b4e69f1058eaf710550216c0b7d51e26a22844e4737e17135a954cb215953fff28dfd6976794c26aad507225231afb2db2e31d85b9ca680803bded3c7e896cf0959d945c451733563cd6684f6de597cbec0fdb11254e02044744ec9ffb61a00d120f6bbdc09b95bccedd07b701707626a95e891fe29609e7514ee9ba3b506cb2a3ffe0b6e6dbeae4adb678fa8551a14d8344ba0584aab0a8bb7296b6ee8f85ce2375f290c5d5e7eb905f3d49cee6cd381f65b1ce8af9e442fb0f218610ee14c833919e2aa260a7c77ba13baeca68809df32aaa05ae8f27ff9f04ce57938863c91e346f071fe350203010001120203011a670a221220add8ac59b1fe19f20dfaa2228c239e3c131d73e0a7848ac869d5eb959a27ec6c10a5d9c6f28b301a0a0a08047f00000106c2231a0a0a0804c0a8011706c2231a0a0a0804c0a8011606c2231a0a0a08044caa3c850688fc1a0a0a08044caa3c850686602a800228a3216af2fd7701d4cc0014d5fc410a8423a21170b2784516d7740405c7bf903cd7ed837725e857680c0cb0dbaf6fc4f95d7fe692872f16417e8fcfaad3cdd3b529e8fb224ba3a0cabdc36bee1f77ab013aa35beae0e46c4bf4e438ffad4a2fa558b43323a2526547bb47935ea1f3ea9fa67dc8bd51160f4a9875717ec146f0ef293cd4892e0395b042e6e7f0fd0af9babb06e1527ee0f11b50671d7a190877d354abe11e32c607c7492598b37a29c35925c43b89f5a4e92e97b1973ecaa1027714e2439ffe27b390e31d4763d6b5fe637c95848e8832f1bb7ec4de20ee9b767286661a8d0aeeedf5cdb97e639fa530b237a2914459e173fb2aad3363d8bb08" - let payload = Array(hex: payloadString) + let payload = [UInt8](hex: payloadString) let lengthPrefix = uVarInt(payload) - let identifyProto = try? LibP2PIdentify.IdentifyMessage(contiguousBytes: payload.dropFirst(lengthPrefix.bytesRead)) + let identifyProto = try? LibP2PIdentify.IdentifyMessage( + contiguousBytes: payload.dropFirst(lengthPrefix.bytesRead) + ) XCTAssertNotNil(identifyProto) @@ -62,7 +67,12 @@ final class LibP2PIdentifyTests: XCTestCase { // Try and decode the Sealed Envelope containing the Signed PeerRecord // We should use the public key that we have on file to verify the signedEnvelope - guard let signedEnvelope = try? SealedEnvelope(marshaledEnvelope: identifyProto!.signedPeerRecord.bytes, verifiedWithPublicKey: nil) else { + guard + let signedEnvelope = try? SealedEnvelope( + marshaledEnvelope: identifyProto!.signedPeerRecord.bytes, + verifiedWithPublicKey: nil + ) + else { XCTFail("Identify::Failed to decode Envelope containing PeerRecord") return } @@ -85,16 +95,25 @@ final class LibP2PIdentifyTests: XCTestCase { /// Ensure our identifiedPeer Events are being fired correctly var peerUpdateEvents: Int = 0 - application.events.on(self, event: .remotePeerProtocolChange({ _ in - peerUpdateEvents += 1 - })) + application.events.on( + self, + event: .remotePeerProtocolChange({ _ in + peerUpdateEvents += 1 + }) + ) for hexRecord in Fixtures.PushRecords { - let payload = Array(hex: hexRecord) + let payload = [UInt8](hex: hexRecord) let identifyProto = try LibP2PIdentify.IdentifyMessage(contiguousBytes: payload) - let signedEnvelope = try SealedEnvelope(marshaledEnvelope: identifyProto.signedPeerRecord.bytes, verifiedWithPublicKey: identifyProto.publicKey.bytes) - let peerRecord = try PeerRecord(marshaledData: Data(signedEnvelope.rawPayload), withPublicKey: identifyProto.publicKey) + let signedEnvelope = try SealedEnvelope( + marshaledEnvelope: identifyProto.signedPeerRecord.bytes, + verifiedWithPublicKey: identifyProto.publicKey.bytes + ) + let peerRecord = try PeerRecord( + marshaledData: Data(signedEnvelope.rawPayload), + withPublicKey: identifyProto.publicKey + ) //print(identifyProto) //print(signedEnvelope) @@ -106,7 +125,8 @@ final class LibP2PIdentifyTests: XCTestCase { updateIdentifiedPeerInPeerStore(peerRecord, identifyMessage: identifyProto) } - func updateIdentifiedPeerInPeerStore(_ peerRecord: PeerRecord, identifyMessage: LibP2PIdentify.IdentifyMessage) { + func updateIdentifiedPeerInPeerStore(_ peerRecord: PeerRecord, identifyMessage: LibP2PIdentify.IdentifyMessage) + { let eventLoop = application.eventLoopGroup.next() let identifiedPeer = peerRecord.peerID guard identifiedPeer != application.peerID else { return } @@ -140,25 +160,65 @@ final class LibP2PIdentifyTests: XCTestCase { //connection.logger.trace("Identify::Adding Metadata to peer \(identifiedPeer.b58String)") //connection.logger.trace("Identify::AgentVersion: \(identifyMessage.agentVersion)") if identifyMessage.hasAgentVersion, let agentVersion = identifyMessage.agentVersion.data(using: .utf8) { - tasks.append(application.peers.add(metaKey: .AgentVersion, data: agentVersion.bytes, toPeer: identifiedPeer, on: eventLoop)) + tasks.append( + application.peers.add( + metaKey: .AgentVersion, + data: agentVersion.bytes, + toPeer: identifiedPeer, + on: eventLoop + ) + ) } //connection.logger.trace("Identify::ProtocolVersion: \(identifyMessage.protocolVersion)") - if identifyMessage.hasProtocolVersion, let protocolVersion = identifyMessage.protocolVersion.data(using: .utf8) { - tasks.append(application.peers.add(metaKey: .ProtocolVersion, data: protocolVersion.bytes, toPeer: identifiedPeer, on: eventLoop)) + if identifyMessage.hasProtocolVersion, + let protocolVersion = identifyMessage.protocolVersion.data(using: .utf8) + { + tasks.append( + application.peers.add( + metaKey: .ProtocolVersion, + data: protocolVersion.bytes, + toPeer: identifiedPeer, + on: eventLoop + ) + ) } //connection.logger.trace("Identify::ObservedAddress: \((try? Multiaddr(identifyMessage.observedAddr).description) ?? "NIL")") - if identifyMessage.hasObservedAddr, let ma = try? Multiaddr(identifyMessage.observedAddr).description.data(using: .utf8) { - tasks.append(application.peers.add(metaKey: .ObservedAddress, data: ma.bytes, toPeer: identifiedPeer, on: eventLoop)) + if identifyMessage.hasObservedAddr, + let ma = try? Multiaddr(identifyMessage.observedAddr).description.data(using: .utf8) + { + tasks.append( + application.peers.add( + metaKey: .ObservedAddress, + data: ma.bytes, + toPeer: identifiedPeer, + on: eventLoop + ) + ) } // -TODO: Our Connection should do this when we complete our security handshake, also we should remove this here... - tasks.append(application.peers.add(metaKey: .LastHandshake, data: String(Date().timeIntervalSince1970).bytes, toPeer: identifiedPeer, on: eventLoop)) + tasks.append( + application.peers.add( + metaKey: .LastHandshake, + data: String(Date().timeIntervalSince1970).bytes, + toPeer: identifiedPeer, + on: eventLoop + ) + ) // Wait for the metadata to be updated then alert the application of the changes... tasks.flatten(on: eventLoop).whenComplete { _ in //print("Identify::Done Adding Metadata to PeerStore. Alerting Application to Remote Peer Protocol Change.") //print(peerRecord) - application.events.post(.remotePeerProtocolChange(RemotePeerProtocolChange(peer: identifiedPeer, protocols: protocols, connection: DummyConnection()))) + application.events.post( + .remotePeerProtocolChange( + RemotePeerProtocolChange( + peer: identifiedPeer, + protocols: protocols, + connection: DummyConnection() + ) + ) + ) } } @@ -178,7 +238,7 @@ final class LibP2PIdentifyTests: XCTestCase { let remotePeerRecords = try application.peers.getRecords(forPeer: aRemotePeer, on: nil).wait() XCTAssertEqual(remotePeerRecords.count, 7) let latestPeerRecord = try application.peers.getMostRecentRecord(forPeer: aRemotePeer, on: nil).wait() - XCTAssertEqual(latestPeerRecord?.sequenceNumber, 1663285097713605627) + XCTAssertEqual(latestPeerRecord?.sequenceNumber, 1_663_285_097_713_605_627) /// Trim the Records for the remote peer try application.peers.trimRecords(forPeer: aRemotePeer, on: nil).wait() @@ -186,7 +246,7 @@ final class LibP2PIdentifyTests: XCTestCase { let remotePeerRecordsAfter = try application.peers.getRecords(forPeer: aRemotePeer, on: nil).wait() XCTAssertEqual(remotePeerRecordsAfter.count, 1) let latestPeerRecordAfter = try application.peers.getMostRecentRecord(forPeer: aRemotePeer, on: nil).wait() - XCTAssertEqual(latestPeerRecordAfter?.sequenceNumber, 1663285097713605627) + XCTAssertEqual(latestPeerRecordAfter?.sequenceNumber, 1_663_285_097_713_605_627) //application.peers.dumpAll() @@ -226,10 +286,10 @@ final class LibP2PIdentifyTests: XCTestCase { app1.identify.ping(addr: ma).whenComplete { result in switch result { - case .success(let latency): - print("Latency: \(latency.nanoseconds) ns") - case .failure(let error): - XCTFail("\(error)") + case .success(let latency): + print("Latency: \(latency.nanoseconds) ns") + case .failure(let error): + XCTFail("\(error)") } pingExpectation.fulfill() } @@ -268,10 +328,10 @@ final class LibP2PIdentifyTests: XCTestCase { app1.identify.ping(peer: app2.peerID).whenComplete { result in switch result { - case .success(let latency): - print("Latency1: \(latency.nanoseconds) ns") - case .failure(let error): - XCTFail("\(error)") + case .success(let latency): + print("Latency1: \(latency.nanoseconds) ns") + case .failure(let error): + XCTFail("\(error)") } pingExpectation.fulfill() } @@ -316,31 +376,31 @@ final class LibP2PIdentifyTests: XCTestCase { app1.identify.ping(peer: app2.peerID).whenComplete { result in switch result { - case .success(let latency): - print("Latency1: \(latency.nanoseconds) ns") - latency1 = latency - case .failure(let error): - XCTFail("\(error)") + case .success(let latency): + print("Latency1: \(latency.nanoseconds) ns") + latency1 = latency + case .failure(let error): + XCTFail("\(error)") } pingExpectation1.fulfill() } app1.identify.ping(peer: app2.peerID).whenComplete { result in switch result { - case .success(let latency): - print("Latency2: \(latency.nanoseconds) ns") - latency2 = latency - case .failure(let error): - XCTFail("\(error)") + case .success(let latency): + print("Latency2: \(latency.nanoseconds) ns") + latency2 = latency + case .failure(let error): + XCTFail("\(error)") } pingExpectation2.fulfill() } app1.identify.ping(peer: app2.peerID).whenComplete { result in switch result { - case .success(let latency): - print("Latency3: \(latency.nanoseconds) ns") - latency3 = latency - case .failure(let error): - XCTFail("\(error)") + case .success(let latency): + print("Latency3: \(latency.nanoseconds) ns") + latency3 = latency + case .failure(let error): + XCTFail("\(error)") } pingExpectation3.fulfill() } @@ -361,7 +421,10 @@ final class LibP2PIdentifyTests: XCTestCase { ("testIDPushRecordDecoding", testIDPushRecordDecoding), ("testLibP2PInternalPingMultiaddr", testLibP2PInternalPingMultiaddr), ("testLibP2PInternalPingPeer", testLibP2PInternalPingPeer), - ("testLibP2PInternalPingPeerCascadeMultipleInflightPings", testLibP2PInternalPingPeerCascadeMultipleInflightPings) + ( + "testLibP2PInternalPingPeerCascadeMultipleInflightPings", + testLibP2PInternalPingPeerCascadeMultipleInflightPings + ), ] } From fc557995f3397b7a0da4a7789bb45d345c49340b Mon Sep 17 00:00:00 2001 From: Brandon <32753167+btoms20@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:47:00 -0800 Subject: [PATCH 5/7] Bumped spm tools to 5.5 --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 12fbbf2..2f7b06e 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.4 +// swift-tools-version:5.5 //===----------------------------------------------------------------------===// // // This source file is part of the swift-libp2p open source project From 4d6821205660c0e6d6868112539a98baf6ccae78 Mon Sep 17 00:00:00 2001 From: Brandon <32753167+btoms20@users.noreply.github.com> Date: Sat, 1 Mar 2025 13:58:22 -0800 Subject: [PATCH 6/7] Since we're not in control of this remote peer, just check to make sure we received at least one record, not exactly 7. --- Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift b/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift index c0738d1..c26bc57 100644 --- a/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift +++ b/Tests/LibP2PIdentifyTests/LibP2PIdentifyTests.swift @@ -236,7 +236,7 @@ final class LibP2PIdentifyTests: XCTestCase { let aRemotePeer = try PeerID(cid: "QmRYiTAVhmPUuE6dnLa2vQGH6pQvUatKJtGFtDRc9bAkeQ") /// Fetch the Records for the remote peer let remotePeerRecords = try application.peers.getRecords(forPeer: aRemotePeer, on: nil).wait() - XCTAssertEqual(remotePeerRecords.count, 7) + XCTAssertGreaterThan(remotePeerRecords.count, 0) let latestPeerRecord = try application.peers.getMostRecentRecord(forPeer: aRemotePeer, on: nil).wait() XCTAssertEqual(latestPeerRecord?.sequenceNumber, 1_663_285_097_713_605_627) From 12d82b91b5e34d640458267cb644ac2d72227fcd Mon Sep 17 00:00:00 2001 From: Brandon <32753167+btoms20@users.noreply.github.com> Date: Sat, 1 Mar 2025 14:06:14 -0800 Subject: [PATCH 7/7] Updated README.md --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 645e563..1601378 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,6 @@ Identify consists of a set of protocols used to help identify remote peers withi - /ipfs/ping - A ping protocol that allows for quick and easy pings to libp2p Peers for measuring latency, testing liveness/connectivity, etc. -#### Heads up ‼️ -- This package is embedded into [swift-libp2p](https://github.com/swift-libp2p/swift-libp2p) . There's no need to include this package as a dependency in your swift-libp2p project. - #### For more details see - [Multiformats / Mulitbase Spec](https://github.com/multiformats/multibase/blob/master/README.md)