Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated BoundWitnessBuilder & Witness Protocols #39

Merged
merged 31 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
39b3f36
Alternatively allow ENV VARS for config
JoelBCarter Nov 11, 2024
0eb1d82
Add default env vars
JoelBCarter Nov 11, 2024
67b815a
Remove hardcoded domains in Panel tests
JoelBCarter Nov 11, 2024
837fbe9
Default to localhost for local dev
JoelBCarter Nov 11, 2024
45b0059
Use defaults in tests
JoelBCarter Nov 11, 2024
e3547b4
Decodable BW
JoelBCarter Nov 11, 2024
a2bdf90
Return tuple from BW builder
JoelBCarter Nov 11, 2024
8f913b2
Destructure tuple from builders
JoelBCarter Nov 11, 2024
c006b1d
Add archivist insert method
JoelBCarter Nov 11, 2024
579f786
Update BW tests to handle tuple response format
JoelBCarter Nov 11, 2024
7511bd1
DRY up URL builder
JoelBCarter Nov 11, 2024
d6c839f
update method signature
JoelBCarter Nov 11, 2024
75dffbc
Notes on Query Bound Witness
JoelBCarter Nov 11, 2024
6978a54
Dump default formatting options to preserve originals where possible
JoelBCarter Nov 11, 2024
d570ddc
Formatting
JoelBCarter Nov 11, 2024
495c7c0
Spelling
JoelBCarter Nov 11, 2024
730abde
Allow for concurrent API requests from SDK
JoelBCarter Nov 11, 2024
ef3e1cb
Namespace thread queue for identification
JoelBCarter Nov 11, 2024
40c8086
Remove `_payloads` from BoundWitness
JoelBCarter Nov 11, 2024
2f25894
Remove previous hash from payloads
JoelBCarter Nov 11, 2024
4812161
Payload extensions
JoelBCarter Nov 11, 2024
85dbb33
Formatting
JoelBCarter Nov 11, 2024
72ed1ad
Documentation
JoelBCarter Nov 11, 2024
f936398
Move Payload extensions to same file
JoelBCarter Nov 11, 2024
372a9e1
Implement witness protocol with associated types for generic implemen…
JoelBCarter Nov 11, 2024
26c0a15
Implement Witness protocol on basic witness
JoelBCarter Nov 11, 2024
86eef8e
Implement Witness protocol on system info witness
JoelBCarter Nov 11, 2024
4209bfc
handle witness arrays in panel report
JoelBCarter Nov 11, 2024
fdad24b
Witness with generic type parameters in protocol
JoelBCarter Nov 11, 2024
964258a
Reduced generics for ease
JoelBCarter Nov 11, 2024
b3bb3e2
Witnesses matching protocol
JoelBCarter Nov 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 64 additions & 4 deletions .swift-format
Original file line number Diff line number Diff line change
@@ -1,10 +1,70 @@
{
"version": 1,
"lineLength": 120,
"fileScopedDeclarationPrivacy": {
"accessLevel": "private"
},
"indentConditionalCompilationBlocks": true,
"indentSwitchCaseLabels": false,
"indentation": {
"spaces": 2
},
"lineBreakAroundMultilineExpressionChainComponents": false,
"lineBreakBeforeControlFlowKeywords": false,
"lineBreakBeforeEachArgument": false,
"lineBreakBeforeEachGenericRequirement": false,
"lineLength": 100,
"maximumBlankLines": 1,
"multiElementCollectionTrailingCommas": true,
"noAssignmentInExpressions": {
"allowedFunctions": [
"XCTAssertNoThrow"
]
},
"prioritizeKeepingFunctionOutputTogether": false,
"respectsExistingLineBreaks": true,
"rules": {
"OrderedImports": true
}
"AllPublicDeclarationsHaveDocumentation": false,
"AlwaysUseLiteralForEmptyCollectionInit": false,
"AlwaysUseLowerCamelCase": true,
"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,
"NoPlaygroundLiterals": true,
"NoVoidReturnOnFunctionSignature": true,
"OmitExplicitReturns": false,
"OneCasePerLine": true,
"OneVariableDeclarationPerLine": true,
"OnlyOneTrailingClosureArgument": true,
"OrderedImports": true,
"ReplaceForEachWithForLoop": true,
"ReturnVoidInsteadOfEmptyTuple": true,
"TypeNamesShouldBeCapitalized": true,
"UseEarlyExits": false,
"UseExplicitNilCheckInConditions": true,
"UseLetInEveryBoundCaseVariable": true,
"UseShorthandTypeNames": true,
"UseSingleLinePropertyGetter": true,
"UseSynthesizedInitializer": true,
"UseTripleSlashForDocumentationComments": true,
"UseWhereClausesInForLoops": false,
"ValidateDocumentationComments": false
},
"spacesAroundRangeFormationOperators": false,
"tabWidth": 8,
"version": 1
}
91 changes: 91 additions & 0 deletions .swiftpm/xcode/xcshareddata/xcschemes/XyoClient.xcscheme
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XyoClient"
BuildableName = "XyoClient"
BlueprintName = "XyoClient"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XyoClientTests"
BuildableName = "XyoClientTests"
BlueprintName = "XyoClientTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<EnvironmentVariables>
<EnvironmentVariable
key = "XYO_API_DOMAIN"
value = "http://localhost:8080"
isEnabled = "YES">
</EnvironmentVariable>
<EnvironmentVariable
key = "XYO_API_MODULE"
value = "Archivist"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "XyoClient"
BuildableName = "XyoClient"
BlueprintName = "XyoClient"
ReferencedContainer = "container:">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
31 changes: 29 additions & 2 deletions Sources/XyoClient/ArchivistApi/ArchivistApiClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,33 @@ public class XyoArchivistApiClient {
}
}

public var url: String {
return "\(self.config.apiDomain)/\(self.config.apiModule)"
}

private init(_ config: XyoArchivistApiConfig) {
self.config = config
}

public func insert(payloads: [XyoBoundWitnessJson]) async throws -> XyoBoundWitnessJson {
// TODO: Build query bound witness
// Perform the request and await the result
let responseData = try await AF.request(
self.url,
method: .post,
parameters: payloads,
encoder: JSONParameterEncoder.default
)
.validate()
.serializingData()
.value

// Attempt to decode the response data into XyoBoundWitnessJson
let decodedResponse = try JSONDecoder().decode([XyoBoundWitnessJson].self, from: responseData)
// TODO: Return payloads instead once they're deserializable
return decodedResponse[0]
}

public func postBoundWitnesses(
_ entries: [XyoBoundWitnessJson]
) throws {
Expand All @@ -38,7 +61,7 @@ public class XyoArchivistApiClient {
) throws {
let body = entries
AF.request(
"\(self.config.apiDomain)/\(self.config.apiModule)",
self.url,
method: .post,
parameters: body,
encoder: JSONParameterEncoder.default
Expand Down Expand Up @@ -80,6 +103,10 @@ public class XyoArchivistApiClient {
}

extension XyoArchivistApiClient {
static fileprivate let queue = DispatchQueue(label: "requests.queue", qos: .utility)
static fileprivate let queue = DispatchQueue(
label: "network.xyo.requests.queue",
qos: .utility,
attributes: [.concurrent]
)
static fileprivate let mainQueue = DispatchQueue.main
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

public class XyoBoundWitnessBodyJson: XyoBoundWitnessBodyProtocol, Encodable {
public class XyoBoundWitnessBodyJson: XyoBoundWitnessBodyProtocol, Encodable, Decodable {
enum CodingKeys: String, CodingKey {
case addresses
case payload_hashes
Expand Down
5 changes: 2 additions & 3 deletions Sources/XyoClient/BoundWitness/BoundWitnessBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,19 @@ public class BoundWitnessBuilder {
}
}

public func build(_ previousHash: String? = nil) throws -> XyoBoundWitnessJson {
public func build(_ previousHash: String? = nil) throws -> (XyoBoundWitnessJson, [XyoPayload]) {
let bw = XyoBoundWitnessJson()
let hashable = hashableFields()
let hash = try BoundWitnessBuilder.hash(hashable)
bw._signatures = try self.sign(hash)
bw._hash = hash
bw._client = "swift"
bw._payloads = _payloads
bw._previous_hash = previousHash
bw.addresses = _witnesses.map { witness in witness?.addressHex! }
bw.previous_hashes = _previous_hashes
bw.payload_hashes = _payload_hashes
bw.payload_schemas = _payload_schemas
return bw
return (bw, _payloads)
}

static func hash<T: Encodable>(_ json: T) throws -> String {
Expand Down
3 changes: 0 additions & 3 deletions Sources/XyoClient/BoundWitness/BoundWitnessJson.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ public class XyoBoundWitnessJson: XyoBoundWitnessBodyJson, XyoBoundWitnessMetaPr
enum CodingKeys: String, CodingKey {
case _client
case _hash
case _payloads
case _previous_hash
case _signatures
case addresses
Expand All @@ -16,14 +15,12 @@ public class XyoBoundWitnessJson: XyoBoundWitnessBodyJson, XyoBoundWitnessMetaPr

public var _client: String?
public var _hash: String?
public var _payloads: [XyoPayload]?
public var _signatures: [String?]?
public var _previous_hash: String?

func encodeMetaFields(_ container: inout KeyedEncodingContainer<CodingKeys>) throws {
try container.encode(_client, forKey: ._client)
try container.encode(_hash, forKey: ._hash)
try container.encode(_payloads, forKey: ._payloads)
try container.encode(_signatures, forKey: ._signatures)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Foundation
public protocol XyoBoundWitnessMetaProtocol {
var _client: String? { get set }
var _hash: String? { get set }
var _payloads: [XyoPayload]? { get set }
var _signatures: [String?]? { get set }
var _previous_hash: String? { get set }
}
20 changes: 15 additions & 5 deletions Sources/XyoClient/Payload/XyoPayload.swift
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import Foundation

open class XyoPayload: Encodable {

public init(_ schema: String, _ previousHash: String? = nil) {
public init(_ schema: String) {
self.schema = schema.lowercased()
self.previousHash = previousHash
}

public var schema: String
public var previousHash: String?
}

extension XyoPayload {

/// Generates a SHA-256 hash of the encoded representation of the instance.
///
/// This method serializes the instance using JSON encoding with sorted keys,
/// converts the encoded data into a UTF-8 string, and then applies SHA-256
/// hashing to generate a hash of the instance's contents.
///
/// - Throws:
/// - `BoundWitnessBuilderError.encodingError` if the instance cannot be
/// converted to a UTF-8 string after encoding.
/// - Any error thrown by the `sha256()` function if the hashing process fails.
/// - Returns: A `Data` object containing the SHA-256 hash of the encoded instance.
public func hash() throws -> Data {
let encoder = JSONEncoder()
encoder.outputFormatting = .sortedKeys
Expand Down
16 changes: 9 additions & 7 deletions Sources/XyoClient/XyoPanel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class XyoPanel {
self.init(archivists: [archivist], witnesses: witnesses ?? [])
}

public convenience init(observe: ((_ previousHash: String?) -> XyoEventPayload?)?) {
public convenience init(observe: (() -> XyoEventPayload?)?) {
if observe != nil {
var witnesses = [XyoWitness]()

Expand All @@ -46,7 +46,7 @@ public class XyoPanel {
}

public func event(_ event: String, _ closure: XyoPanelReportCallback?) throws -> [XyoPayload] {
try report([XyoEventWitness { previousHash in XyoEventPayload(event, previousHash) }], closure)
try report([XyoEventWitness { XyoEventPayload(event) }], closure)
}

public func report(
Expand All @@ -59,9 +59,9 @@ public class XyoPanel {
witnesses.append(contentsOf: self._witnesses)
let payloads = witnesses.map { witness in
witness.observe()
}
let bw = try BoundWitnessBuilder()
.payloads(payloads.compactMap { $0 })
}.flatMap({ $0 })
let (bw, _) = try BoundWitnessBuilder()
.payloads(payloads)
.witnesses(witnesses)
.build(_previous_hash)
self._previous_hash = bw._hash
Expand All @@ -86,8 +86,10 @@ public class XyoPanel {
}

struct Defaults {
static let apiModule = "Archivist"
static let apiDomain = "https://beta.api.archivist.xyo.network"
static let apiDomain =
ProcessInfo.processInfo.environment["XYO_API_DOMAIN"]
?? "https://beta.api.archivist.xyo.network"
static let apiModule = ProcessInfo.processInfo.environment["XYO_API_MODULE"] ?? "Archivist"
}

private static var defaultArchivist: XyoArchivistApiClient {
Expand Down
19 changes: 12 additions & 7 deletions Sources/XyoClient/XyoWitness/Basic/BasicWitness.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,28 @@ import Foundation

open class XyoBasicWitness: XyoWitness {

public init(_ observer: @escaping ObserverClosure) {
public typealias TPayloadOut = XyoPayload

public init(observer: @escaping ObserverClosure) {
_observer = observer
super.init()
}

public init(_ address: XyoAddress, _ observer: @escaping ObserverClosure) {
public init(address: XyoAddress, observer: @escaping ObserverClosure) {
_observer = observer
super.init(address)
super.init(address: address)
}

public typealias ObserverClosure = ((_ previousHash: String?) -> XyoPayload?)

private let _observer: ObserverClosure

override public func observe() -> XyoPayload? {
let payload = _observer(previousHash)
previousHash = try? payload?.hash().toHex()
return payload
override public func observe() -> [XyoPayload] {
if let payload = _observer(previousHash) {
previousHash = try? payload.hash().toHex()
return [payload]
} else {
return []
}
}
}
Loading
Loading