Skip to content

Instantly share code, notes, and snippets.

@jafri
Created September 14, 2019 14:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jafri/85c766a07ea22839816fbb35feb0fc5e to your computer and use it in GitHub Desktop.
Save jafri/85c766a07ea22839816fbb35feb0fc5e to your computer and use it in GitHub Desktop.
import Foundation
import EosioSwift
import EosioSwiftVault
/// Signature provider implementation for EOSIO SDK for Swift using Keychain and/or Secure Enclave.
@objc(EosioKey)
public final class EosioKey: NSObject {
private let vault: EosioVault
/// Require biometric identification for all signatures even if the key does not require it. Defaults to `false`.
public var requireBio = false
/// Init an instance of EosioVaultSignatureProvider.
///
/// - Parameters:
/// - accessGroup: The access group to create an instance of EosioVault.
/// - requireBio: Require biometric identification for all signatures even if the key does not require it. Defaults to `false`.
public init(accessGroup: String, requireBio: Bool = false) {
vault = EosioVault(accessGroup: accessGroup)
self.requireBio = requireBio
}
/// Sign a transaction using an instance of EosioVault with the specified accessGroup.
///
/// - Parameters:
/// - request: The transaction signature request.
/// - completion: The transaction signature response.
public func signTransaction(request: EosioTransactionSignatureRequest, completion: @escaping (EosioTransactionSignatureResponse) -> Void) {
var response = EosioTransactionSignatureResponse()
guard let chainIdData = try? Data(hex: request.chainId) else {
response.error = EosioError(.signatureProviderError, reason: "\(request.chainId) is not a valid chain id")
return completion(response)
}
let zeros = Data(repeating: 0, count: 32)
let message = chainIdData + request.serializedTransaction + zeros
sign(message: message, publicKeys: request.publicKeys) { (signatures, error) in
guard let signatures = signatures else {
response.error = error
return completion(response)
}
guard signatures.count > 0 else {
response.error = EosioError(.signatureProviderError, reason: "No signatures")
return completion(response)
}
var signedTransaction = EosioTransactionSignatureResponse.SignedTransaction()
signedTransaction.signatures = signatures
signedTransaction.serializedTransaction = request.serializedTransaction
response.signedTransaction = signedTransaction
completion(response)
}
}
/// Recursive function to sign a message with public keys. If there are multiple keys, the function will sign with the first and call itself with the remaining keys.
private func sign(message: Data, publicKeys: [String], completion: @escaping ([String]?, EosioError?) -> Void) {
guard let firstPublicKey = publicKeys.first else {
return completion([String](), nil)
}
vault.sign(message: message, eosioPublicKey: firstPublicKey, requireBio: requireBio) { [weak self] (signature, error) in
guard let signature = signature else {
return completion(nil, error)
}
var remainingPublicKeys = publicKeys
remainingPublicKeys.removeFirst()
if remainingPublicKeys.count == 0 {
return completion([signature], nil)
}
guard let strongSelf = self else {
return completion(nil, EosioError(.unexpectedError, reason: "self does not exist"))
}
strongSelf.sign(message: message, publicKeys: remainingPublicKeys, completion: { (signatures, error) in
guard let signatures = signatures else {
return completion(nil, error)
}
completion([signature] + signatures, nil)
})
}
}
/// Get all available EOSIO keys for the instance of EosioVault with the specified accessGroup.
///
/// - Parameters:
/// - completion: The available keys response.
public func getAvailableKeys(completion: @escaping (EosioAvailableKeysResponse) -> Void) {
var response = EosioAvailableKeysResponse()
do {
let vaultKeys = try vault.getAllVaultKeys()
response.keys = vaultKeys.compactMap({ (vaultKey) -> String? in
return vaultKey.eosioPublicKey
})
completion(response)
} catch {
response.error = error.eosioError
completion(response)
}
}
@objc func constantsToExport() -> [AnyHashable : Any]! {
return ["initialCount": 0]
}
@objc static func requiresMainQueueSetup() -> Bool {
return true
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment