# Title `v1.2.0-pre0` - Network Robustness, Fog 1.2.0, Apple Silicon/M1 & Mac Catalyst # Description Added a way for implementing apps to pass in there own `HttpRequester` which can be useful for network robustness. Subspec `LibMobileCoin` now supports Apple Silicon/M1 and Mac Catalyst. Code changes to support Fog v1.2.0. #### `pre1` Changes - Uses the `v1.2.0-pre1` version of `LibMobileCoin` which supports a higher version of `GRPC`. - Added bridging headers - Updated Gemfile/Makefile to use the latest `cocoapods` version `1.11.2` #### Future Work Fix the `docs` steps in the circleci build process. Will require `jazzy` related fixes and testing on Xcode 11. # Changes ## Network Robustness Adds a separate `HTTP` networking architecture. An object conforming to `protocol HttpRequester` can be provided to the `NetworkConfig` object when the `MobileCoinClient` is created. Then the `TransportProtocolOption` can be changed from `grpc` to `http`. > NOTE: This branch will not run because it depends on changes in other submodules that have not yet been merged. ### `HttpRequester` Implementing apps/frameworks should provide an object conforming to this protocol. Our `RestApiRequester` wraps around an `HttpRequester` to communicate with with our services using `protobuf`s. ``` Sources/Network/HttpConnection/HttpRequester.swift ``` ### Attested & Auth Connection Wrappers `HTTP` versions of the Auth & Attested wrapper classes. These protocol and their default implementations handle the logic paths needed for authentication/attestation and re-authentication/attestation: > Can become generic ``` Sources/Network/HttpConnection/ArbitraryHttpConnection.swift Sources/Network/HttpConnection/AttestedHttpConnection.swift ``` ### Networking Protocols Separate code-paths for `HTTP` versions of the networking protocols: ``` Sources/Network/HttpConnection/HttpCallable/AttestedHttpCallable.swift Sources/Network/HttpConnection/HttpCallable/AuthHttpCallable.swift Sources/Network/HttpConnection/HttpCallable/AuthHttpCallableClientWrapper.swift Sources/Network/HttpConnection/HttpCallable/HttpCallable.swift ``` `HTTP` "interfaces" that closely mimic functionality from `GRPC`. Allows us to re-use more of our existing patterns. ``` Sources/Network/HttpConnection/HTTPInterface/HTTPCallOptions.swift Sources/Network/HttpConnection/HTTPInterface/HTTPClient.swift Sources/Network/HttpConnection/HTTPInterface/HTTPClientCall.swift Sources/Network/HttpConnection/HTTPInterface/HTTPMethod.swift Sources/Network/HttpConnection/HTTPInterface/HTTPResponse.swift Sources/Network/HttpConnection/HTTPInterface/HTTPStatus.swift Sources/Network/HttpConnection/HTTPInterface/HTTPUnaryCall.swift ``` ### `HTTP` Connection Implementations Wrapper classes that interface directly with `protoc-swift` generated `.swift` files for our protobuf models. ``` Sources/Network/HttpConnection/HttpConnection.swift Sources/Network/HttpConnection/HttpConnections/BlockchainHttpConnection.swift Sources/Network/HttpConnection/HttpConnections/ConsensusHttpConnection.swift Sources/Network/HttpConnection/HttpConnections/FogBlockHttpConnection.swift Sources/Network/HttpConnection/HttpConnections/FogKeyImageHttpConnection.swift Sources/Network/HttpConnection/HttpConnections/FogMerkleProofHttpConnection.swift Sources/Network/HttpConnection/HttpConnections/FogReportHttpConnection.swift Sources/Network/HttpConnection/HttpConnections/FogUntrustedTxOutHttpConnection.swift Sources/Network/HttpConnection/HttpConnections/FogViewHttpConnection.swift ``` ### `HTTP` versions of `protoc-swift` generated models The GRPC versions of these are generated by `protoc-swift`. The HTTP versions were edited by hand to work with the `HTTP` Connections implementations. > **This could be automated preferably with a `protoc-swift` plugin template but also `sed`/`VIM` if needed.** ``` Sources/Network/HttpConnection/HttpConnections/Http Proto Generated/attest.http.swift Sources/Network/HttpConnection/HttpConnections/Http Proto Generated/consensus_client.http.swift Sources/Network/HttpConnection/HttpConnections/Http Proto Generated/consensus_common.http.swift Sources/Network/HttpConnection/HttpConnections/Http Proto Generated/ledger.http.swift Sources/Network/HttpConnection/HttpConnections/Http Proto Generated/report.http.swift Sources/Network/HttpConnection/HttpConnections/Http Proto Generated/view.http.swift ``` ## Fog Updates The latest version of `fog` changes the name of a protobuf `FogLedger_Block` -> `FogLedger_BlockData`. ### Compressed Commitment The `TxOut` compressed commitment is no longer sent in the protobuf message because it can be reconstructed with its constituent parts (+ the user's `view_private_key`) We now reconstruct the commitment in several places whereas before it was being returned from the decoded protobuf message. Lastly some function signatures into `LibMobileCoin` were updated to adjust to the changes. ## Miscellaneous New MrEnclave values Support for Apple Silicon/M1 & Mac Catalyst ## Unit Tests One unit test was removed. It creates a TxOut and tries to unmask the value with an **incorrect** private view key. The return value should be 'nil' but is noise. It will require a change in the rust code and should be implemented in a future release. Some objects were re-serialized to match the new TxOut structure. Credentials are not required for `consensus` so this has been changed in the `NetworkConfig` Tests can be run with `TransportProtocol == .http` by changing the default value in `NetworkConfig`
112 lines
3.9 KiB
Swift
112 lines
3.9 KiB
Swift
//
|
|
// Copyright (c) 2020-2021 MobileCoin. All rights reserved.
|
|
//
|
|
|
|
// swiftlint:disable all
|
|
|
|
import Foundation
|
|
import SwiftProtobuf
|
|
import NIOSSL
|
|
|
|
public protocol HttpRequester {
|
|
func request(
|
|
url: URL,
|
|
method: HTTPMethod,
|
|
headers: [String: String]?,
|
|
body: Data?,
|
|
completion: @escaping (Result<HTTPResponse, Error>) -> Void)
|
|
}
|
|
|
|
public class RestApiRequester {
|
|
let requester: HttpRequester
|
|
let baseUrl: URL
|
|
let trustRoots: [NIOSSLCertificate]?
|
|
let prefix: String = "gw"
|
|
var challengeDelegate: URLSessionDelegate?
|
|
|
|
init(requester: HttpRequester, baseUrl: URL, trustRoots: [NIOSSLCertificate]? = []) {
|
|
self.requester = requester
|
|
self.baseUrl = baseUrl
|
|
self.trustRoots = trustRoots
|
|
}
|
|
}
|
|
|
|
protocol Requester {
|
|
func makeRequest<T: HTTPClientCall>(call: T, completion: @escaping (HttpCallResult<T.ResponsePayload>) -> Void)
|
|
}
|
|
|
|
extension RestApiRequester : Requester {
|
|
private func completeURL(path: String) -> URL? {
|
|
.prefix(baseUrl, pathComponents:[prefix, path])
|
|
}
|
|
|
|
public func makeRequest<T: HTTPClientCall>(call: T, completion: @escaping (HttpCallResult<T.ResponsePayload>) -> Void) {
|
|
guard let url = completeURL(path: call.path) else {
|
|
completion(HttpCallResult(status: HTTPStatus(code: 1, message: "could not construct URL")))
|
|
return
|
|
}
|
|
|
|
var request = URLRequest(url: url.absoluteURL)
|
|
request.addProtoHeaders()
|
|
request.addHeaders(call.options?.headers ?? [:])
|
|
|
|
do {
|
|
request.httpBody = try call.requestPayload?.serializedData()
|
|
} catch let error {
|
|
completion(HttpCallResult(status: HTTPStatus(code: 1, message: error.localizedDescription)))
|
|
}
|
|
|
|
requester.request(url: url, method: call.method, headers: request.allHTTPHeaderFields, body: request.httpBody) { result in
|
|
switch result {
|
|
case .failure(let error):
|
|
completion(HttpCallResult(status: HTTPStatus(code: 1, message: error.localizedDescription)))
|
|
case .success(let httpResponse):
|
|
let response = httpResponse.httpUrlResponse
|
|
|
|
logger.info("Http Request url: \(url)")
|
|
logger.info("Status code: \(response.statusCode)")
|
|
|
|
let responsePayload : T.ResponsePayload? = {
|
|
guard let data = httpResponse.responseData,
|
|
let responsePayload = try? T.ResponsePayload.init(serializedData: data)
|
|
else {
|
|
return nil
|
|
}
|
|
return responsePayload
|
|
}()
|
|
|
|
let result = HttpCallResult(status: HTTPStatus(code: response.statusCode, message: ""), metadata: response, response: responsePayload)
|
|
completion(result)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
fileprivate extension URL {
|
|
static func prefix(_ url: URL, pathComponents: [String]) -> URL? {
|
|
let prunedComponents = pathComponents.map({ $0.hasPrefix("/") ? String($0.dropFirst()) : $0})
|
|
var components = URLComponents()
|
|
components.scheme = url.scheme
|
|
components.host = url.host
|
|
components.path = "/" + (url.pathComponents + prunedComponents).joined(separator: "/")
|
|
return components.url
|
|
}
|
|
}
|
|
|
|
fileprivate extension URLRequest {
|
|
mutating func addProtoHeaders() {
|
|
let contentType = (fieldName:"Content-Type", value:"application/x-protobuf")
|
|
self.setValue(contentType.value, forHTTPHeaderField: contentType.fieldName)
|
|
|
|
let accept = (fieldName:"Accept", value:"application/x-protobuf")
|
|
self.addValue(accept.value, forHTTPHeaderField: accept.fieldName)
|
|
}
|
|
|
|
mutating func addHeaders(_ headers: [String:String]) {
|
|
headers.forEach { headerFieldName, value in
|
|
self.setValue(value, forHTTPHeaderField: headerFieldName)
|
|
}
|
|
}
|
|
}
|