diff --git a/Package.swift b/Package.swift index 2f10252..08c0bc3 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.5 +// swift-tools-version:6.0 //===----------------------------------------------------------------------===// // // This source file is part of the swift-libp2p open source project @@ -35,16 +35,16 @@ let package = Package( .package(url: "https://github.com/apple/swift-nio.git", .upToNextMajor(from: "2.0.0")), // LibP2P Peer Identities - .package(url: "https://github.com/swift-libp2p/swift-peer-id.git", .upToNextMinor(from: "0.1.0")), + .package(url: "https://github.com/swift-libp2p/swift-peer-id.git", .upToNextMinor(from: "0.2.0")), // LibP2P Multiaddr - .package(url: "https://github.com/swift-libp2p/swift-multiaddr.git", .upToNextMinor(from: "0.1.0")), + .package(url: "https://github.com/swift-libp2p/swift-multiaddr.git", .upToNextMinor(from: "0.2.0")), // Logging - .package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.0.0")), + .package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.6.0")), // Swift Protobuf - //.package(url: "https://github.com/apple/swift-protobuf.git", .exact("1.19.0")), + .package(url: "https://github.com/apple/swift-protobuf.git", .upToNextMajor(from: "1.33.3")), ], targets: [ // Targets are the basic building blocks of a package. A target can define a module or a test suite. @@ -56,7 +56,7 @@ let package = Package( .product(name: "Logging", package: "swift-log"), .product(name: "PeerID", package: "swift-peer-id"), .product(name: "Multiaddr", package: "swift-multiaddr"), - //.product(name: "SwiftProtobuf", package: "swift-protobuf"), + .product(name: "SwiftProtobuf", package: "swift-protobuf"), ] ), .testTarget( diff --git a/README.md b/README.md index 665bf86..d824b26 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ let package = Package( ... dependencies: [ ... - .package(name: "LibP2PCore", url: "https://github.com/swift-libp2p/swift-libp2p-core.git", .upToNextMajor(from: "0.0.1")) + .package(name: "LibP2PCore", url: "https://github.com/swift-libp2p/swift-libp2p-core.git", .upToNextMinor(from: "0.4.0")) ], ... .target( @@ -55,7 +55,7 @@ check out the [tests]() for more examples import LibP2PCore -// You now have access to thing like PeerID, Multiaddr, Connections, Swift-NIO, etc... +// You now have access to core components like PeerID, Multiaddr, Connections, Swift-NIO, etc... ``` diff --git a/Sources/LibP2PCore/ConnectionManager/ConnectionManager.swift b/Sources/LibP2PCore/ConnectionManager/ConnectionManager.swift index f9b6410..5e38ebf 100644 --- a/Sources/LibP2PCore/ConnectionManager/ConnectionManager.swift +++ b/Sources/LibP2PCore/ConnectionManager/ConnectionManager.swift @@ -15,7 +15,7 @@ import NIOCore /// - TODO: Remove Optional Return Value -public protocol ConnectionManager { +public protocol ConnectionManager: Sendable { func getConnections(on: EventLoop?) -> EventLoopFuture<[Connection]> func getConnectionsToPeer(peer: PeerID, on: EventLoop?) -> EventLoopFuture<[Connection]> func getBestConnectionForPeer(peer: PeerID, on: EventLoop?) -> EventLoopFuture @@ -42,7 +42,7 @@ public protocol ConnectionManager { } /// Peer Connectedness -public enum Connectedness { +public enum Connectedness: Sendable { /// We have not yet attempted to connect to the peer in question case NotConnected /// We have an existing open connection to the peer in question diff --git a/Sources/LibP2PCore/Crypto/Crypto.swift b/Sources/LibP2PCore/Crypto/Crypto.swift index 635ffed..125de54 100644 --- a/Sources/LibP2PCore/Crypto/Crypto.swift +++ b/Sources/LibP2PCore/Crypto/Crypto.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// /// PrivKey represents a private key that can be used to generate a public key and sign data -public protocol PrivateKey { +public protocol PrivateKey: Sendable { var key: [UInt8] { get } /// Cryptographically sign the given bytes @@ -24,7 +24,7 @@ public protocol PrivateKey { } /// PubKey is a public key that can be used to verifiy data signed with the corresponding private key -public protocol PublicKey { +public protocol PublicKey: Sendable { var key: [UInt8] { get } /// Verify that 'sig' is the signed hash of 'data' diff --git a/Sources/LibP2PCore/DHT/DHTCore.swift b/Sources/LibP2PCore/DHT/DHTCore.swift index 1560166..1ccfded 100644 --- a/Sources/LibP2PCore/DHT/DHTCore.swift +++ b/Sources/LibP2PCore/DHT/DHTCore.swift @@ -31,7 +31,7 @@ public protocol DHTCore: Discovery, EventLoopService { } -public protocol DHTRecord { +public protocol DHTRecord: Sendable { var key: Data { get } var value: Data { get } var author: Data { get } diff --git a/Sources/LibP2PCore/Discovery/Discovery.swift b/Sources/LibP2PCore/Discovery/Discovery.swift index 89ca003..3bb2c92 100644 --- a/Sources/LibP2PCore/Discovery/Discovery.swift +++ b/Sources/LibP2PCore/Discovery/Discovery.swift @@ -17,37 +17,33 @@ import NIOCore import PeerID /// Advertiser is an interface for advertising services -public protocol Advertiser { +public protocol Advertiser: Sendable { /// Advertise advertises a service on the specified protocol and returns the registration TTL upon successful registration - /// - /// - TODO: Add in options func advertise(service: String, options: Options?) -> EventLoopFuture } /// Discoverer is an interface for peer discovery -public protocol Discoverer { +public protocol Discoverer: Sendable { /// FindPeers discovers peers providing a service - /// - /// - TODO: Add in options func findPeers(supportingService: String, options: Options?) -> EventLoopFuture /// Allows LibP2P to register a callback / event handler on the Discovery mechanism to be alerted of various events, such as peer discovery. - var onPeerDiscovered: ((_ peerInfo: PeerInfo) -> Void)? { get set } + var onPeerDiscovered: (@Sendable (_ peerInfo: PeerInfo) -> Void)? { get set } } /// Discovery is an interface that combines service advertisement and peer discovery -public protocol Discovery: Advertiser, Discoverer { +public protocol Discovery: Advertiser, Discoverer, Sendable { static var key: String { get } } -public protocol PeerDiscovery: EventLoopService { +public protocol PeerDiscovery: EventLoopService, Sendable { /// Allows LibP2P to register a callback / event handler on the Discovery mechanism to be alerted of various events, such as peer discovery. - var on: ((_ event: PeerDiscoveryEvent) -> Void)? { get set } + var on: (@Sendable (_ event: PeerDiscoveryEvent) -> Void)? { get set } /// Allows LibP2P to query the Discovery mechanism for all of the peers it has encountered so far func knownPeers() -> EventLoopFuture<[PeerInfo]> } -public protocol Options { +public protocol Options: Sendable { /// TTL is an option that provides a hint for the duration of an advertisement var ttl: TimeAmount { get } /// Limit is an option that provides an upper bound on the peer count for discovery @@ -65,7 +61,7 @@ public struct StandardOptions: Options { public var limit: Int } -public struct DiscoverdPeers { +public struct DiscoverdPeers: Sendable { public let cookie: Data? public let peers: [PeerInfo] diff --git a/Sources/LibP2PCore/Event/PeerDiscoveryEvent.swift b/Sources/LibP2PCore/Event/PeerDiscoveryEvent.swift index 54d1197..2dbbafb 100644 --- a/Sources/LibP2PCore/Event/PeerDiscoveryEvent.swift +++ b/Sources/LibP2PCore/Event/PeerDiscoveryEvent.swift @@ -14,7 +14,7 @@ import Multiaddr -public enum PeerDiscoveryEvent { +public enum PeerDiscoveryEvent: Sendable { //case ready /// Every time a peer is discovered by a discovery service, it emits a peer event with the discovered peers information case onPeer(PeerInfo) diff --git a/Sources/LibP2PCore/Identify/Identify.swift b/Sources/LibP2PCore/Identify/Identify.swift index bf90f9d..58656ee 100644 --- a/Sources/LibP2PCore/Identify/Identify.swift +++ b/Sources/LibP2PCore/Identify/Identify.swift @@ -16,16 +16,16 @@ import Multiaddr import NIOCore import PeerID -public struct IdentifyMessage { - var listenAddresses: [Multiaddr] = [] +public struct IdentifyMessage: Sendable { + var listenAddresses: [Multiaddr] var observedAddress: Multiaddr? - var protocols: [String] = [] + var protocols: [String] var publicKey: PeerID? var agentVersion: String? var protocolVersion: String? } -public protocol IdentityManager { +public protocol IdentityManager: Sendable { func register() func ping(peer: PeerID) -> EventLoopFuture @@ -33,3 +33,13 @@ public protocol IdentityManager { //func constructIdentifyMessage(req:Request) throws -> [UInt8] } + +extension IdentityManager { + public func ping(peer: PeerID) async throws -> TimeAmount { + try await self.ping(peer: peer).get() + } + + public func ping(addr: Multiaddr) async throws -> TimeAmount { + try await self.ping(addr: addr).get() + } +} diff --git a/Sources/LibP2PCore/LibP2PCore.swift b/Sources/LibP2PCore/LibP2PCore.swift index caf2a3d..3605ec5 100644 --- a/Sources/LibP2PCore/LibP2PCore.swift +++ b/Sources/LibP2PCore/LibP2PCore.swift @@ -22,7 +22,7 @@ //public protocol Codecs { } -public enum Mode: String { +public enum Mode: String, Sendable { case initiator case listener } diff --git a/Sources/LibP2PCore/Network/Connection.swift b/Sources/LibP2PCore/Network/Connection.swift index 6919887..e6ce993 100644 --- a/Sources/LibP2PCore/Network/Connection.swift +++ b/Sources/LibP2PCore/Network/Connection.swift @@ -24,7 +24,7 @@ import PeerID /// - Libp2p Connection ≈ Swift NIO Client (or maybe Libp2p Transport is more akin to the Client, and Channel is a parent wrapper that handles meta data surrounding the client, streams and peer) /// /// [LibP2P Connection Interface Documentation](https://github.com/libp2p/js-libp2p-interfaces/tree/master/src/connection) -public protocol Connection: AnyObject { +public protocol Connection: AnyObject, Sendable { typealias NegotiationResult = (protocol: String, leftoverBytes: ByteBuffer?) typealias SecuredResult = (securityCodec: String, remotePeer: PeerID?, warning: SecurityWarnings?) @@ -173,7 +173,7 @@ public class ConnectionStats: CustomStringConvertible { case closing case closed } - public enum Direction { + public enum Direction: Sendable { case inbound case outbound } diff --git a/Sources/LibP2PCore/Network/Stream.swift b/Sources/LibP2PCore/Network/Stream.swift index 659bc21..3ea53fc 100644 --- a/Sources/LibP2PCore/Network/Stream.swift +++ b/Sources/LibP2PCore/Network/Stream.swift @@ -12,9 +12,10 @@ // //===----------------------------------------------------------------------===// +import NIOConcurrencyHelpers import NIOCore -public protocol Stream: AnyObject { +public protocol Stream: AnyObject, Sendable { /// The underlying connection this stream belongs to /// - Important: This must be a weakly held reference to our parent Connection var connection: Connection? { get } @@ -51,7 +52,7 @@ public protocol Stream: AnyObject { func write(_ buffer: ByteBuffer) -> EventLoopFuture /// A method that gets called when Stream Events are triggered - var on: ((LibP2PCore.StreamEvent) -> EventLoopFuture)? { get set } + var on: (@Sendable (LibP2PCore.StreamEvent) -> EventLoopFuture)? { get set } //func on(_ event:LibP2P.StreamEvent) -> EventLoopFuture /// Requests the Stream be closed on our end @@ -83,8 +84,8 @@ public protocol _Stream: Stream { /// The underlying connection this stream belongs to /// - Important: This must be a weakly held reference to our parent Connection /// Holding a reference to the parent Connection object, gives us a bridge to accessing the channel in order to kick off writes and access the channels allocater, etc... - var _connection: Connection? { get set } - var _streamState: LibP2PCore.StreamState { get set } + var _connection: NIOLockedValueBox { get } + var _streamState: NIOLockedValueBox { get } var mode: LibP2PCore.Mode { get } //var _channel:Channel? { get } } @@ -223,7 +224,7 @@ public final class StreamHandler { } } -public enum StreamState: UInt8 { +public enum StreamState: UInt8, Sendable { case initialized = 0 case open case receiveClosed diff --git a/Sources/LibP2PCore/Peer/Peer.swift b/Sources/LibP2PCore/Peer/Peer.swift index 62e08f5..a1e89ed 100644 --- a/Sources/LibP2PCore/Peer/Peer.swift +++ b/Sources/LibP2PCore/Peer/Peer.swift @@ -35,7 +35,7 @@ import PeerID //} /// A peer (PeerID) and their known addresses (Multiaddr) -public struct PeerInfo { +public struct PeerInfo: Sendable { public let peer: PeerID public let addresses: [Multiaddr] @@ -55,3 +55,16 @@ extension Multiaddr { return try PeerID(cid: cid) } } + +extension PeerInfo: CustomStringConvertible { + public var description: String { + if self.addresses.isEmpty { + return self.peer.description + } + return """ + \(self.peer) [ + \(self.addresses.map({ $0.description }).joined(separator: "\n") ) + ] + """ + } +} diff --git a/Sources/LibP2PCore/PeerStore/PeerStore.swift b/Sources/LibP2PCore/PeerStore/PeerStore.swift index 75a6435..405aae0 100644 --- a/Sources/LibP2PCore/PeerStore/PeerStore.swift +++ b/Sources/LibP2PCore/PeerStore/PeerStore.swift @@ -12,23 +12,90 @@ // //===----------------------------------------------------------------------===// +import NIOConcurrencyHelpers import NIOCore public typealias Metadata = [String: [UInt8]] -public final class ComprehensivePeer { - public var id: PeerID - public var addresses: [Multiaddr] = [] - public var protocols: [SemVerProtocol] = [] - public var metadata: Metadata = [:] - public var records: [PeerRecord] = [] +public final class ComprehensivePeer: Sendable { + public let id: PeerID - public init(id: PeerID) { + public var addresses: Set { + get { _addresses.withLockedValue { $0 } } + set { _addresses.withLockedValue { $0 = newValue } } + } + private let _addresses: NIOLockedValueBox> + + public var protocols: Set { + get { _protocols.withLockedValue { $0 } } + set { _protocols.withLockedValue { $0 = newValue } } + } + private let _protocols: NIOLockedValueBox> + + public var metadata: Metadata { + get { _metadata.withLockedValue { $0 } } + set { _metadata.withLockedValue { $0 = newValue } } + } + private let _metadata: NIOLockedValueBox + + public var records: Set { + get { _records.withLockedValue { $0 } } + set { _records.withLockedValue { $0 = newValue } } + } + private let _records: NIOLockedValueBox> + + public init( + id: PeerID, + addresses: Set = [], + protocols: Set = [], + metadata: Metadata = [:], + records: Set = [] + ) { self.id = id + self._addresses = .init(addresses) + self._protocols = .init(protocols) + self._metadata = .init(metadata) + self._records = .init(records) } + + //public func add(address: Multiaddr) { + // self._addresses.withLockedValue { $0.insert(address) } + //} + // + //public func add(protocol: SemVerProtocol) { + // self._protocols.withLockedValue { $0.insert(`protocol`) } + //} + // + //public func addMetadata(key: String, value: [UInt8]) { + // self._metadata.withLockedValue { $0[key] = value } + //} + // + //public func add(record: PeerRecord) { + // self._records.withLockedValue { $0.insert(record) } + //} } -public protocol PeerStore: KeyRepository, AddressRepository, ProtocolRepository, MetadataRepository, RecordRepository { +extension ComprehensivePeer: CustomStringConvertible { + public var description: String { + let header = "--- đŸ‘Ĩ \(self.id) đŸ‘Ĩ ---" + return """ + \(header) + â˜Žī¸ Addresses: + \t- \(self.addresses.map { $0.description }.joined(separator: "\n\t- ")) + 📒 Protocols: + \t- \(self.protocols.map { $0.stringValue }.joined(separator: "\n\t- ")) + â„šī¸ MetaData: + \t- \(self.metadata.map { "\($0.key) - \(String(data: Data($0.value), encoding: .utf8) ?? $0.value.description)" }.joined(separator: "\n\t- ")) + 📜 Records: + \t\(self.records.map { "\($0.description.replacingOccurrences(of: "\n", with: "\n\t"))" }.joined(separator: "\n\t")) + \(String(repeating: "-", count: header.count + 2)) + """ + } +} + +public protocol PeerStore: KeyRepository, AddressRepository, ProtocolRepository, MetadataRepository, RecordRepository, + Sendable +{ func all() -> EventLoopFuture<[ComprehensivePeer]> func count() -> EventLoopFuture func dump(peer: PeerID) @@ -48,6 +115,15 @@ extension PeerStore { } } + /// Given a `PeerInfo` object this method adds both the `PeerID` and the associated `Multiaddr`s to the `PeerStore`. + /// - Parameters: + /// - peerInfo: the `PeerInfo` object to store + /// - on: An optional `EventLoop` to return on + /// - Returns: `Void` upon success, or error upon failure + public func add(peerInfo: PeerInfo, on: EventLoop? = nil) async throws { + try await self.add(peerInfo: peerInfo, on: on).get() + } + public func getPeerInfo(byID id: String, on: EventLoop? = nil) -> EventLoopFuture { self.getKey(forPeer: id, on: on).flatMap { key in self.getAddresses(forPeer: key, on: on).map { addresses in @@ -55,6 +131,10 @@ extension PeerStore { } } } + + public func getPeerInfo(byID id: String, on: EventLoop? = nil) async throws -> PeerInfo { + try await self.getPeerInfo(byID: id, on: on).get() + } } public protocol RecordRepository { @@ -167,8 +247,8 @@ extension ProtocolRepository { } } -public struct MetadataBook { - public enum Keys: String { +public struct MetadataBook: Sendable { + public enum Keys: String, Sendable { case AgentVersion = "agentVersion" case ProtocolVersion = "protocolVersion" case Latency = "latency" @@ -178,7 +258,7 @@ public struct MetadataBook { case Discovered = "discovered" } - public struct LatencyMetadata: Codable, CustomStringConvertible { + public struct LatencyMetadata: Codable, CustomStringConvertible, Sendable { public var streamLatency: UInt64 public var connectionLatency: UInt64 public var streamCount: UInt64 @@ -215,8 +295,8 @@ public struct MetadataBook { } } - public struct PrunableMetadata: Codable, CustomStringConvertible { - public enum Prunable: UInt8, Codable { + public struct PrunableMetadata: Codable, CustomStringConvertible, Sendable { + public enum Prunable: UInt8, Codable, Sendable { case prunable = 0 case preferred case necessary diff --git a/Sources/LibP2PCore/Protobufs/Envelope.pb.swift b/Sources/LibP2PCore/Protobufs/Envelope.pb.swift index 6cb4646..5e8765d 100644 --- a/Sources/LibP2PCore/Protobufs/Envelope.pb.swift +++ b/Sources/LibP2PCore/Protobufs/Envelope.pb.swift @@ -11,12 +11,12 @@ // SPDX-License-Identifier: MIT // //===----------------------------------------------------------------------===// - // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. -// Source: Envelope.proto +// Source: Sources/LibP2PCore/Protobufs/Envelope.proto // // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ @@ -41,7 +41,7 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP /// The payload is prefixed with a byte string that determines the type, so it /// can be deserialized deterministically. Often, this byte string is a /// multicodec. -struct EnvelopeMessage { +struct EnvelopeMessage: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -92,36 +92,18 @@ struct EnvelopeMessage { var unknownFields = SwiftProtobuf.UnknownStorage() - enum KeyType: SwiftProtobuf.Enum { - typealias RawValue = Int - case rsa // = 0 - case ed25519 // = 1 - case secp256K1 // = 2 + enum KeyType: Int, SwiftProtobuf.Enum, Swift.CaseIterable { + case rsa = 0 + case ed25519 = 1 + case secp256K1 = 2 init() { self = .rsa } - init?(rawValue: Int) { - switch rawValue { - case 0: self = .rsa - case 1: self = .ed25519 - case 2: self = .secp256K1 - default: return nil - } - } - - var rawValue: Int { - switch self { - case .rsa: return 0 - case .ed25519: return 1 - case .secp256K1: return 2 - } - } - } - struct PublicKey { + struct PublicKey: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -160,24 +142,11 @@ struct EnvelopeMessage { fileprivate var _signature: Data? = nil } -#if swift(>=4.2) - -extension EnvelopeMessage.KeyType: CaseIterable { - // Support synthesized by the compiler. -} - -#endif // swift(>=4.2) - // MARK: - Code below here is support for the SwiftProtobuf runtime. extension EnvelopeMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = "Envelope" - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "public_key"), - 2: .standard(proto: "payload_type"), - 3: .same(proto: "payload"), - 5: .same(proto: "signature"), - ] + static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}public_key\0\u{3}payload_type\0\u{1}payload\0\u{2}\u{2}signature\0") public var isInitialized: Bool { if self._publicKey == nil {return false} @@ -204,18 +173,22 @@ extension EnvelopeMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen } func traverse(visitor: inout V) throws { - if let v = self._publicKey { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._publicKey { try visitor.visitSingularMessageField(value: v, fieldNumber: 1) - } - if let v = self._payloadType { + } }() + try { if let v = self._payloadType { try visitor.visitSingularBytesField(value: v, fieldNumber: 2) - } - if let v = self._payload { + } }() + try { if let v = self._payload { try visitor.visitSingularBytesField(value: v, fieldNumber: 3) - } - if let v = self._signature { + } }() + try { if let v = self._signature { try visitor.visitSingularBytesField(value: v, fieldNumber: 5) - } + } }() try unknownFields.traverse(visitor: &visitor) } @@ -230,19 +203,12 @@ extension EnvelopeMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplemen } extension EnvelopeMessage.KeyType: SwiftProtobuf._ProtoNameProviding { - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 0: .same(proto: "RSA"), - 1: .same(proto: "Ed25519"), - 2: .same(proto: "Secp256k1"), - ] + static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0RSA\0\u{1}Ed25519\0\u{1}Secp256k1\0") } extension EnvelopeMessage.PublicKey: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = EnvelopeMessage.protoMessageName + ".PublicKey" - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "Type"), - 2: .same(proto: "Data"), - ] + static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}Type\0\u{1}Data\0") public var isInitialized: Bool { if self._type == nil {return false} @@ -264,12 +230,16 @@ extension EnvelopeMessage.PublicKey: SwiftProtobuf.Message, SwiftProtobuf._Messa } func traverse(visitor: inout V) throws { - if let v = self._type { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every if/case branch local when no optimizations + // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and + // https://github.com/apple/swift-protobuf/issues/1182 + try { if let v = self._type { try visitor.visitSingularEnumField(value: v, fieldNumber: 1) - } - if let v = self._data { + } }() + try { if let v = self._data { try visitor.visitSingularBytesField(value: v, fieldNumber: 2) - } + } }() try unknownFields.traverse(visitor: &visitor) } diff --git a/Sources/LibP2PCore/Protobufs/PeerRecord.pb.swift b/Sources/LibP2PCore/Protobufs/PeerRecord.pb.swift index 190aa4d..24ac775 100644 --- a/Sources/LibP2PCore/Protobufs/PeerRecord.pb.swift +++ b/Sources/LibP2PCore/Protobufs/PeerRecord.pb.swift @@ -11,12 +11,12 @@ // SPDX-License-Identifier: MIT // //===----------------------------------------------------------------------===// - // DO NOT EDIT. // swift-format-ignore-file +// swiftlint:disable all // // Generated by the Swift generator plugin for the protocol buffer compiler. -// Source: PeerRecord.proto +// Source: Sources/LibP2PCore/Protobufs/PeerRecord.proto // // For information on using the generated types, please see the documentation: // https://github.com/apple/swift-protobuf/ @@ -34,15 +34,16 @@ fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAP typealias Version = _2 } -/// PeerRecord messages contain information that is useful to share with other peers. -/// Currently, a PeerRecord contains the public listen addresses for a peer, but this -/// is expected to expand to include other information in the future. +/// PeerRecord messages contain information that is useful to share with other +/// peers. Currently, a PeerRecord contains the public listen addresses for a +/// peer, but this is expected to expand to include other information in the +/// future. /// /// PeerRecords are designed to be serialized to bytes and placed inside of /// SignedEnvelopes before sharing with other peers. /// See https://github.com/libp2p/go-libp2p-core/record/pb/envelope.proto for /// the SignedEnvelope definition. -struct PeerRecordMessage { +struct PeerRecordMessage: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -50,7 +51,8 @@ struct PeerRecordMessage { /// peer_id contains a libp2p peer id in its binary representation. var peerID: Data = Data() - /// seq contains a monotonically-increasing sequence counter to order PeerRecords in time. + /// seq contains a monotonically-increasing sequence counter to order + /// PeerRecords in time. var seq: UInt64 = 0 /// addresses is a list of public listen addresses for the peer. @@ -60,7 +62,7 @@ struct PeerRecordMessage { /// AddressInfo is a wrapper around a binary multiaddr. It is defined as a /// separate message to allow us to add per-address metadata in the future. - struct AddressInfo { + struct AddressInfo: Sendable { // SwiftProtobuf.Message conformance is added in an extension below. See the // `Message` and `Message+*Additions` files in the SwiftProtobuf library for // methods supported on all messages. @@ -81,11 +83,7 @@ fileprivate let _protobuf_package = "peer.pb" extension PeerRecordMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = _protobuf_package + ".PeerRecord" - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .standard(proto: "peer_id"), - 2: .same(proto: "seq"), - 3: .same(proto: "addresses"), - ] + static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}peer_id\0\u{1}seq\0\u{1}addresses\0") mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { @@ -125,9 +123,7 @@ extension PeerRecordMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem extension PeerRecordMessage.AddressInfo: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { static let protoMessageName: String = PeerRecordMessage.protoMessageName + ".AddressInfo" - static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ - 1: .same(proto: "multiaddr"), - ] + static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}multiaddr\0") mutating func decodeMessage(decoder: inout D) throws { while let fieldNumber = try decoder.nextFieldNumber() { diff --git a/Sources/LibP2PCore/Protocol/Protocol.swift b/Sources/LibP2PCore/Protocol/Protocol.swift index 27ffae7..c411084 100644 --- a/Sources/LibP2PCore/Protocol/Protocol.swift +++ b/Sources/LibP2PCore/Protocol/Protocol.swift @@ -21,13 +21,13 @@ struct ID { static let Testing = "/p2p/_testing" } -public protocol LibP2PProtocol { +public protocol LibP2PProtocol: Sendable { var proto: String { get } var stringValue: String { get } var version: SemanticVersion { get } } -public protocol SemanticVersion { +public protocol SemanticVersion: Sendable { var stringValue: String { get } } @@ -65,7 +65,7 @@ public final class ProtocolRouteHandler: ChannelInboundHandler { } } -public protocol ProtocolHandler { +public protocol ProtocolHandler: Sendable { func handleData(_ data: [UInt8]) -> EventLoopFuture func write(_ data: [UInt8]) } @@ -73,8 +73,8 @@ public protocol ProtocolHandler { /// Semantic Versioning 2.0.0 /// /// [Spec](https://semver.org/#semantic-versioning-200) -public struct SemVerProtocol: Equatable { - public enum SemVersion: Equatable { +public struct SemVerProtocol: Equatable, Hashable, Sendable { + public enum SemVersion: Equatable, Hashable, Sendable { case exact(ProtocolVersion) case from(ProtocolVersion) case upToNextMinor(ProtocolVersion) @@ -125,7 +125,7 @@ public struct SemVerProtocol: Equatable { } - public struct ProtocolVersion: Equatable { + public struct ProtocolVersion: Equatable, Hashable, Sendable { let major: Int let minor: Int let patch: Int diff --git a/Sources/LibP2PCore/PubSub/PubSub.swift b/Sources/LibP2PCore/PubSub/PubSub.swift index 65e3860..4faffe0 100644 --- a/Sources/LibP2PCore/PubSub/PubSub.swift +++ b/Sources/LibP2PCore/PubSub/PubSub.swift @@ -16,7 +16,7 @@ import NIOCore public enum PubSub { /// ValidationResult represents the decision of an extended validator - public enum ValidationResult { + public enum ValidationResult: Sendable { /// Accept is a validation decision that indicates a valid message that should be accepted and delivered to the application and forwarded to the network. case accept /// Reject is a validation decision that indicates an invalid message that should not be delivered to the application or forwarded to the application. Furthermore the peer that forwarded the message should be penalized by peer scoring routers. @@ -27,7 +27,7 @@ public enum PubSub { case throttle } - public enum SubscriptionEvent { + public enum SubscriptionEvent: Sendable { case newPeer(PeerID) case data(PubSubMessage) case error(Error) @@ -41,7 +41,7 @@ public enum PubSub { } } - public struct SubscriptionConfig { + public struct SubscriptionConfig: Sendable { public let topic: String public let signaturePolicy: SignaturePolicy public let validator: ValidatorFunction @@ -60,19 +60,19 @@ public enum PubSub { } } - public enum SignaturePolicy { + public enum SignaturePolicy: Sendable { case strictSign case strictNoSign } - public enum ValidatorFunction { + public enum ValidatorFunction: Sendable { /// Doesn't perform any Message Validation, accepts all inbound messages case acceptAll /// Perform custom Message Validation on each inbound message /// Ex: Rejecting messages based on their address, peerID or any other metric - case custom((_: PubSubMessage) -> Bool) + case custom(@Sendable (_: PubSubMessage) -> Bool) - public var validationFunction: ((_: PubSubMessage) -> Bool) { + public var validationFunction: (@Sendable (_: PubSubMessage) -> Bool) { switch self { case .acceptAll: return { _ in true } @@ -151,7 +151,7 @@ public enum PubSub { } } - public enum MessageIDFunction { + public enum MessageIDFunction: Sendable { /// Calculates a Message's ID by hashing the Message Sequence Number and the Message Sender case hashSequenceNumberAndFromFields /// Calculates a Message's ID by hashing the Sequence Number, Sender, Data and Topic fields @@ -159,9 +159,9 @@ public enum PubSub { /// Simply concatenates the messages From data and Sequence Number (default message id function) case concatFromAndSequenceFields /// Specify your own custom method for generating a Message's ID - case custom((_: PubSubMessage) -> Data) + case custom(@Sendable (_: PubSubMessage) -> Data) - public var messageIDFunction: ((_: PubSubMessage) -> Data) { + public var messageIDFunction: (@Sendable (_: PubSubMessage) -> Data) { switch self { case .hashSequenceNumberAndFromFields: return { message in @@ -190,14 +190,14 @@ public enum PubSub { } public enum MessageState { - public enum FilterType { + public enum FilterType: Sendable { case known case unknown case full } } - public struct Subscriber { + public struct Subscriber: Sendable { public let id: PeerID public private(set) var inbound: Stream? public private(set) var outbound: Stream? @@ -254,7 +254,7 @@ public enum PubSub { // func messageID(_ msg:PubSubMessage) -> [UInt8] //} -public protocol RPCMessageCore { +public protocol RPCMessageCore: Sendable { var subs: [SubOptsCore] { get } var messages: [PubSubMessage] { get } //var control:ControlMessageCore { get } @@ -262,12 +262,12 @@ public protocol RPCMessageCore { public protocol ControlMessageCore {} -public protocol SubOptsCore { +public protocol SubOptsCore: Sendable { var subscribe: Bool { get } var topicID: String { get } } -public protocol PubSubMessage: CustomStringConvertible { +public protocol PubSubMessage: CustomStringConvertible, Sendable { var from: Data { get } var data: Data { get } var seqno: Data { get } @@ -291,7 +291,7 @@ extension PubSubMessage { } } -public protocol PubSubCore: EventLoopService, AnyObject { +public protocol PubSubCore: EventLoopService, AnyObject, Sendable { static var multicodec: String { get } func start() throws @@ -338,27 +338,12 @@ public protocol PeerConnectionDelegate { /// Use these protocols to abstract away the specifics for both PeerState and MessageCache /// Like FloodSub might have a basic implementation while GossipSub has a more complex one. Either way, PubSubBase shouldn't care. -//public protocol PeerStateProtocol:EventLoopService, PeerConnectionDelegate { -// func addNewPeer(_ peer:PeerInfo) -> EventLoopFuture -// func removePeer(_ peer:PeerID) -> EventLoopFuture -// func update(topics:[String], for peer:PeerID) -> EventLoopFuture -// func update(subscriptions:[String:Bool], for peer:PeerID) -> EventLoopFuture -// func peersSubscribedTo(topic:String, on loop:EventLoop?) -> EventLoopFuture<[PeerID]> -// func peersSubscribedTo2(topic:String, on loop:EventLoop?) -> EventLoopFuture<[(PeerID, Stream)]> -// func topicSubscriptions(on loop:EventLoop?) -> EventLoopFuture<[String]> -// func streamFor(_ peer:PeerID) -> EventLoopFuture -// func isFullPeer(_ peer:PeerID) -> EventLoopFuture -// func makeFullPeer(_ peer:PeerID, for topic:String) -> EventLoopFuture -// func makeMetaPeer(_ peer:PeerID, for topic:String) -> EventLoopFuture -// func subscribeSelf(to topic:String, on loop:EventLoop?) -> EventLoopFuture<[String]> -// func unsubscribeSelf(from topic:String, on loop:EventLoop?) -> EventLoopFuture<[String]> -// //func peerExists(_ peer:PeerID, atAddress address:Multiaddr, on loop:EventLoop?) -> EventLoopFuture -//} - public protocol PeerStateProtocol: EventLoopService, PeerConnectionDelegate { // Add and Remove Peers func addNewPeer(_ peer: PeerID, on: EventLoop?) -> EventLoopFuture func removePeer(_ peer: PeerID, on: EventLoop?) -> EventLoopFuture + func getAllPeers(on loop: EventLoop?) -> EventLoopFuture<[PubSub.Subscriber]> + //func peerExists(_ peer:PeerID, atAddress address:Multiaddr, on loop:EventLoop?) -> EventLoopFuture // Attach inbound & outbound streams to existing peer func attachInboundStream(_ peer: PeerID, inboundStream: Stream, on: EventLoop?) -> EventLoopFuture @@ -370,7 +355,6 @@ public protocol PeerStateProtocol: EventLoopService, PeerConnectionDelegate { func update(topics: [String], for peer: PeerID, on: EventLoop?) -> EventLoopFuture func update(subscriptions: [String: Bool], for peer: PeerID, on: EventLoop?) -> EventLoopFuture func peersSubscribedTo(topic: String, on loop: EventLoop?) -> EventLoopFuture<[PubSub.Subscriber]> - func getAllPeers(on loop: EventLoop?) -> EventLoopFuture<[PubSub.Subscriber]> func topicSubscriptions(on loop: EventLoop?) -> EventLoopFuture<[String]> func subscribeSelf(to topic: String, on loop: EventLoop?) -> EventLoopFuture<[String]> func unsubscribeSelf(from topic: String, on loop: EventLoop?) -> EventLoopFuture<[String]> @@ -378,7 +362,9 @@ public protocol PeerStateProtocol: EventLoopService, PeerConnectionDelegate { // Get a peers inbound / outbound streams func streamsFor(_ peer: PeerID, on: EventLoop?) -> EventLoopFuture - //func peerExists(_ peer:PeerID, atAddress address:Multiaddr, on loop:EventLoop?) -> EventLoopFuture + //func isFullPeer(_ peer:PeerID) -> EventLoopFuture + //func makeFullPeer(_ peer:PeerID, for topic:String) -> EventLoopFuture + //func makeMetaPeer(_ peer:PeerID, for topic:String) -> EventLoopFuture } /// Use these protocols to abstract away the specifics for both PeerState and MessageCache diff --git a/Sources/LibP2PCore/Record/PeerRecord.swift b/Sources/LibP2PCore/Record/PeerRecord.swift index 4ad40b2..8f364d5 100644 --- a/Sources/LibP2PCore/Record/PeerRecord.swift +++ b/Sources/LibP2PCore/Record/PeerRecord.swift @@ -12,10 +12,10 @@ // //===----------------------------------------------------------------------===// -public final class PeerRecord: Record { +public final class PeerRecord: Record, Hashable, Sendable { //static let domain:String = "libp2p-peer-record" - static let codec: Codecs = .libp2p_peer_record //.libp2p_peer_record + static let codec: Codecs = .libp2p_peer_record public let peerID: PeerID public let multiaddrs: [Multiaddr] @@ -35,8 +35,8 @@ public final class PeerRecord: Record { } public init(marshaledData: Data) throws { - let pr = try PeerRecordMessage(contiguousBytes: marshaledData) - self.peerID = try PeerID(fromBytesID: pr.peerID.bytes) + let pr = try PeerRecordMessage(serializedBytes: marshaledData) + self.peerID = try PeerID(fromBytesID: pr.peerID.byteArray) self.multiaddrs = try pr.addresses.map { try Multiaddr($0.multiaddr) } @@ -44,11 +44,11 @@ public final class PeerRecord: Record { } public init(marshaledData: Data, withPublicKey pubKey: Data) throws { - let pr = try PeerRecordMessage(contiguousBytes: marshaledData) + let pr = try PeerRecordMessage(serializedBytes: marshaledData) let validatingPubKey = try PeerID(marshaledPublicKey: pubKey) - guard pr.peerID.bytes == validatingPubKey.bytes else { + guard pr.peerID.byteArray == validatingPubKey.id else { print("Error: PubKey Bytes Don't Match") - print(pr.peerID.bytes.asString(base: .base16)) + print(pr.peerID.byteArray.asString(base: .base16)) print(validatingPubKey.b58String) throw Errors.noPublicKey } @@ -72,14 +72,14 @@ public final class PeerRecord: Record { public func marshal() throws -> [UInt8] { var rec = PeerRecordMessage() - rec.peerID = Data(self.peerID.bytes) + rec.peerID = Data(self.peerID.id) rec.addresses = try self.multiaddrs.map { var addr = PeerRecordMessage.AddressInfo() addr.multiaddr = try $0.binaryPacked() return addr } rec.seq = self.sequenceNumber - return try rec.serializedData().bytes + return try rec.serializedData().byteArray } public func equals(_ r: R) -> Bool where R: Record { @@ -92,6 +92,11 @@ public final class PeerRecord: Record { lhs.equals(rhs) } + public func hash(into hasher: inout Hasher) { + hasher.combine(self.peerID.id) + hasher.combine(self.multiaddrs) + } + public func seal(withPrivateKey key: PeerID) throws -> Envelope { try SealedEnvelope(record: self, signedWithKey: key) } @@ -101,7 +106,7 @@ public final class PeerRecord: Record { /// This also results in the Multicodec resolving to cidv3 instead of libp2p-peer-record during decoding. /// I guess for now we just use the hardcoded values... public func unsignedPayload() -> [UInt8] { - uVarIntLengthPrefixed(domain.data(using: .utf8)!.bytes) + uVarIntLengthPrefixed(domain.data(using: .utf8)!.byteArray) + uVarIntLengthPrefixed([0x03, 0x01]) //+ uVarIntLengthPrefixed( Multicodec.getPrefix(multiCodec: PeerRecord.codec) ) + uVarIntLengthPrefixed(try! self.marshal()) diff --git a/Sources/LibP2PCore/Record/Record.swift b/Sources/LibP2PCore/Record/Record.swift index 44b338a..f900e43 100644 --- a/Sources/LibP2PCore/Record/Record.swift +++ b/Sources/LibP2PCore/Record/Record.swift @@ -15,7 +15,7 @@ import Multiaddr import PeerID -public protocol Record: Equatable { +public protocol Record: Equatable, Sendable { var peerID: PeerID { get } var multiaddrs: [Multiaddr] { get } var sequenceNumber: UInt64 { get } @@ -44,7 +44,7 @@ public protocol Record: Equatable { } /// An Envelope contains a signed Record -public protocol Envelope: CustomStringConvertible { +public protocol Envelope: CustomStringConvertible, Sendable { var pubKey: PeerID { get } var payloadType: [UInt8] { get } @@ -82,7 +82,7 @@ public protocol Envelope: CustomStringConvertible { func marshal() throws -> [UInt8] } -public enum Errors: Error, CustomStringConvertible { +public enum Errors: Error, CustomStringConvertible, Sendable { case noPrivateKey case noPublicKey case emptyDomain diff --git a/Sources/LibP2PCore/Record/SealedEnvelope.swift b/Sources/LibP2PCore/Record/SealedEnvelope.swift index c47d57f..56b2d54 100644 --- a/Sources/LibP2PCore/Record/SealedEnvelope.swift +++ b/Sources/LibP2PCore/Record/SealedEnvelope.swift @@ -22,7 +22,7 @@ import SwiftProtobuf // string specified when creating and verifying the envelope. You must know the // domain string used to produce the envelope in order to verify the signature // and access the payload. -public final class SealedEnvelope: Envelope { +public struct SealedEnvelope: Envelope, Sendable { // The public key that can be used to verify the signature and derive the peer id of the signer. //PublicKey crypto.PubKey @@ -41,14 +41,6 @@ public final class SealedEnvelope: Envelope { //signature []byte public let signature: [UInt8] - // the unmarshalled payload as a Record, cached on first access via the Record accessor method - //cached Record - //unmarshalError error - //unmarshalOnce sync.Once - lazy var cached: PeerRecord? = { - nil - }() - /// Creates a new Signed & SealedEnvelope containing the specified Record, ready for marsahling and sending to remote peers... public init(record: R, signedWithKey key: PeerID) throws { guard let privKey = key.keyPair?.privateKey else { @@ -61,13 +53,13 @@ public final class SealedEnvelope: Envelope { self.rawPayload = try record.marshal() - self.signature = try privKey.sign(message: Data(record.unsignedPayload())).bytes + self.signature = try privKey.sign(message: Data(record.unsignedPayload())).byteArray } /// Takes a marshalled / serialized Envelope object public init(marshaledEnvelope bytes: [UInt8], verifiedWithPublicKey pubKey: [UInt8]? = nil) throws { //print("Attempting to instantiate a SealedEnvelope from marshaled data") - let env = try EnvelopeMessage(contiguousBytes: bytes) + let env = try EnvelopeMessage(serializedBytes: bytes) //print("We have an Envelope, attempting to extract PublicKey") if let pub = pubKey { self.pubKey = try PeerID(marshaledPublicKey: Data(pub)) @@ -83,11 +75,11 @@ public final class SealedEnvelope: Envelope { } //print("We have a Public Key, proceeding with signature verification") - self.payloadType = env.payloadType.bytes + self.payloadType = env.payloadType.byteArray - self.rawPayload = env.payload.bytes + self.rawPayload = env.payload.byteArray - self.signature = env.signature.bytes + self.signature = env.signature.byteArray guard try verifySignature() else { throw Errors.invalidSignature @@ -103,7 +95,7 @@ public final class SealedEnvelope: Envelope { //pub.type = .rsa //pub.data = try pubKey.marshal() //env.publicKey = pub - env.publicKey = try EnvelopeMessage.PublicKey(contiguousBytes: pubKey.marshal()) + env.publicKey = try EnvelopeMessage.PublicKey(serializedBytes: pubKey.marshal()) //print("Envelope Marshalled PubKey:") //print(pub) env.payloadType = Data(self.payloadType) diff --git a/Sources/LibP2PCore/Routing/Routing.swift b/Sources/LibP2PCore/Routing/Routing.swift index 4767f75..6012a2b 100644 --- a/Sources/LibP2PCore/Routing/Routing.swift +++ b/Sources/LibP2PCore/Routing/Routing.swift @@ -16,7 +16,7 @@ import Multiaddr import NIOCore import PeerID -enum RoutingErrors: Error { +enum RoutingErrors: Error, Sendable { /// ErrNotFound is returned when the router fails to find the requested record. case notFound /// ErrNotSupported is returned when the router doesn't support the given record type/operation. @@ -90,6 +90,8 @@ func keyForPublicKey(id: PeerID) -> String { "/pk/" + id.b58String } +// TODO: This should not be in the global namespace + func getPublicKey(_ store: ValueStore, peer: PeerID, on: EventLoop) -> EventLoopFuture { /// If the PeerID has a public key, just return it if peer.keyPair?.publicKey != nil { diff --git a/Sources/LibP2PCore/Service/EventLoopService.swift b/Sources/LibP2PCore/Service/EventLoopService.swift index 6cc4b94..248620f 100644 --- a/Sources/LibP2PCore/Service/EventLoopService.swift +++ b/Sources/LibP2PCore/Service/EventLoopService.swift @@ -35,7 +35,7 @@ extension EventLoopService { } } -public enum ServiceLifecycleState { +public enum ServiceLifecycleState: Sendable { case starting case started case stopping diff --git a/Sources/LibP2PCore/Topology/Topology.swift b/Sources/LibP2PCore/Topology/Topology.swift index a44f8cf..bd0ecd2 100644 --- a/Sources/LibP2PCore/Topology/Topology.swift +++ b/Sources/LibP2PCore/Topology/Topology.swift @@ -40,19 +40,19 @@ public protocol Topology { } /// An optional Object containing the handler called when a peer is connected or disconnected -public struct TopologyHandler { +public struct TopologyHandler: Sendable { /// called everytime a peer is connected in the topology context. - public let onConnect: (PeerID, Connection) -> Void + public let onConnect: @Sendable (PeerID, Connection) -> Void - public let onNewStream: ((Stream) -> Void)? + public let onNewStream: (@Sendable (Stream) -> Void)? /// called everytime a peer is disconnected in the topology context. - public let onDisconnect: ((PeerID) -> Void)? + public let onDisconnect: (@Sendable (PeerID) -> Void)? public init( - onConnect: @escaping ((PeerID, Connection) -> Void), - onNewStream: ((Stream) -> Void)? = nil, - onDisconnect: ((PeerID) -> Void)? = nil + onConnect: @escaping (@Sendable (PeerID, Connection) -> Void), + onNewStream: (@Sendable (Stream) -> Void)? = nil, + onDisconnect: (@Sendable (PeerID) -> Void)? = nil ) { self.onConnect = onConnect self.onNewStream = onNewStream diff --git a/Sources/LibP2PCore/Transport/Transport.swift b/Sources/LibP2PCore/Transport/Transport.swift index af3f700..39da18f 100644 --- a/Sources/LibP2PCore/Transport/Transport.swift +++ b/Sources/LibP2PCore/Transport/Transport.swift @@ -65,7 +65,7 @@ public enum TransportConfig {} /// `Close`. /// /// For a conceptual overview, see https://docs.libp2p.io/concepts/transport/ -public protocol Transport: CustomStringConvertible { +public protocol Transport: CustomStringConvertible, Sendable { /// The transports Uniqe key descriptor static var key: String { get } diff --git a/Tests/LibP2PCoreTests/LibP2PCoreTests.swift b/Tests/LibP2PCoreTests/LibP2PCoreTests.swift index 8b8a857..f691721 100644 --- a/Tests/LibP2PCoreTests/LibP2PCoreTests.swift +++ b/Tests/LibP2PCoreTests/LibP2PCoreTests.swift @@ -12,12 +12,13 @@ // //===----------------------------------------------------------------------===// -import XCTest +import Testing @testable import LibP2PCore -final class LibP2PCoreTests: XCTestCase { - func testExample() throws { +@Suite("Libp2p Core Tests") +struct LibP2PCoreTests { + @Test func testExample() throws { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct // results. diff --git a/Tests/LibP2PCoreTests/MultiaddrPeerIDTests.swift b/Tests/LibP2PCoreTests/MultiaddrPeerIDTests.swift index fd38c34..24126cf 100644 --- a/Tests/LibP2PCoreTests/MultiaddrPeerIDTests.swift +++ b/Tests/LibP2PCoreTests/MultiaddrPeerIDTests.swift @@ -12,16 +12,17 @@ // //===----------------------------------------------------------------------===// -import XCTest +import Testing @testable import LibP2PCore // These Multiaddr <-> PeerID tests are located here in swift-libp2p-core because // this is the first package in our stack that depends on the two of them -final class MultiaddrPeerIDTests: XCTestCase { +@Suite("Multiaddr PeerID Tests") +struct MultiaddrPeerIDTests { // Make sure we can extract a PeerID from a Multiaddr - func testGetPeerID() throws { + @Test func testGetPeerID() throws { // B58 String let ma1 = try Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN") let peerID1 = try ma1.getPeerID() @@ -36,37 +37,37 @@ final class MultiaddrPeerIDTests: XCTestCase { ) let peerID3 = try ma3.getPeerID() - XCTAssertEqual(peerID1, peerID2) - XCTAssertEqual(peerID1, peerID3) + #expect(peerID1 == peerID2) + #expect(peerID1 == peerID3) // Embedded Public Key let ma4 = try Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/12D3KooWAfPDpPRRRBrmqy9is2zjU5srQ4hKuZitiGmh4NTTpS2d") let peerID4 = try ma4.getPeerID() - XCTAssertEqual(peerID4.type, .isPublic) + #expect(peerID4.type == .isPublic) // Throw when no PeerID is present - XCTAssertThrowsError(try Multiaddr("/dnsaddr/bootstrap.libp2p.io/").getPeerID()) + #expect(throws: Error.self) { try Multiaddr("/dnsaddr/bootstrap.libp2p.io/").getPeerID() } } - func testGetPeerIDEmbeddedEd25519PublicKey() throws { + @Test func testGetPeerIDEmbeddedEd25519PublicKey() throws { let ma1 = try Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/12D3KooWAfPDpPRRRBrmqy9is2zjU5srQ4hKuZitiGmh4NTTpS2d") let embeddedKeyInBytes = try BaseEncoding.decode(ma1.getPeerIDString()!, as: .base58btc) - let peerID1 = try PeerID(fromBytesID: embeddedKeyInBytes.data.bytes) + let peerID1 = try PeerID(fromBytesID: embeddedKeyInBytes.data.byteArray) let ma2 = try Multiaddr("/dnsaddr/bootstrap.libp2p.io/p2p/12D3KooWAfPDpPRRRBrmqy9is2zjU5srQ4hKuZitiGmh4NTTpS2d") let peerID2 = try ma2.getPeerID() - XCTAssertEqual(peerID1, peerID2) - XCTAssertEqual(peerID1.type, .isPublic) - XCTAssertEqual(peerID2.type, .isPublic) + #expect(peerID1 == peerID2) + #expect(peerID1.type == .isPublic) + #expect(peerID2.type == .isPublic) let ma3 = try Multiaddr("/ip4/139.178.91.71/tcp/4001/p2p/QmPoHmYtUt8BU9eiwMYdBfT6rooBnna5fdAZHUaZASGQY8") let peerID3 = try ma3.getPeerID() - XCTAssertEqual(peerID3.type, .idOnly) + #expect(peerID3.type == .idOnly) - XCTAssertEqual(peerID1, peerID3) + #expect(peerID1 == peerID3) } }