From 1eed630af8ad682c0c95f65b0a3fd53f405b780a Mon Sep 17 00:00:00 2001 From: Mikunj Date: Thu, 2 May 2019 12:40:42 +1000 Subject: [PATCH] Use UInt64 instead of Decimal when calculating proof of work. Fix target calculations in proof of work. Fix incorrect greater than comparison between UInt8 arrays. --- LokiKit/ProofOfWork.swift | 110 +++++++++++++++----------------------- 1 file changed, 43 insertions(+), 67 deletions(-) diff --git a/LokiKit/ProofOfWork.swift b/LokiKit/ProofOfWork.swift index 20fc73a10..368b0bebd 100644 --- a/LokiKit/ProofOfWork.swift +++ b/LokiKit/ProofOfWork.swift @@ -1,63 +1,40 @@ import CryptoSwift -private extension Int { +private extension UInt64 { init(_ decimal: Decimal) { - let double = NSDecimalNumber(decimal: decimal).doubleValue - self.init(double) + self.init(truncating: decimal as NSDecimalNumber) } } -private extension UInt8 { - init(_ decimal: Decimal) { - self.init(Int(decimal)) - } -} - -private extension Decimal { - /// Get the remainder of a Decimal - static func %(lhs: Decimal, rhs: Int) -> Decimal { - return Decimal(Int(lhs) % rhs) - } +// UInt8 Array specific stuff we need +private extension Array where Element == UInt8 { - /// Divide a Decimal by an Int - static func /(lhs: Decimal, rhs: Int) -> Decimal { - return lhs / Decimal(rhs) - } - - /// Convert a Decimal to a UInt8 array of a given length - func toArray(ofLength length: Int) -> [UInt8] { - return (0..> $0 & 0x000000FF) } + self.init(array) } -} - -// UInt8 Array specific stuff we need -private extension Array where Element == UInt8 { /// Compare if lhs array is greater than rhs array static func >(lhs: [UInt8], rhs: [UInt8]) -> Bool { guard lhs.count == rhs.count else { return false } - // lhs is greater than rhs if any value in lhs is greater than the corresponding value in the rhs - return zip(lhs, rhs).contains { $0 > $1 } + for i in (0.. rhs[i] + } + return false } - /// Increment the UInt8 array by a given amount /// /// - Parameter amount: The amount to increment by /// - Returns: The incrememnted array func increment(by amount: Int) -> [UInt8] { - var newNonce = [UInt8](self) + var newNonce = self var increment = amount for i in (0.. 0 else { break } @@ -77,26 +54,27 @@ private extension Array where Element == UInt8 { */ public enum ProofOfWork { - static let nonceLength = 8 + // If this changes then we also have to use something other than UInt64 to support the new length + private static let nonceLength = 8 // Modify this value for difficulty scaling - enum NonceTrials { + private enum NonceTrials { static let development = 10 static let production = 100 } - public struct Configuration { + struct Configuration { var pubKey: String var data: String var timestamp: Date - var ttl: UInt + var ttl: Int var isDevelopment = false - func getPayload() -> [UInt8] { - let timestampString = String(timestamp.timeIntervalSince1970) + var payload: [UInt8] { + let timestampString = String(Int(timestamp.timeIntervalSince1970)) let ttlString = String(ttl) let payloadString = timestampString + ttlString + pubKey + data - return [UInt8](payloadString.utf8) + return payloadString.bytes } } @@ -107,16 +85,16 @@ public enum ProofOfWork { /// /// - Parameter config: The configuration data /// - Returns: A nonce string or nil if it failed - public static func calculate(with config: Configuration) -> String? { - let payload = config.getPayload() + static func calculate(with config: Configuration) -> String? { + let payload = config.payload let nonceTrials = config.isDevelopment ? NonceTrials.development : NonceTrials.production let target = calcTarget(ttl: config.ttl, payloadLength: payload.count, nonceTrials: nonceTrials) // Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER - let maxSafeInteger = pow(2, 53) - 1 - var trialValue = maxSafeInteger.toArray(ofLength: nonceLength) + let maxSafeInteger = UInt64(pow(2, 53) - 1) + var trialValue = [UInt8](maxSafeInteger) - let initialHash = [UInt8](config.data.sha512().utf8) + let initialHash = payload.sha512() var nonce = [UInt8](repeating: 0, count: nonceLength) while trialValue > target { @@ -132,25 +110,23 @@ public enum ProofOfWork { } /// Calculate the UInt8 target we need to reach - private static func calcTarget(ttl: UInt, payloadLength: Int, nonceTrials: Int) -> [UInt8] { - let decimalTTL = Decimal(ttl) - let decimalPayloadLength = Decimal(payloadLength) - let decimalNonceTrials = Decimal(nonceTrials) - - let decimalTwo16 = pow(2, 16) - 1 - let decimalTwo64 = pow(2, 64) - 1 - + private static func calcTarget(ttl: Int, payloadLength: Int, nonceTrials: Int) -> [UInt8] { + let two16 = UInt64(pow(2, 16) - 1) + let two64 = UInt64(pow(2, 64) - 1) + // ttl converted to seconds - let ttlSeconds = decimalTTL / 1000 - + let ttlSeconds = ttl / 1000 + // Do all the calculations - let totalLength = decimalPayloadLength + Decimal(nonceLength) - let ttlMult = ttlSeconds * totalLength - let innerFrac = ttlMult / decimalTwo16 + let totalLength = UInt64(payloadLength + nonceLength) + let ttlMult = UInt64(ttlSeconds) * totalLength + + // UInt64 values + let innerFrac = ttlMult / two16 let lenPlusInnerFrac = totalLength + innerFrac - let denominator = decimalNonceTrials * lenPlusInnerFrac - let targetNum = decimalTwo64 / Int(denominator) + let denominator = UInt64(nonceTrials) * lenPlusInnerFrac + let targetNum = two64 / denominator - return targetNum.toArray(ofLength: nonceLength) + return [UInt8](targetNum) } }