mirror of https://github.com/oxen-io/session-ios
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
3.7 KiB
Swift
110 lines
3.7 KiB
Swift
5 years ago
|
import CryptoSwift
|
||
|
|
||
|
private extension UInt64 {
|
||
|
|
||
|
fileprivate init(_ decimal: Decimal) {
|
||
|
self.init(truncating: decimal as NSDecimalNumber)
|
||
|
}
|
||
|
|
||
|
// Convert a UInt8 array to a UInt64
|
||
|
fileprivate init(_ bytes: [UInt8]) {
|
||
|
precondition(bytes.count <= MemoryLayout<UInt64>.size)
|
||
|
var value: UInt64 = 0
|
||
|
for byte in bytes {
|
||
|
value <<= 8
|
||
|
value |= UInt64(byte)
|
||
|
}
|
||
|
self.init(value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private extension MutableCollection where Element == UInt8, Index == Int {
|
||
|
|
||
|
/// Increment every element by the given amount
|
||
|
///
|
||
|
/// - Parameter amount: The amount to increment by
|
||
|
/// - Returns: The incremented collection
|
||
|
fileprivate func increment(by amount: Int) -> Self {
|
||
|
var result = self
|
||
|
var increment = amount
|
||
|
for i in (0..<result.count).reversed() {
|
||
|
guard increment > 0 else { break }
|
||
|
let sum = Int(result[i]) + increment
|
||
|
result[i] = UInt8(sum % 256)
|
||
|
increment = sum / 256
|
||
|
}
|
||
|
return result
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* The main proof of work logic.
|
||
|
*
|
||
|
* This was copied from the desktop messenger.
|
||
|
* Ref: libloki/proof-of-work.js
|
||
|
*/
|
||
|
public enum ProofOfWork {
|
||
|
|
||
|
// If this changes then we also have to use something other than UInt64 to support the new length
|
||
|
private static let nonceLength = 8
|
||
|
|
||
|
/// Calculate a proof of work with the given configuration
|
||
|
///
|
||
|
/// Ref: https://bitmessage.org/wiki/Proof_of_work
|
||
|
///
|
||
|
/// - Parameters:
|
||
|
/// - data: The message data
|
||
|
/// - pubKey: The message recipient
|
||
|
/// - timestamp: The timestamp
|
||
|
/// - ttl: The message time to live in milliseconds
|
||
|
/// - Returns: A nonce string or `nil` if it failed
|
||
|
public static func calculate(data: String, pubKey: String, timestamp: UInt64, ttl: UInt64) -> String? {
|
||
|
let payload = createPayload(pubKey: pubKey, data: data, timestamp: timestamp, ttl: ttl)
|
||
|
let target = calcTarget(ttl: ttl, payloadLength: payload.count, nonceTrials: Int(SnodeAPI.powDifficulty))
|
||
|
|
||
|
// Start with the max value
|
||
|
var trialValue = UInt64.max
|
||
|
|
||
|
let initialHash = payload.sha512()
|
||
|
var nonce = [UInt8](repeating: 0, count: nonceLength)
|
||
|
|
||
|
while trialValue > target {
|
||
|
nonce = nonce.increment(by: 1)
|
||
|
|
||
|
// This is different to the bitmessage PoW
|
||
|
// resultHash = hash(nonce + hash(data)) ==> hash(nonce + initialHash)
|
||
|
let resultHash = (nonce + initialHash).sha512()
|
||
|
let trialValueArray = Array(resultHash[0..<8])
|
||
|
trialValue = UInt64(trialValueArray)
|
||
|
}
|
||
|
|
||
|
return nonce.toBase64()
|
||
|
}
|
||
|
|
||
|
/// Get the proof of work payload
|
||
|
private static func createPayload(pubKey: String, data: String, timestamp: UInt64, ttl: UInt64) -> [UInt8] {
|
||
|
let timestampString = String(timestamp)
|
||
|
let ttlString = String(ttl)
|
||
|
let payloadString = timestampString + ttlString + pubKey + data
|
||
|
return payloadString.bytes
|
||
|
}
|
||
|
|
||
|
/// Calculate the target we need to reach
|
||
|
private static func calcTarget(ttl: UInt64, payloadLength: Int, nonceTrials: Int) -> UInt64 {
|
||
|
let two16 = UInt64(pow(2, 16) - 1)
|
||
|
let two64 = UInt64(pow(2, 64) - 1)
|
||
|
|
||
|
// Do all the calculations
|
||
|
let totalLength = UInt64(payloadLength + nonceLength)
|
||
|
let ttlInSeconds = ttl / 1000
|
||
|
let ttlMult = ttlInSeconds * totalLength
|
||
|
|
||
|
// UInt64 values
|
||
|
let innerFrac = ttlMult / two16
|
||
|
let lenPlusInnerFrac = totalLength + innerFrac
|
||
|
let denominator = UInt64(nonceTrials) * lenPlusInnerFrac
|
||
|
|
||
|
return two64 / denominator
|
||
|
}
|
||
|
}
|