Skip to content
Open
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let package = Package(
],
dependencies: [
// Main depedencies
.package(url: "https://github.com/Boilertalk/secp256k1.swift.git", from: "0.1.0"),
.package(url: "https://github.com/21-DOT-DEV/swift-secp256k1.git", from: "0.1.0"),
.package(url: "https://github.com/bitmark-inc/tweetnacl-swiftwrap.git", from: "1.0.2"),
.package(url: "https://github.com/bigearsenal/task-retrying-swift.git", from: "2.0.0"),

Expand All @@ -29,7 +29,7 @@ let package = Package(
.target(
name: "SolanaSwift",
dependencies: [
.product(name: "secp256k1", package: "secp256k1.swift"),
.product(name: "P256K", package: "swift-secp256k1"),
.product(name: "TweetNacl", package: "tweetnacl-swiftwrap"),
.product(name: "Task_retrying", package: "task-retrying-swift"),
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@ public class JSONRPCAPIClient: SolanaAPIClient {
public let endpoint: APIEndPoint
private let networkManager: NetworkManager

public init(endpoint: APIEndPoint, networkManager: NetworkManager = URLSession(configuration: .default)) {
public init(endpoint: APIEndPoint, networkManager: NetworkManager) {

self.endpoint = endpoint
self.networkManager = networkManager
}

public convenience init(endpoint: APIEndPoint) {
self.init(endpoint: endpoint, networkManager: URLSession(configuration: .default))
}

// MARK: -

public func getTransaction(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#if canImport(CommonCrypto)
import CommonCrypto
#endif
//
// ASKKeychain.swift
// AskCoin-HD
//
// Created by 仇弘扬 on 2017/8/14.
// Copyright © 2017年 askcoin. All rights reserved.
//
import CommonCrypto
import Foundation

let BTCKeychainMainnetPrivateVersion: UInt32 = 0x0488_ADE4
Expand Down Expand Up @@ -96,7 +98,7 @@ public class Keychain: NSObject {
guard let prvKey = self.privateKey else {
return nil
}
return CKSecp256k1.generatePublicKey(withPrivateKey: prvKey, compression: true)
return try CKSecp256k1.generatePublicKey(withPrivateKey: prvKey, compression: true)
}()

// MARK: - Extended private key
Expand Down
174 changes: 32 additions & 142 deletions Sources/SolanaSwift/BlockchainClient/Helpers/CKSecp256k1.swift
Original file line number Diff line number Diff line change
@@ -1,156 +1,46 @@
import Foundation
import secp256k1
import P256K

struct CKSecp256k1 {
/*
+ (NSData *)generatePublicKeyWithPrivateKey:(NSData *)privateKeyData compression:(BOOL)isCompression
{
secp256k1_context *context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);

const unsigned char *prvKey = (const unsigned char *)privateKeyData.bytes;
secp256k1_pubkey pKey;

int result = secp256k1_ec_pubkey_create(context, &pKey, prvKey);
if (result != 1) {
return nil;
}

int size = isCompression ? 33 : 65;
unsigned char *pubkey = malloc(size);

size_t s = size;

result = secp256k1_ec_pubkey_serialize(context, pubkey, &s, &pKey, isCompression ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
if (result != 1) {
return nil;
}

secp256k1_context_destroy(context);

NSMutableData *data = [NSMutableData dataWithBytes:pubkey length:size];
free(pubkey);
return data;
}
*/
static func generatePublicKey(withPrivateKey privateKeyData: Data, compression isCompression: Bool) -> Data? {
let context = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN))!
let prvKey = privateKeyData.bytes
var pKey = secp256k1_pubkey()

var result = secp256k1_ec_pubkey_create(context, &pKey, prvKey)
if result != 1 {
return nil
// MARK: - Public key from private key
// Returns compressed (33-byte) or uncompressed (65-byte) pubkey bytes.

static func generatePublicKey(withPrivateKey privateKeyBytes: Data,
compression: Bool) throws -> Data {
let privKey = try P256K.Signing.PrivateKey(dataRepresentation: privateKeyBytes)
if compression {
// format: .compressed → dataRepresentation returns 33 bytes
return privKey.publicKey.dataRepresentation
} else {
return privKey.publicKey.uncompressedRepresentation
}

let size = isCompression ? 33 : 65
let pubkey = UnsafeMutablePointer<UInt8>.allocate(capacity: size)
var s = size

result = secp256k1_ec_pubkey_serialize(
context,
pubkey,
&s,
&pKey,
UInt32(isCompression ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED)
)
if result != 1 {
return nil
}

secp256k1_context_destroy(context)

let data = NSMutableData(bytes: pubkey, length: size).copy()
return data as? Data
}

/*
+ (NSData *)compactSignData:(NSData *)msgData withPrivateKey:(NSData *)privateKeyData
{
secp256k1_context *context = secp256k1_context_create(SECP256K1_CONTEXT_SIGN);

const unsigned char *prvKey = (const unsigned char *)privateKeyData.bytes;
const unsigned char *msg = (const unsigned char *)msgData.bytes;

unsigned char *siga = malloc(64);
secp256k1_ecdsa_signature sig;
int result = secp256k1_ecdsa_sign(context, &sig, msg, prvKey, NULL, NULL);

result = secp256k1_ecdsa_signature_serialize_compact(context, siga, &sig);

if (result != 1) {
return nil;
}

secp256k1_context_destroy(context);
// MARK: - Sign — returns 64-byte compact (r || s)

NSMutableData *data = [NSMutableData dataWithBytes:siga length:64];
free(siga);
return data;
}
*/
static func compactSignData(msgData: Data, withPrivateKey privateKeyData: Data) -> Data? {
let context = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_SIGN))!
let prvKey = privateKeyData.bytes
let msg = msgData.bytes
let siga = UnsafeMutablePointer<UInt8>.allocate(capacity: 64)
var sig = secp256k1_ecdsa_signature()
static func sign(message: Data, with privateKeyBytes: Data) throws -> Data {
let privKey = try P256K.Signing.PrivateKey(dataRepresentation: privateKeyBytes)
let sig = try privKey.signature(for: message)
return sig.compactRepresentation
}

var result = secp256k1_ecdsa_sign(context, &sig, msg, prvKey, nil, nil)
result = secp256k1_ecdsa_signature_serialize_compact(context, siga, &sig)
// MARK: - Verify

if result != 1 {
return nil
static func verify(signature signatureData: Data,
message messageData: Data,
publicKeyData: Data) throws -> Bool {
let format: P256K.Format = publicKeyData.count == 33 ? .compressed : .uncompressed
guard publicKeyData.count == 33 || publicKeyData.count == 65 else {
throw CKSecp256k1Error.invalidPublicKeyLength
}

secp256k1_context_destroy(context)

let data = NSMutableData(bytes: siga, length: 64).copy()
return data as? Data
let pubKey = try P256K.Signing.PublicKey(dataRepresentation: publicKeyData,
format: format)
let sig = try P256K.Signing.ECDSASignature(compactRepresentation: signatureData)
return pubKey.isValidSignature(sig, for: messageData)
}
}

/*
+ (NSInteger)verifySignedData:(NSData *)sigData withMessageData:(NSData *)msgData usePublickKey:(NSData *)pubKeyData
{
secp256k1_context *context = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);

const unsigned char *sig = (const unsigned char *)sigData.bytes;
const unsigned char *msg = (const unsigned char *)msgData.bytes;

const unsigned char *pubKey = (const unsigned char *)pubKeyData.bytes;

secp256k1_pubkey pKey;
int pubResult = secp256k1_ec_pubkey_parse(context, &pKey, pubKey, pubKeyData.length);
if (pubResult != 1) return -3;

secp256k1_ecdsa_signature sig_ecdsa;
int sigResult = secp256k1_ecdsa_signature_parse_compact(context, &sig_ecdsa, sig);
if (sigResult != 1) return -4;

int result = secp256k1_ecdsa_verify(context, &sig_ecdsa, msg, &pKey);

secp256k1_context_destroy(context);
return result;
}*/
static func verifySignedData(sigData: Data, withMessageData msgData: Data,
usePublickKey pubKeyData: Data) -> Int32
{
let context = secp256k1_context_create(UInt32(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))!
let sig = sigData.bytes
let msg = msgData.bytes

let pubKey = pubKeyData.bytes
var pKey = secp256k1_pubkey()

let pubResult = secp256k1_ec_pubkey_parse(context, &pKey, pubKey, pubKeyData.count)
if pubResult != 1 { return -3 }

var sig_ecdsa = secp256k1_ecdsa_signature()
let sigResult = secp256k1_ecdsa_signature_parse_compact(context, &sig_ecdsa, sig)
if sigResult != 1 { return -4 }

let result = secp256k1_ecdsa_verify(context, &sig_ecdsa, msg, &pKey)

secp256k1_context_destroy(context)
return result
}
enum CKSecp256k1Error: Error {
case invalidPublicKeyLength
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#if canImport(Accelerate)
import Accelerate.vecLib
#endif
import Foundation

public typealias UInt1X = FixedWidthInteger & BinaryInteger & UnsignedInteger & Codable
Expand Down
3 changes: 3 additions & 0 deletions Sources/SolanaSwift/Extensions/URLRequest+Extensions.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

public extension URLRequest {
func cURL(pretty: Bool = false) -> String {
Expand Down
5 changes: 5 additions & 0 deletions Sources/SolanaSwift/Helpers/URLSession+NetworkManager.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

#if canImport(FoundationNetworking)
extension URLSession: NetworkManager {
public func requestData(request: URLRequest) async throws -> Data {
let (data, _): (Data, URLResponse)
(data, _) = try await self.data(for: request)
return data
}
}
#endif // canImport(FoundationNetworking)
4 changes: 4 additions & 0 deletions Sources/SolanaSwift/Socket/SocketInterfaces.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ public protocol WebSocketTaskProvider {
func createWebSocketTask(with url: URL) -> WebSocketTask
}

#if canImport(FoundationNetworking) || canImport(Darwin)
extension URLSession: WebSocketTaskProvider {
public func createWebSocketTask(with url: URL) -> WebSocketTask {
webSocketTask(with: url)
}
}
#endif

/// Abstract websocket task, default is URLSessionWebSocketTask
public protocol WebSocketTask {
Expand All @@ -21,7 +23,9 @@ public protocol WebSocketTask {
func sendPing(pongReceiveHandler: @escaping (Error?) -> Void)
}

#if canImport(FoundationNetworking) || canImport(Darwin)
extension URLSessionWebSocketTask: WebSocketTask {}
#endif

/// Delegate for listening socket's events
public protocol SolanaSocketEventsDelegate: AnyObject {
Expand Down