diff --git a/Sources/secp256k1/UInt256.swift b/Sources/secp256k1/UInt256.swift new file mode 120000 index 00000000..8b5f285e --- /dev/null +++ b/Sources/secp256k1/UInt256.swift @@ -0,0 +1 @@ +../zkp/UInt256.swift \ No newline at end of file diff --git a/Sources/zkp/Asymmetric.swift b/Sources/zkp/Asymmetric.swift index 662bf539..6fcfef1e 100644 --- a/Sources/zkp/Asymmetric.swift +++ b/Sources/zkp/Asymmetric.swift @@ -101,6 +101,15 @@ public extension secp256k1 { } } + /// Creates a secp256k1 private key for signing from a data representation. + /// + /// - Parameter data: A raw representation of the key. + /// - Parameter format: The key format, default is .compressed. + /// - Throws: An error if the raw representation does not create a private key for signing. + @available(macOS 13.3, *) public init(_ staticInt: UInt256, format: secp256k1.Format = .compressed) throws { + self.baseKey = try PrivateKeyImplementation(rawRepresentation: staticInt.rawValue, format: format) + } + /// Determines if two private keys are equal. /// /// - Parameters: diff --git a/Sources/zkp/UInt256.swift b/Sources/zkp/UInt256.swift new file mode 100644 index 00000000..cb6e993b --- /dev/null +++ b/Sources/zkp/UInt256.swift @@ -0,0 +1,962 @@ +// +// UInt256.swift +// GigaBitcoin/secp256k1.swift +// +// Modifications Copyright (c) 2024 GigaBitcoin LLC +// Distributed under the MIT software license +// +// See the accompanying file LICENSE for information +// +// +// NOTICE: THIS FILE HAS BEEN MODIFIED BY GigaBitcoin LLC +// UNDER COMPLIANCE WITH THE APACHE 2.0 LICENSE FROM THE +// ORIGINAL WORK OF THE COMPANY Apple Inc. +// +// THE FOLLOWING IS THE COPYRIGHT OF THE ORIGINAL DOCUMENT: +// +// +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// RUN: %target-run-simple-swift(-parse-as-library) +// REQUIRES: executable_test +// REQUIRES: reflection +// UNSUPPORTED: freestanding +// END. +// +//===----------------------------------------------------------------------===// + +import Foundation + +/// A larger fixed-width integer, stored as a SIMD vector of words. +/// +/// FIXME: Not implemented yet. +/// - ``FixedWidthInteger.multipliedFullWidth(by:)`` +/// - ``FixedWidthInteger.dividingFullWidth(_:)`` + +public protocol SIMDWordsInteger: Codable, + CustomDebugStringConvertible, + CustomReflectable, + FixedWidthInteger, + Sendable + where + Magnitude: SIMDWordsInteger, + Magnitude.Magnitude == Magnitude, + Magnitude.Stride == Stride, + Magnitude.Vector == Vector, + Magnitude.Words == Words, + Stride: SIMDWordsInteger, + Stride.Magnitude == Magnitude, + Stride.Stride == Stride, + Stride.Vector == Vector, + Stride.Words == Words { + associatedtype Vector: Sendable & SIMD + + var vector: Vector { get set } + + init(_ vector: Vector) +} + +public extension SIMDWordsInteger where Self == Stride { + /// Creates a new instance with the same memory representation as the given + /// value. + /// + /// - Parameter source: A value of an associated type. + + @inlinable + init(bitPattern source: Magnitude) { + self.init(source.vector) + } +} + +public extension SIMDWordsInteger where Self == Magnitude { + /// Creates a new instance with the same memory representation as the given + /// value. + /// + /// - Parameter source: A value of an associated type. + + @inlinable + init(bitPattern source: Stride) { + self.init(source.vector) + } +} + +public extension SIMDWordsInteger { + /// Creates a new instance from the given integer, if it can be represented + /// exactly. + /// + /// - Parameter source: An immutable arbitrary-precision signed integer. + @available(macOS 13.3, *) + init?(exactly source: StaticBigInt) { + if source.signum() == 0 { + self = .zero + } else { + if Self.isSigned { + guard source.bitWidth <= Self.bitWidth else { return nil } + } else { + guard source.bitWidth <= Self.bitWidth + 1 else { return nil } + guard source.signum() >= 0 else { return nil } + } + self.init(truncatingIfNeeded: source) + } + } + + /// Creates a new instance from the *bit pattern* of the given integer, by + /// truncating or sign extending its binary representation to fit this type. + /// + /// - Parameter source: An immutable arbitrary-precision signed integer. + @available(macOS 13.3, *) + init(truncatingIfNeeded source: StaticBigInt) { + var vector = Vector.zero + for index in vector.indices { + vector[index] = source[index] + } + self.init(vector) + } +} + +extension SIMDWordsInteger { + @inlinable + var _doubleWidth: (high: Self, low: Magnitude) { + (high: _isNegative ? ~Self.zero : Self.zero, low: Magnitude(vector)) + } + + @inlinable + var _isNegative: Bool { + Self.isSigned && Int(bitPattern: vector[Vector.scalarCount - 1]) < 0 + } + + @inlinable + var _isNonnegative: Bool { + !_isNegative + } + + @inlinable + var _isNonzero: Bool { + self != .zero + } + + @inlinable + var _isPowerOfTwo: Bool { + _isNonnegative && nonzeroBitCount == 1 + } + + @inlinable + var _isZero: Bool { + self == .zero + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - AdditiveArithmetic APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + @inlinable + static var zero: Self { + Self(Vector.zero) + } + + static func + (_ lhs: Self, _ rhs: Self) -> Self { + let result = lhs.addingReportingOverflow(rhs) + guard !result.overflow else { + preconditionFailure( + "arithmetic overflow: '\(lhs)' + '\(rhs)' as '\(Self.self)'" + ) + } + return result.partialValue + } + + static func - (_ lhs: Self, _ rhs: Self) -> Self { + let result = lhs.subtractingReportingOverflow(rhs) + guard !result.overflow else { + preconditionFailure( + "arithmetic overflow: '\(lhs)' - '\(rhs)' as '\(Self.self)'" + ) + } + return result.partialValue + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - BinaryInteger APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + init(truncatingIfNeeded source: some BinaryInteger) { + var vector = Vector(repeating: (source < .zero) ? ~0 : 0) + for (index, word) in zip(vector.indices, source.words) { + vector[index] = word + } + self.init(vector) + } + + @inlinable + var _lowWord: UInt { + vector[0] + } + + var trailingZeroBitCount: Int { + guard _isNonzero else { return Self.bitWidth } + var result = 0 + for word in words { + result += word.trailingZeroBitCount + guard word == 0 else { break } + } + return result + } + + @inlinable + var words: SIMDWrapper { + SIMDWrapper(wrappedValue: vector) + } + + @inlinable + func quotientAndRemainder( + dividingBy rhs: Self + ) -> (quotient: Self, remainder: Self) { + rhs.dividingFullWidth(_doubleWidth) + } + + @inlinable + func signum() -> Self { + _isNegative ? -1 : _isZero ? .zero : +1 + } + + static func / (_ lhs: Self, _ rhs: Self) -> Self { + let result = lhs.dividedReportingOverflow(by: rhs) + guard !result.overflow else { + preconditionFailure( + "arithmetic overflow: '\(lhs)' / '\(rhs)' as '\(Self.self)'" + ) + } + return result.partialValue + } + + @inlinable + static func /= (_ lhs: inout Self, _ rhs: Self) { + lhs = lhs / rhs + } + + static func % (_ lhs: Self, _ rhs: Self) -> Self { + let result = lhs.remainderReportingOverflow(dividingBy: rhs) + guard !result.overflow else { + preconditionFailure( + "arithmetic overflow: '\(lhs)' % '\(rhs)' as '\(Self.self)'" + ) + } + return result.partialValue + } + + @inlinable + static func %= (_ lhs: inout Self, _ rhs: Self) { + lhs = lhs % rhs + } + + @inlinable + static prefix func ~ (_ rhs: Self) -> Self { + Self(~rhs.vector) + } + + @inlinable + static func & (_ lhs: Self, _ rhs: Self) -> Self { + Self(lhs.vector & rhs.vector) + } + + @inlinable + static func &= (_ lhs: inout Self, _ rhs: Self) { + lhs = lhs & rhs + } + + @inlinable + static func | (_ lhs: Self, _ rhs: Self) -> Self { + Self(lhs.vector | rhs.vector) + } + + @inlinable + static func |= (_ lhs: inout Self, _ rhs: Self) { + lhs = lhs | rhs + } + + @inlinable + static func ^ (_ lhs: Self, _ rhs: Self) -> Self { + Self(lhs.vector ^ rhs.vector) + } + + @inlinable + static func ^= (_ lhs: inout Self, _ rhs: Self) { + lhs = lhs ^ rhs + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Codable APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + init(from decoder: any Decoder) throws { + let container = try decoder.singleValueContainer() + let source = try container.decode(String.self) + guard let target = Self(source, radix: 10) else { + throw DecodingError.dataCorruptedError( + in: container, + debugDescription: "'\(source)' as '\(Self.self)'" + ) + } + self = target + } + + func encode(to encoder: any Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(String(self, radix: 10)) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Comparable APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + if Self.isSigned { + guard rhs._isNonzero else { return lhs._isNegative } + guard lhs._isNonzero else { return rhs._isNonnegative } + guard lhs._isNegative == rhs._isNegative else { return lhs._isNegative } + } else { + guard rhs._isNonzero else { return false } + guard lhs._isNonzero else { return true } + } + return lhs.words.reversed().lexicographicallyPrecedes(rhs.words.reversed()) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - CustomDebugStringConvertible APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + var debugDescription: String { + var result = _isNegative ? "-0x" : "+0x" + result.reserveCapacity(result.count + (Self.bitWidth / 4)) + for word in magnitude.words.reversed() { + result += String(repeating: "0", count: word.leadingZeroBitCount / 4) + if word != 0 { + result += String(word, radix: 16, uppercase: true) + } + } + return result + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - CustomReflectable APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + var customMirror: Mirror { + Mirror(self, unlabeledChildren: EmptyCollection()) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - CustomStringConvertible APIs + +//===----------------------------------------------------------------------===// + +#if true // FIXME: Requires `quotientAndRemainder(dividingBy: 10)`. + + public extension SIMDWordsInteger { + var description: String { + debugDescription + } + } +#endif + +//===----------------------------------------------------------------------===// + +// MARK: - ExpressibleByIntegerLiteral APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + @available(macOS 13.3, *) + init(integerLiteral source: StaticBigInt) { + self = Self(exactly: source) ?? { + preconditionFailure("integer overflow: '\(source)' as '\(Self.self)'") + }() + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - FixedWidthInteger APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + // FIXME: + + init(_truncatingBits source: UInt) { + var vector = Vector.zero + vector[0] = source + self.init(vector) + } + + @inlinable + static var bitWidth: Int { + Vector.scalarCount * Vector.Scalar.bitWidth + } + + var byteSwapped: Self { + Self(Vector(words.reversed().lazy.map { $0.byteSwapped })) + } + + var leadingZeroBitCount: Int { + guard _isNonzero else { return Self.bitWidth } + var result = 0 + for word in words.reversed() { + result += word.leadingZeroBitCount + guard word == 0 else { break } + } + return result + } + + @inlinable + var nonzeroBitCount: Int { + _isNonzero ? Int(vector.nonzeroBitCount.wrappedSum()) : 0 + } + + func addingReportingOverflow( + _ rhs: Self + ) -> (partialValue: Self, overflow: Bool) { + guard rhs._isNonzero else { return (partialValue: self, overflow: false) } + guard _isNonzero else { return (partialValue: rhs, overflow: false) } + @SIMDWrapper var lhs = vector + @SIMDWrapper var rhs = rhs.vector + var flag = (false, false) + let indices: Range = lhs.indices.dropLast(Self.isSigned ? 1 : 0) + for index in indices { + let carry: UInt = (flag.0 || flag.1) ? 1 : 0 + (lhs[index], flag.0) = lhs[index].addingReportingOverflow(carry) + (lhs[index], flag.1) = lhs[index].addingReportingOverflow(rhs[index]) + } + if Self.isSigned { + let carry: Int = (flag.0 || flag.1) ? 1 : 0 + ($lhs._last, flag.0) = $lhs._last.addingReportingOverflow(carry) + ($lhs._last, flag.1) = $lhs._last.addingReportingOverflow($rhs._last) + } + return (partialValue: Self(lhs), overflow: flag.0 != flag.1) + } + + func subtractingReportingOverflow( + _ rhs: Self + ) -> (partialValue: Self, overflow: Bool) { + guard rhs._isNonzero else { return (partialValue: self, overflow: false) } + guard self != rhs else { return (partialValue: .zero, overflow: false) } + @SIMDWrapper var lhs = vector + @SIMDWrapper var rhs = rhs.vector + var flag = (false, false) + let indices: Range = lhs.indices.dropLast(Self.isSigned ? 1 : 0) + for index in indices { + let borrow: UInt = (flag.0 || flag.1) ? 1 : 0 + (lhs[index], flag.0) = lhs[index].subtractingReportingOverflow(borrow) + (lhs[index], flag.1) = lhs[index].subtractingReportingOverflow(rhs[index]) + } + if Self.isSigned { + let borrow: Int = (flag.0 || flag.1) ? 1 : 0 + ($lhs._last, flag.0) = $lhs._last.subtractingReportingOverflow(borrow) + ($lhs._last, flag.1) = $lhs._last.subtractingReportingOverflow($rhs._last) + } + return (partialValue: Self(lhs), overflow: flag.0 != flag.1) + } + + func multipliedReportingOverflow( + by rhs: Self + ) -> (partialValue: Self, overflow: Bool) { + let (high, low) = multipliedFullWidth(by: rhs) + let partialValue = Self(low.vector) + let overflow: Bool + if high._isZero { + overflow = partialValue._isNegative + } else if Self.isSigned, high == ~.zero { + overflow = partialValue._isNonnegative + } else { + overflow = true + } + return (partialValue: partialValue, overflow: overflow) + } + + func dividedReportingOverflow( + by rhs: Self + ) -> (partialValue: Self, overflow: Bool) { + if rhs._isZero { + return (partialValue: self, overflow: true) + } + if Self.isSigned, self == .min, rhs == (-1 as Self) { + return (partialValue: self, overflow: true) + } + let partialValue = rhs.dividingFullWidth(_doubleWidth).quotient + return (partialValue: partialValue, overflow: false) + } + + func remainderReportingOverflow( + dividingBy rhs: Self + ) -> (partialValue: Self, overflow: Bool) { + if rhs._isZero { + return (partialValue: self, overflow: true) + } + if Self.isSigned, self == .min, rhs == (-1 as Self) { + return (partialValue: .zero, overflow: true) + } + let partialValue = rhs.dividingFullWidth(_doubleWidth).remainder + return (partialValue: partialValue, overflow: false) + } + + func multipliedFullWidth( + by rhs: Self + ) -> (high: Self, low: Magnitude) { + guard _isNonzero, rhs._isNonzero else { return (high: .zero, low: .zero) } + guard self != (1 as Self) else { return rhs._doubleWidth } + guard rhs != (1 as Self) else { return _doubleWidth } + if _isPowerOfTwo || rhs._isPowerOfTwo { + let (lhs, rhs) = _isPowerOfTwo ? (rhs, self) : (self, rhs) + let bitShift = rhs.trailingZeroBitCount + var doubleWidth = (high: lhs, low: Magnitude(lhs.vector)) + doubleWidth.high &>>= (Self.bitWidth - bitShift) + doubleWidth.low &<<= bitShift + return doubleWidth + } + fatalError(#function) // FIXME: Not implemented yet. + } + + func dividingFullWidth( + _ lhs: (high: Self, low: Magnitude) + ) -> (quotient: Self, remainder: Self) { + fatalError(#function) // FIXME: Not implemented yet. + } + + static func &<< (_ lhs: Self, _ rhs: Self) -> Self { + let (quotient, remainder): (Int, Int) + do { + assert(Self.bitWidth.nonzeroBitCount == 1) + let rhs = Int(truncatingIfNeeded: rhs) & (Self.bitWidth &- 1) + guard rhs != 0 else { return lhs } + quotient = rhs >> UInt.bitWidth.trailingZeroBitCount + remainder = rhs & (UInt.bitWidth &- 1) + } + @SIMDWrapper var resultX = lhs.vector + @SIMDWrapper var resultY = (remainder == 0) ? Vector.zero : lhs.vector + resultX &<<= UInt(remainder) + resultY &>>= UInt(UInt.bitWidth - remainder) + $resultX._shiftWords(by: quotient) + $resultY._shiftWords(by: quotient + 1) + return Self(resultX ^ resultY) + } + + @inlinable + static func &<<= (_ lhs: inout Self, _ rhs: Self) { + lhs = lhs &<< rhs + } + + static func &>> (_ lhs: Self, _ rhs: Self) -> Self { + let (quotient, remainder): (Int, Int) + do { + assert(Self.bitWidth.nonzeroBitCount == 1) + let rhs = Int(truncatingIfNeeded: rhs) & (Self.bitWidth &- 1) + guard rhs != 0 else { return lhs } + quotient = rhs >> UInt.bitWidth.trailingZeroBitCount + remainder = rhs & (UInt.bitWidth &- 1) + } + @SIMDWrapper var resultX = lhs.vector + @SIMDWrapper var resultY = (remainder == 0) ? Vector.zero : lhs.vector + resultX &>>= UInt(remainder) + resultY &<<= UInt(UInt.bitWidth - remainder) + if lhs._isNegative { + $resultX._last = lhs.words._last &>> remainder + } + $resultX._shiftWords(by: -quotient, newValue: lhs._isNegative ? ~0 : 0) + $resultY._shiftWords(by: -quotient - 1) + return Self(resultX ^ resultY) + } + + @inlinable + static func &>>= (_ lhs: inout Self, _ rhs: Self) { + lhs = lhs &>> rhs + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Hashable APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger { + @inlinable + func hash(into hasher: inout Hasher) { + hasher.combine(vector) + } + + @inlinable + static func == (_ lhs: Self, _ rhs: Self) -> Bool { + lhs.vector == rhs.vector + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Numeric APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger where Self == Stride { + @inlinable + var magnitude: Magnitude { + Magnitude(bitPattern: _isNegative ? ~self &+ (1 as Self) : self) + } +} + +public extension SIMDWordsInteger { + static func * (_ lhs: Self, _ rhs: Self) -> Self { + let result = lhs.multipliedReportingOverflow(by: rhs) + guard !result.overflow else { + preconditionFailure( + "arithmetic overflow: '\(lhs)' * '\(rhs)' as '\(Self.self)'" + ) + } + return result.partialValue + } + + @inlinable + static func *= (_ lhs: inout Self, _ rhs: Self) { + lhs = lhs * rhs + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Strideable APIs + +//===----------------------------------------------------------------------===// + +public extension SIMDWordsInteger where Self == Stride { + @inlinable + func advanced(by n: Stride) -> Self { + self + n + } + + @inlinable + func distance(to other: Self) -> Stride { + other - self + } +} + +public extension SIMDWordsInteger where Self == Magnitude { + @inlinable + func advanced(by n: Stride) -> Self { + n._isNegative ? self - n.magnitude : self + n.magnitude + } + + @inlinable + func distance(to other: Self) -> Stride { + (self > other) ? -Stride(self - other) : Stride(other - self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - SIMDWrapper Type + +//===----------------------------------------------------------------------===// + +/// A mutable random-access collection, stored as a SIMD vector. + +@frozen +@propertyWrapper +public struct SIMDWrapper { + public var wrappedValue: Vector + + @inlinable + public init(wrappedValue: Vector) { + self.wrappedValue = wrappedValue + } + + @inlinable + public var projectedValue: Self { + get { + self + } + set { + self = newValue + } + } +} + +extension SIMDWrapper: Codable { + public init(from decoder: any Decoder) throws { + let container = try decoder.singleValueContainer() + let wrappedValue = try container.decode(Vector.self) + self.init(wrappedValue: wrappedValue) + } + + public func encode(to encoder: any Encoder) throws { + var container = encoder.singleValueContainer() + try container.encode(wrappedValue) + } +} + +extension SIMDWrapper: Hashable { + @inlinable + public func hash(into hasher: inout Hasher) { + hasher.combine(wrappedValue) + } + + @inlinable + public static func == (_ lhs: Self, _ rhs: Self) -> Bool { + lhs.wrappedValue == rhs.wrappedValue + } +} + +extension SIMDWrapper: MutableCollection, RandomAccessCollection { + public typealias Element = Vector.Scalar + + public typealias Index = Int + + public typealias Indices = Range + + @inlinable + public var indices: Indices { + wrappedValue.indices + } + + @inlinable + public var startIndex: Index { + indices.lowerBound + } + + @inlinable + public var endIndex: Index { + indices.upperBound + } + + @inlinable + public subscript(_ index: Index) -> Element { + get { + wrappedValue[index] + } + set { + wrappedValue[index] = newValue + } + } +} + +extension SIMDWrapper: Sendable where Vector: Sendable {} + +private extension SIMDWrapper where Element == UInt { + var _last: Int { + get { + Int(bitPattern: wrappedValue[index(before: endIndex)]) + } + set { + wrappedValue[index(before: endIndex)] = UInt(bitPattern: newValue) + } + } + + mutating func _shiftWords(by distance: Int, newValue: UInt = 0) { + let absolute = abs(distance) + switch distance.signum() { + case +1: + self[indices.dropFirst(absolute)] = dropLast(absolute) + for index in indices.prefix(absolute) { + self[index] = newValue + } + + case -1: + self[indices.dropLast(absolute)] = dropFirst(absolute) + for index in indices.suffix(absolute) { + self[index] = newValue + } + + default: + return + } + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - 128-bit Integer Types + +//===----------------------------------------------------------------------===// + +/// A 128-bit signed integer, stored as a SIMD vector of words. + +@frozen @available(macOS 13.3, *) +public struct Int128: SIMDWordsInteger, SignedInteger { + public typealias Magnitude = UInt128 + + public typealias Stride = Int128 + + #if arch(i386) || arch(arm) || arch(arm64_32) || arch(wasm32) || arch(powerpc) + + public typealias Vector = SIMD4 + #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) + + public typealias Vector = SIMD2 + #endif + + public var vector: Vector + + @inlinable + public init(_ vector: Vector) { + self.vector = vector + } +} + +/// A 128-bit unsigned integer, stored as a SIMD vector of words. + +@frozen @available(macOS 13.3, *) +public struct UInt128: SIMDWordsInteger, UnsignedInteger { + public typealias Magnitude = UInt128 + + @available(macOS 13.3, *) + public typealias Stride = Int128 + + #if arch(i386) || arch(arm) || arch(arm64_32) || arch(wasm32) || arch(powerpc) + + public typealias Vector = SIMD4 + #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) + + public typealias Vector = SIMD2 + #endif + + public var vector: Vector + + @inlinable + public init(_ vector: Vector) { + self.vector = vector + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - 256-bit Integer Types + +//===----------------------------------------------------------------------===// + +/// A 256-bit signed integer, stored as a SIMD vector of words. + +@frozen @available(macOS 13.3, *) +public struct Int256: SIMDWordsInteger, SignedInteger { + public typealias Magnitude = UInt256 + + public typealias Stride = Int256 + + #if arch(i386) || arch(arm) || arch(arm64_32) || arch(wasm32) || arch(powerpc) + + public typealias Vector = SIMD8 + #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) + + public typealias Vector = SIMD4 + #endif + + public var vector: Vector + + @inlinable + public init(_ vector: Vector) { + self.vector = vector + } +} + +/// A 256-bit unsigned integer, stored as a SIMD vector of words. + +@frozen @available(macOS 13.3, *) +public struct UInt256: SIMDWordsInteger, UnsignedInteger { + public typealias Magnitude = UInt256 + + public typealias Stride = Int256 + + #if arch(i386) || arch(arm) || arch(arm64_32) || arch(wasm32) || arch(powerpc) + + public typealias Vector = SIMD8 + #elseif arch(x86_64) || arch(arm64) || arch(powerpc64) || arch(powerpc64le) || arch(s390x) + + public typealias Vector = SIMD4 + #endif + + public var vector: Vector + + @inlinable + public init(_ vector: Vector) { + self.vector = vector + } +} + +@available(macOS 13.3, *) +extension UInt256.Vector: Sequence { + public func makeIterator() -> Iterator { + Iterator(self) + } + + public struct Iterator: IteratorProtocol { + private let vector: UInt256.Vector + private var index = 0 + + init(_ vector: UInt256.Vector) { + self.vector = vector + } + + public mutating func next() -> UInt256? { + guard index < vector.scalarCount else { + return nil + } + + let result = vector[index] + index += 1 + + return UInt256(result) + } + } +} + +@available(macOS 13.3, *) +extension UInt256: RawRepresentable { + public typealias RawValue = Data + + public init(rawValue data: Data) { + let value = data.withUnsafeBytes { + $0.load(as: UInt256.self) + } + self = value.bigEndian + } + + public var rawValue: Data { + let hexData = words.reduce(Data(capacity: Self.bitWidth / 8)) { accumulator, word in + withUnsafeBytes(of: word.bigEndian) { Data($0) } + accumulator + } + + return hexData + } +} diff --git a/Tests/zkpTests/UInt256Tests.swift b/Tests/zkpTests/UInt256Tests.swift new file mode 100644 index 00000000..ab35f464 --- /dev/null +++ b/Tests/zkpTests/UInt256Tests.swift @@ -0,0 +1,842 @@ +// +// UInt256Tests.swift +// GigaBitcoin/secp256k1.swift +// +// Modifications Copyright (c) 2024 GigaBitcoin LLC +// Distributed under the MIT software license +// +// See the accompanying file LICENSE for information +// +// +// NOTICE: THIS FILE HAS BEEN MODIFIED BY GigaBitcoin LLC +// UNDER COMPLIANCE WITH THE APACHE 2.0 LICENSE FROM THE +// ORIGINAL WORK OF THE COMPANY Apple Inc. +// +// THE FOLLOWING IS THE COPYRIGHT OF THE ORIGINAL DOCUMENT: +// +// +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if canImport(zkp) + @testable import zkp +#else + @testable import secp256k1 +#endif + +import XCTest + +//===----------------------------------------------------------------------===// + +// MARK: - SIMDWordsInteger Tests + +//===----------------------------------------------------------------------===// + +#if canImport(Foundation) + import class Foundation.JSONDecoder + import class Foundation.JSONEncoder +#endif + +#if canImport(StdlibUnittest) + import StdlibUnittest + typealias Base = Any +#else + import XCTest + typealias Base = XCTestCase +#endif + +@main +final class SIMDWordsIntegerTests: Base { + static func main() { + #if canImport(StdlibUnittest) + let testCase = SIMDWordsIntegerTests() + let testSuite = TestSuite("SIMDWordsIntegerTests") + testSuite.test("Addition", testCase.testAddition) + testSuite.test("BitCounting", testCase.testBitCounting) + testSuite.test("BitShifting", testCase.testBitShifting) + testSuite.test("BitTwiddling", testCase.testBitTwiddling) + testSuite.test("ByteSwapping", testCase.testByteSwapping) + testSuite.test("Multiplication", testCase.testMultiplication) + testSuite.test("Reflection", testCase.testReflection) + testSuite.test("Semantics", testCase.testSemantics) + testSuite.test("Subtraction", testCase.testSubtraction) + testSuite.test("TypeProperties", testCase.testTypeProperties) + testSuite.test("Words", testCase.testWords) + runAllTests() + #endif + } +} + +#if canImport(Foundation) && canImport(StdlibUnittest) + func checkCodable(_ instances: some Sequence) { + do { + let decoder = JSONDecoder() + let encoder = JSONEncoder() + let expected = Array(instances) + let actual = try decoder.decode( + type(of: expected), + from: encoder.encode(expected) + ) + expectEqual(expected, actual) + } catch { + expectUnreachableCatch(error) + } + } +#endif + +#if !canImport(StdlibUnittest) + func expectEqual( + _ expected: T, + _ actual: T, + _ message: @autoclosure () -> String = "", + file: StaticString = #file, + line: UInt = #line + ) { + XCTAssertEqual(expected, actual, message(), file: file, line: line) + } +#endif + +#if !canImport(StdlibUnittest) + func expectEqual( + _ expected: (T, U), + _ actual: (T, U), + _ message: @autoclosure () -> String = "", + file: StaticString = #file, + line: UInt = #line + ) { + XCTAssertEqual(expected.0, actual.0, message(), file: file, line: line) + XCTAssertEqual(expected.1, actual.1, message(), file: file, line: line) + } +#endif + +//===----------------------------------------------------------------------===// + +// MARK: - Addition Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testAddition(_: T.Type) { + expectEqual(T.zero, T.zero + T.zero) + expectEqual(T.min, T.zero + T.min) + expectEqual(T.min, T.min + T.zero) + expectEqual(T.max, T.zero + T.max) + expectEqual(T.max, T.max + T.zero) + expectEqual(~T.zero, T.min + T.max) + expectEqual(~T.zero, T.max + T.min) + expectEqual(T.min, T.max &+ T(1)) + expectEqual(T.min, T(1) &+ T.max) + if T.isSigned { + expectEqual(T.max, T.min &+ T(-1)) + expectEqual(T.max, T(-1) &+ T.min) + } + if T.bitWidth >= 64 { + if T.isSigned { + expectEqual(-2 as T, (-1 as T) + (-1 as T)) + expectEqual(+0 as T, (-1 as T) + (+1 as T)) + expectEqual(+0 as T, (+1 as T) + (-1 as T)) + expectEqual( + -0x8000_0000_0000_0000 as T, + -0x0123_4567_89AB_CDEF as T + + -0x7EDC_BA98_7654_3211 as T + ) + expectEqual( + -0x8000_0000_0000_0000 as T, + -0x7EDC_BA98_7654_3211 as T + + -0x0123_4567_89AB_CDEF as T + ) + expectEqual( + +0x7FFF_FFFF_FFFF_FFFF as T, + +0x0123_4567_89AB_CDEF as T + + +0x7EDC_BA98_7654_3210 as T + ) + expectEqual( + +0x7FFF_FFFF_FFFF_FFFF as T, + +0x7EDC_BA98_7654_3210 as T + + +0x0123_4567_89AB_CDEF as T + ) + } else { + expectEqual( + +0xFFFF_FFFF_FFFF_FFFF as T, + +0x0123_4567_89AB_CDEF as T + + +0xFEDC_BA98_7654_3210 as T + ) + expectEqual( + +0xFFFF_FFFF_FFFF_FFFF as T, + +0xFEDC_BA98_7654_3210 as T + + +0x0123_4567_89AB_CDEF as T + ) + } + } + if T.bitWidth >= 128 { + if T.isSigned { + expectEqual( + -0x8000_0000_0000_0000_0000_0000_0000_0000 as T, + -0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + + -0x7EDC_BA98_7654_3210_FEDC_BA98_7654_3211 as T + ) + expectEqual( + -0x8000_0000_0000_0000_0000_0000_0000_0000 as T, + -0x7EDC_BA98_7654_3210_FEDC_BA98_7654_3211 as T + + -0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + ) + expectEqual( + +0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T, + +0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + + +0x7EDC_BA98_7654_3210_FEDC_BA98_7654_3210 as T + ) + expectEqual( + +0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T, + +0x7EDC_BA98_7654_3210_FEDC_BA98_7654_3210 as T + + +0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + ) + } else { + expectEqual( + +0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T, + +0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + + +0xFEDC_BA98_7654_3210_FEDC_BA98_7654_3210 as T + ) + expectEqual( + +0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T, + +0xFEDC_BA98_7654_3210_FEDC_BA98_7654_3210 as T + + +0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + ) + } + } + } + + func testAddition() { + testAddition(Int64.self) + testAddition(UInt64.self) + testAddition(Int128.self) + testAddition(UInt128.self) + testAddition(Int256.self) + testAddition(UInt256.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - BitCounting Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testBitCounting(_: T.Type) { + typealias Element = ( + actual: T, + expected: ( + leadingZeroBitCount: Int, + nonzeroBitCount: Int, + trailingZeroBitCount: Int + ) + ) + lazy var negatives: [Element] = [ + (-0x8000_0000_0000_0000_0000_0000_0000_0000, (0, 1, 127)), + (-0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, (0, 2, 0)), + (-0x0000_0000_FEDC_BA98_7654_3210_0000_0000, (0, 61, 36)), + (-0x0000_0000_0123_4567_89AB_CDEF_0000_0000, (0, 65, 32)), + (-0x0000_0000_0000_0000_8000_0000_0000_0000, (0, 65, 63)), + (-0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFF, (0, 66, 0)), + (-0x0000_0000_0000_0000_0000_0000_0000_0002, (0, 127, 1)), + (-0x0000_0000_0000_0000_0000_0000_0000_0001, (0, 128, 0)) + ] + lazy var positives: [Element] = [ + (+0x0000_0000_0000_0000_0000_0000_0000_0000, (128, 0, 128)), + (+0x0000_0000_0000_0000_0000_0000_0000_0001, (127, 1, 0)), + (+0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFE, (65, 62, 1)), + (+0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFF, (65, 63, 0)), + (+0x0000_0000_0123_4567_89AB_CDEF_0000_0000, (39, 32, 32)), + (+0x0000_0000_FEDC_BA98_7654_3210_0000_0000, (32, 32, 36)), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE, (1, 126, 1)), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, (1, 127, 0)) + ] + lazy var extras: [Element] = [ + (+0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE, (0, 127, 1)), + (+0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, (0, 128, 0)) + ] + precondition(T.bitWidth == 128) + let elements = T.isSigned ? negatives + positives : positives + extras + for (actual, expected) in elements { + expectEqual(expected.leadingZeroBitCount, actual.leadingZeroBitCount) + expectEqual(expected.nonzeroBitCount, actual.nonzeroBitCount) + expectEqual(expected.trailingZeroBitCount, actual.trailingZeroBitCount) + } + } + + func testBitCounting() { + testBitCounting(Int128.self) + testBitCounting(UInt128.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - BitShifting Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testBitShifting(_: T.Type) { + expectEqual(T.min, T.min &<< T.zero) + expectEqual(T.min, T.min &>> T.zero) + expectEqual(T.max, T.max &<< T.zero) + expectEqual(T.max, T.max &>> T.zero) + expectEqual(T.min, T.min &<< T.bitWidth) + expectEqual(T.min, T.min &>> T.bitWidth) + expectEqual(T.max, T.max &<< T.bitWidth) + expectEqual(T.max, T.max &>> T.bitWidth) + if T.bitWidth >= 64 { + do { + let expected: T = (T.bitWidth == 64) + ? +0x000_0000_789A_BCDE_F000_0000 + : +0x012_3456_789A_BCDE_F000_0000 + var actual: T = +0x000_0000_0123_4567_89AB_CDEF + actual <<= 28 + expectEqual(expected, actual) + } + do { + let expected: T = +0x0000_0000_1234_5678 + var actual: T = +0x0123_4567_89AB_CDEF + actual >>= 28 + expectEqual(expected, actual) + } + if T.isSigned { + let expected: T = -0x0000_0000_1234_5679 + var actual: T = -0x0123_4567_89AB_CDEF + actual >>= 28 + expectEqual(expected, actual) + } + } + if T.bitWidth >= 128 { + do { + let expected: T = (T.bitWidth == 128) + ? +0x0000_0000_0000_0000_7766_5544_3322_1100_0000_0000_0000_0000 + : +0x7FEE_DDCC_BBAA_9988_7766_5544_3322_1100_0000_0000_0000_0000 + var actual: T = +0x0000_0000_0000_0000_7FEE_DDCC_BBAA_9988_7766_5544_3322_1100 + actual <<= 64 + expectEqual(expected, actual) + } + do { + let expected: T = (T.bitWidth == 128) + ? +0x0_0000_0000_0000_0000_7665_5443_3221_1000_0000_0000_0000_0000 + : +0x7_FEED_DCCB_BAA9_9887_7665_5443_3221_1000_0000_0000_0000_0000 + var actual: T = +0x0_0000_0000_0000_0000_7FEE_DDCC_BBAA_9988_7766_5544_3322_1100 + actual <<= 68 + expectEqual(expected, actual) + } + do { + let expected: T = +0x0000_0000_0000_0000_7FEE_DDCC_BBAA_9988 + var actual: T = +0x7FEE_DDCC_BBAA_9988_7766_5544_3322_1100 + actual >>= 64 + expectEqual(expected, actual) + } + do { + let expected: T = +0x0000_0000_0000_0000_07FE_EDDC_CBBA_A998 + var actual: T = +0x7FEE_DDCC_BBAA_9988_7766_5544_3322_1100 + actual >>= 68 + expectEqual(expected, actual) + } + if T.isSigned { + let expected: T = -0x0000_0000_0000_0000_07FE_EDDC_CBBA_A999 + var actual: T = -0x7FEE_DDCC_BBAA_9988_7766_5544_3322_1100 + actual >>= 68 + expectEqual(expected, actual) + } + } + } + + func testBitShifting() { + testBitShifting(Int64.self) + testBitShifting(UInt64.self) + testBitShifting(Int128.self) + testBitShifting(UInt128.self) + testBitShifting(Int256.self) + testBitShifting(UInt256.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - BitTwiddling Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testBitTwiddling(_: T.Type) { + expectEqual(T.max, ~.min) + expectEqual(T.min, ~.max) + expectEqual(T.min, .min & .min) + expectEqual(T.max, .max & .max) + expectEqual(T.zero, .min & .max) + expectEqual(T.zero, .max & .min) + expectEqual(T.min, .min | .min) + expectEqual(T.max, .max | .max) + expectEqual(~T.zero, .min | .max) + expectEqual(~T.zero, .max | .min) + expectEqual(T.zero, .min ^ .min) + expectEqual(T.zero, .max ^ .max) + expectEqual(~T.zero, .min ^ .max) + expectEqual(~T.zero, .max ^ .min) + } + + func testBitTwiddling() { + testBitTwiddling(Int64.self) + testBitTwiddling(UInt64.self) + testBitTwiddling(Int128.self) + testBitTwiddling(UInt128.self) + testBitTwiddling(Int256.self) + testBitTwiddling(UInt256.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - ByteSwapping Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testByteSwapping(_: T.Type) { + typealias Element = (lhs: T, rhs: T) + lazy var negatives: [Element] = [ + (-0x8000_0000_0000_0000_0000_0000_0000_0000, +0x0000_0000_0000_0000_0000_0000_0000_0080), + (-0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, +0x0100_0000_0000_0000_0000_0000_0000_0080), + (-0x0000_0000_FEDC_BA98_7654_3210_0000_0000, +0x0000_0000_F0CD_AB89_6745_2301_FFFF_FFFF), + (-0x0000_0000_0123_4567_89AB_CDEF_0000_0000, +0x0000_0000_1132_5476_98BA_DCFE_FFFF_FFFF), + (-0x0000_0000_0000_0000_8000_0000_0000_0000, +0x0000_0000_0000_0080_FFFF_FFFF_FFFF_FFFF), + (-0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFF, +0x0100_0000_0000_0080_FFFF_FFFF_FFFF_FFFF), + (-0x0000_0000_0000_0000_0000_0000_0000_0002, -0x0100_0000_0000_0000_0000_0000_0000_0001), + (-0x0000_0000_0000_0000_0000_0000_0000_0001, -0x0000_0000_0000_0000_0000_0000_0000_0001), + (+0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFE, -0x0100_0000_0000_0081_0000_0000_0000_0000), + (+0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFF, -0x0000_0000_0000_0081_0000_0000_0000_0000), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE, -0x0100_0000_0000_0000_0000_0000_0000_0081), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, -0x0000_0000_0000_0000_0000_0000_0000_0081) + ] + lazy var positives: [Element] = [ + (+0x0000_0000_0000_0000_0000_0000_0000_0000, +0x0000_0000_0000_0000_0000_0000_0000_0000), + (+0x0000_0000_0000_0000_0000_0000_0000_0001, +0x0100_0000_0000_0000_0000_0000_0000_0000), + (+0x0000_0000_0123_4567_89AB_CDEF_0000_0000, +0x0000_0000_EFCD_AB89_6745_2301_0000_0000), + (+0x0000_0000_FEDC_BA98_7654_3210_0000_0000, +0x0000_0000_1032_5476_98BA_DCFE_0000_0000) + ] + lazy var extras: [Element] = [ + (+0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFE, +0xFEFF_FFFF_FFFF_FF7F_0000_0000_0000_0000), + (+0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFF, +0xFFFF_FFFF_FFFF_FF7F_0000_0000_0000_0000), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE, +0xFEFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FF7F), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, +0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FF7F), + (+0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE, +0xFEFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF), + (+0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, +0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF) + ] + precondition(T.bitWidth == 128) + let elements = T.isSigned ? negatives + positives : positives + extras + for (lhs, rhs) in elements { + expectEqual(lhs, rhs.byteSwapped) + expectEqual(rhs, lhs.byteSwapped) + expectEqual(lhs, T(bigEndian: lhs.bigEndian)) + expectEqual(rhs, T(bigEndian: rhs.bigEndian)) + expectEqual(lhs, T(littleEndian: lhs.littleEndian)) + expectEqual(rhs, T(littleEndian: rhs.littleEndian)) + } + } + + func testByteSwapping() { + testByteSwapping(Int128.self) + testByteSwapping(UInt128.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Multiplication Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testMultiplication(_: T.Type) { + let identity: T = 1 + do { + expectEqual(T.zero, T.zero * T.zero) + expectEqual(T.zero, T.zero * T.min) + expectEqual(T.zero, T.min * T.zero) + expectEqual(T.zero, T.zero * T.max) + expectEqual(T.zero, T.max * T.zero) + expectEqual(T.zero, T.zero * identity) + expectEqual(T.zero, identity * T.zero) + } + do { + expectEqual(identity, identity * identity) + expectEqual(T.min, T.min * identity) + expectEqual(T.min, identity * T.min) + expectEqual(T.max, T.max * identity) + expectEqual(T.max, identity * T.max) + } + if T.bitWidth >= 64 { + if T.isSigned { + expectEqual( + -0x48D1_59E2_6AF3_7BC0 as T, + -0x0123_4567_89AB_CDEF as T * + +0x0000_0000_0000_0040 as T + ) + expectEqual( + -0x48D1_59E2_6AF3_7BC0 as T, + +0x0000_0000_0000_0040 as T * + -0x0123_4567_89AB_CDEF as T + ) + expectEqual( + +0x48D1_59E2_6AF3_7BC0 as T, + +0x0123_4567_89AB_CDEF as T * + +0x0000_0000_0000_0040 as T + ) + expectEqual( + +0x48D1_59E2_6AF3_7BC0 as T, + +0x0000_0000_0000_0040 as T * + +0x0123_4567_89AB_CDEF as T + ) + } else { + expectEqual( + +0x91A2_B3C4_D5E6_F780 as T, + +0x0123_4567_89AB_CDEF as T * + +0x0000_0000_0000_0080 as T + ) + expectEqual( + +0x91A2_B3C4_D5E6_F780 as T, + +0x0000_0000_0000_0080 as T * + +0x0123_4567_89AB_CDEF as T + ) + } + } + if T.bitWidth == 128 { + if T.isSigned { + expectEqual( + ( + high: -0x2000_0000_0000_0000_0000_0000_0000_0000 as T, + low: +0x0000_0000_0000_0000_0000_0000_0000_0000 as T.Magnitude + ), + T.min.multipliedFullWidth(by: +0x4000_0000_0000_0000_0000_0000_0000_0000) + ) + expectEqual( + ( + high: +0x1FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T, + low: +0xC000_0000_0000_0000_0000_0000_0000_0000 as T.Magnitude + ), + T.max.multipliedFullWidth(by: +0x4000_0000_0000_0000_0000_0000_0000_0000) + ) + } else { + expectEqual( + ( + high: +0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T, + low: +0x8000_0000_0000_0000_0000_0000_0000_0000 as T.Magnitude + ), + T.max.multipliedFullWidth(by: +0x8000_0000_0000_0000_0000_0000_0000_0000) + ) + } + } + } + + func testMultiplication() { + testMultiplication(Int64.self) + testMultiplication(UInt64.self) + testMultiplication(Int128.self) + testMultiplication(UInt128.self) + testMultiplication(Int256.self) + testMultiplication(UInt256.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Reflection Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testReflection(_: T.Type) { + typealias Element = (actual: T, expected: String) + lazy var negatives: [Element] = [ + (-0x8000_0000_0000_0000_0000_0000_0000_0000, "-0x80000000000000000000000000000000"), + (-0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, "-0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + (-0x0000_0000_0123_4567_89AB_CDEF_0000_0000, "-0x000000000123456789ABCDEF00000000"), + (-0x0000_0000_0000_0000_0000_0000_0000_0002, "-0x00000000000000000000000000000002"), + (-0x0000_0000_0000_0000_0000_0000_0000_0001, "-0x00000000000000000000000000000001") + ] + lazy var positives: [Element] = [ + (+0x0000_0000_0000_0000_0000_0000_0000_0000, "+0x00000000000000000000000000000000"), + (+0x0000_0000_0000_0000_0000_0000_0000_0001, "+0x00000000000000000000000000000001"), + (+0x0000_0000_0123_4567_89AB_CDEF_0000_0000, "+0x000000000123456789ABCDEF00000000"), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE, "+0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, "+0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + ] + lazy var extras: [Element] = [ + (+0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE, "+0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"), + (+0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, "+0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") + ] + precondition(T.bitWidth == 128) + let elements = T.isSigned ? negatives + positives : positives + extras + for (actual, expected) in elements { + let expectedOutput = "- \(expected)\n" + var actualOutput = "" + dump(actual, to: &actualOutput) + expectEqual(expectedOutput, actualOutput) + expectEqual(expected, String(reflecting: actual)) + } + } + + func testReflection() { + testReflection(Int128.self) + testReflection(UInt128.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Semantics Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testSemantics(_: T.Type) { + typealias Element = (value: T, distance: T.Stride) + lazy var negatives: [Element] = [ + (-0x8000_0000_0000_0000_0000_0000_0000_0000, 0x0000_0000_0000_0000_0000_0000_0000_0001), + (-0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, 0x7FFF_FFFF_0123_4567_89AB_CDEF_FFFF_FFFF), + (-0x0000_0000_FEDC_BA98_7654_3210_0000_0000, 0x0000_0000_FDB9_7530_ECA8_6421_0000_0000), + (-0x0000_0000_0123_4567_89AB_CDEF_0000_0000, 0x0000_0000_0123_4567_09AB_CDEF_0000_0000), + (-0x0000_0000_0000_0000_8000_0000_0000_0000, 0x0000_0000_0000_0000_0000_0000_0000_0001), + (-0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFF, 0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFD), + (-0x0000_0000_0000_0000_0000_0000_0000_0002, 0x0000_0000_0000_0000_0000_0000_0000_0001), + (-0x0000_0000_0000_0000_0000_0000_0000_0001, 0x0000_0000_0000_0000_0000_0000_0000_0001) + ] + lazy var positives: [Element] = [ + (+0x0000_0000_0000_0000_0000_0000_0000_0000, 0x0000_0000_0000_0000_0000_0000_0000_0001), + (+0x0000_0000_0000_0000_0000_0000_0000_0001, 0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFD), + (+0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFE, 0x0000_0000_0000_0000_0000_0000_0000_0001), + (+0x0000_0000_0000_0000_7FFF_FFFF_FFFF_FFFF, 0x0000_0000_0123_4567_09AB_CDEF_0000_0001), + (+0x0000_0000_0123_4567_89AB_CDEF_0000_0000, 0x0000_0000_FDB9_7530_ECA8_6421_0000_0000), + (+0x0000_0000_FEDC_BA98_7654_3210_0000_0000, 0x7FFF_FFFF_0123_4567_89AB_CDEF_FFFF_FFFE), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFE, 0x0000_0000_0000_0000_0000_0000_0000_0001), + (+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, 0x0000_0000_0000_0000_0000_0000_0000_0000) + ] + let elements = T.isSigned ? negatives + positives : positives + do { + let sortedValues: [T] = elements.map { $0.value } + expectEqual(sortedValues, sortedValues.shuffled().sorted()) + #if canImport(Foundation) && canImport(StdlibUnittest) + // FIXME: checkCodable(sortedValues) + #endif + #if canImport(StdlibUnittest) + checkComparable(sortedValues, oracle: { $0 <=> $1 }) + checkHashable(sortedValues, equalityOracle: { $0 == $1 }) + // FIXME: checkLosslessStringConvertible(sortedValues) + #endif + } + for index in zip( + elements.indices.dropLast(), + elements.indices.dropFirst() + ) { + let value: (T, T) + let distance: (T.Stride, T.Stride) + (value.0, distance.0) = elements[index.0] + (value.1, distance.1) = elements[index.1] + expectEqual(value.0, value.0.advanced(by: .zero)) + expectEqual(value.1, value.1.advanced(by: .zero)) + expectEqual(value.1, value.0.advanced(by: +distance.0)) + expectEqual(value.0, value.1.advanced(by: -distance.0)) + expectEqual(+distance.0, value.0.distance(to: value.1)) + expectEqual(-distance.0, value.1.distance(to: value.0)) + } + if T.isSigned { + for (value, _) in negatives { + expectEqual(-1, value.signum()) + } + } + do { + for (value, _) in positives where value != .zero { + expectEqual(+1, value.signum()) + } + expectEqual(T.zero, T.zero.signum()) + } + } + + func testSemantics() { + testSemantics(Int128.self) + testSemantics(UInt128.self) + testSemantics(Int256.self) + testSemantics(UInt256.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Subtraction Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testSubtraction(_: T.Type) { + expectEqual(T.zero, T.zero - T.zero) + expectEqual(T.min, T.min - T.zero) + expectEqual(T.max, T.max - T.zero) + expectEqual(T.zero, T.min - T.min) + expectEqual(T.zero, T.max - T.max) + expectEqual(T.max, T.min &- T(1)) + if T.isSigned { + expectEqual(T.min, T.max &- T(-1)) + } + if T.bitWidth >= 64 { + if T.isSigned { + expectEqual(+0 as T, (-1 as T) - (-1 as T)) + expectEqual(-2 as T, (-1 as T) - (+1 as T)) + expectEqual(+2 as T, (+1 as T) - (-1 as T)) + expectEqual( + -0x8000_0000_0000_0000 as T, + -0x0123_4567_89AB_CDEF as T - + +0x7EDC_BA98_7654_3211 as T + ) + expectEqual( + -0x8000_0000_0000_0000 as T, + -0x7EDC_BA98_7654_3211 as T - + +0x0123_4567_89AB_CDEF as T + ) + expectEqual( + +0x7FFF_FFFF_FFFF_FFFF as T, + +0x0123_4567_89AB_CDEF as T - + -0x7EDC_BA98_7654_3210 as T + ) + expectEqual( + +0x7FFF_FFFF_FFFF_FFFF as T, + +0x7EDC_BA98_7654_3210 as T - + -0x0123_4567_89AB_CDEF as T + ) + } else { + expectEqual( + +0x0123_4567_89AB_CDEF as T, + +0xFFFF_FFFF_FFFF_FFFF as T - + +0xFEDC_BA98_7654_3210 as T + ) + expectEqual( + +0xFEDC_BA98_7654_3210 as T, + +0xFFFF_FFFF_FFFF_FFFF as T - + +0x0123_4567_89AB_CDEF as T + ) + } + } + if T.bitWidth >= 128 { + if T.isSigned { + expectEqual( + -0x8000_0000_0000_0000_0000_0000_0000_0000 as T, + -0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T - + +0x7EDC_BA98_7654_3210_FEDC_BA98_7654_3211 as T + ) + expectEqual( + -0x8000_0000_0000_0000_0000_0000_0000_0000 as T, + -0x7EDC_BA98_7654_3210_FEDC_BA98_7654_3211 as T - + +0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + ) + expectEqual( + +0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T, + +0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T - + -0x7EDC_BA98_7654_3210_FEDC_BA98_7654_3210 as T + ) + expectEqual( + +0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T, + +0x7EDC_BA98_7654_3210_FEDC_BA98_7654_3210 as T - + -0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + ) + } else { + expectEqual( + +0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T, + +0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T - + +0xFEDC_BA98_7654_3210_FEDC_BA98_7654_3210 as T + ) + expectEqual( + +0xFEDC_BA98_7654_3210_FEDC_BA98_7654_3210 as T, + +0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF as T - + +0x0123_4567_89AB_CDEF_0123_4567_89AB_CDEF as T + ) + } + } + } + + func testSubtraction() { + testSubtraction(Int64.self) + testSubtraction(UInt64.self) + testSubtraction(Int128.self) + testSubtraction(UInt128.self) + testSubtraction(Int256.self) + testSubtraction(UInt256.self) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - TypeProperties Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testTypeProperties() { + expectEqual(128, Int128.bitWidth) + expectEqual(128, UInt128.bitWidth) + expectEqual(256, Int256.bitWidth) + expectEqual(256, UInt256.bitWidth) + expectEqual(true, Int128.isSigned) + expectEqual(false, UInt128.isSigned) + expectEqual(true, Int256.isSigned) + expectEqual(false, UInt256.isSigned) + } +} + +//===----------------------------------------------------------------------===// + +// MARK: - Words Tests + +//===----------------------------------------------------------------------===// + +@available(macOS 13.3, *) +extension SIMDWordsIntegerTests { + func testWords(_: T.Type) { + if T.isSigned { + expectEqual(true, T.zero.words.allSatisfy { $0 == .min }) + expectEqual(true, (-1 as T).words.allSatisfy { $0 == .max }) + expectEqual(true, T.min.words.dropLast().allSatisfy { $0 == .min }) + expectEqual(true, T.max.words.dropLast().allSatisfy { $0 == .max }) + expectEqual(UInt(bitPattern: .min), T.min.words.last) + expectEqual(UInt(bitPattern: .max), T.max.words.last) + } else { + expectEqual(true, T.min.words.allSatisfy { $0 == .min }) + expectEqual(true, T.max.words.allSatisfy { $0 == .max }) + } + } + + func testWords() { + testWords(Int64.self) + testWords(UInt64.self) + testWords(Int128.self) + testWords(UInt128.self) + testWords(Int256.self) + testWords(UInt256.self) + expectEqual(-0x8000_0000_0000_0000_0000_0000_0000_0000, Int128.min) + expectEqual(+0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, Int128.max) + expectEqual(+0x0000_0000_0000_0000_0000_0000_0000_0000, UInt128.min) + expectEqual(+0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, UInt128.max) + } +} diff --git a/Tests/zkpTests/secp256k1Tests.swift b/Tests/zkpTests/secp256k1Tests.swift index 69d4d2de..bbf7b71b 100644 --- a/Tests/zkpTests/secp256k1Tests.swift +++ b/Tests/zkpTests/secp256k1Tests.swift @@ -718,6 +718,35 @@ final class secp256k1Tests: XCTestCase { XCTAssertTrue(publicKey.isValidSignature(signature, for: SHA256.hash(data: messageData))) } + @available(macOS 13.3, *) + func testUInt256() { + let expectedPrivateKey: UInt256 = 0x7DA1_2CC3_9BB4_189A_C72D_34FC_2225_DF5C_F36A_AACD_CAC7_E5A4_3963_299B_C8D8_88ED + let expectedPrivateKey2: UInt256 = 0x1BB5_FC86_3773_7549_414D_7F1B_82A5_C12D_234B_56DB_AC17_5E14_0F63_046A_EBA8_DF87 + let expectedPublicKey = "023521df7b94248ffdf0d37f738a4792cc3932b6b1b89ef71cddde8251383b26e7" + let combinedPrivateKey = expectedPrivateKey + expectedPrivateKey2 + + let privateKey = try! secp256k1.Signing.PrivateKey(expectedPrivateKey) + let privateKey2 = try! secp256k1.Signing.PrivateKey(expectedPrivateKey2) + let privateKey3 = try! secp256k1.Signing.PrivateKey(combinedPrivateKey) + + print("privateKey \(String(bytes: privateKey.rawRepresentation))") + print("privateKey2 \(String(bytes: privateKey2.rawRepresentation))") + print("combinedPrivateKey UInt256 \(combinedPrivateKey)") + print("combinedPrivateKey P256K1 \(String(bytes: privateKey3.rawRepresentation))") + + print("publicKey \(String(bytes: privateKey.publicKey.rawRepresentation))") + print("publicKey2 \(String(bytes: privateKey2.publicKey.rawRepresentation))") + + let combinedPublicKey = try! secp256k1.Signing.PublicKey.combine(privateKey.publicKey, privateKey2.publicKey) + + print("combinedPublicKey UInt256 \(String(bytes: privateKey3.publicKey.rawRepresentation))") + print("combinedPublicKey P256K1 \(String(bytes: combinedPublicKey.rawRepresentation))") + + // Verify the keys matches the expected keys output + XCTAssertEqual(expectedPrivateKey, UInt256(rawValue: privateKey.rawRepresentation)) + XCTAssertEqual(expectedPublicKey, String(bytes: privateKey.publicKey.rawRepresentation)) + } + static var allTests = [ ("testUncompressedKeypairCreation", testUncompressedKeypairCreation), ("testCompressedKeypairCreation", testCompressedKeypairCreation),