Skip to content

Commit

Permalink
Merge pull request #232 from yenom/refactor-address
Browse files Browse the repository at this point in the history
♻️ Refactor address
  • Loading branch information
usatie authored Sep 21, 2019
2 parents 6d0d88c + 10aff45 commit fd1d400
Show file tree
Hide file tree
Showing 57 changed files with 1,433 additions and 740 deletions.
112 changes: 96 additions & 16 deletions BitcoinKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

50 changes: 50 additions & 0 deletions Sources/BitcoinKit/Core/Address/Address.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Address.swift
//
// Copyright © 2019 BitcoinKit developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

import Foundation

public protocol Address: CustomStringConvertible {
var network: Network { get }
var hashType: BitcoinAddress.HashType { get }
var data: Data { get }
var legacy: String { get }
var cashaddr: String { get }
}

extension Address {
@available(*, deprecated, message: "Always returns nil. If you need public key with address, please use PublicKey instead.")
public var publicKey: Data? {
return nil
}

@available(*, deprecated, renamed: "legacy")
public var base58: String {
return legacy
}

@available(*, deprecated, renamed: "hashType")
public var type: BitcoinAddress.HashType {
return hashType
}
}
32 changes: 32 additions & 0 deletions Sources/BitcoinKit/Core/Address/AddressError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// AddressError.swift
//
// Copyright © 2019 BitcoinKit developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

import Foundation

public enum AddressError: Error {
case invalid
case invalidScheme
case invalidVersionByte
case invalidDataSize
}
87 changes: 87 additions & 0 deletions Sources/BitcoinKit/Core/Address/BitcoinAddress+Cashaddr.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// BitcoinAddress+Cashaddr.swift
//
// Copyright © 2019 BitcoinKit developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

import Foundation

extension BitcoinAddress {
public var versionByte: VersionByte {
return VersionByte(hashType, hashSize)
}

/// Bech32 encoded bitcoincash address format
public var cashaddr: String {
let scheme: BitcoinScheme
switch network {
case .mainnetBCH: scheme = .bitcoincash
case .testnetBCH: scheme = .bchtest
case .mainnetBTC: scheme = .bitcoincash
case .testnetBTC: scheme = .bchtest
default:
assertionFailure("cashaddr is only supported for \(network).")
scheme = .bitcoincash
}
return Bech32.encode(payload: [versionByte.rawValue] + data, prefix: scheme.rawValue)
}

/// Creates a new BitcoinAddress with the bech32 encoded address with scheme.
///
/// The network will be .mainnetBTC or .testnetBTC. This initializer performs
/// prefix validation, bech32 decode, and hash size validation.
///
/// ```
/// let address = try BitcoinAddress(cashaddr: "bitcoincash:qpjdpjrm5zvp2al5u4uzmp36t9m0ll7gd525rss978")
/// ```
///
/// - Parameter bech32: Bech32 encoded String value to use as the source of the new
/// instance. It must come with scheme "bitcioncash:" or "bchtest:".
public init(cashaddr: String) throws {
// prefix validation and decode
guard let decoded = Bech32.decode(cashaddr) else {
throw AddressError.invalid
}

switch BitcoinScheme(scheme: decoded.prefix) {
case .some(.bitcoincash):
network = .mainnetBCH
case .some(.bchtest):
network = .testnetBCH
default:
throw AddressError.invalidScheme
}

let payload = decoded.data
guard let versionByte = VersionByte(payload[0]) else {
throw AddressError.invalidVersionByte
}
self.hashType = versionByte.hashType
self.hashSize = versionByte.hashSize

self.data = payload.dropFirst()

// validate data size
guard data.count == hashSize.sizeInBytes else {
throw AddressError.invalidVersionByte
}
}
}
106 changes: 106 additions & 0 deletions Sources/BitcoinKit/Core/Address/BitcoinAddress+HashSize.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// BitcoinAddress+HashSize.swift
//
// Copyright © 2019 BitcoinKit developers
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

import Foundation

extension BitcoinAddress {
/// An object that represents the hash size of a cashaddr.
///
/// The 3 least significant bits of VersionByte in cashaddr are the size bits.
/// In most cases, the size of the hash is 160 bis, however different sizes
/// are also possible.
/// https://www.bitcoincash.org/spec/cashaddr.html
public struct HashSize {
public let rawValue: UInt8
/// Creates a new HashSize instance with 3 bits value.
///
/// Size bits are the least 3 bits of the version byte. So the rawValue
/// should be 0-7.
/// - Parameter rawValue: UInt8 value of the 3 bits.
public init?(rawValue: UInt8) {
guard [0, 1, 2, 3, 4, 5, 6, 7].contains(rawValue) else {
return nil
}
self.rawValue = rawValue
}

/// Creates a new HashSize instance with the actual size of the hash.
///
/// The hash size in bits can be 160, 192, 224, 256, 320, 384, 448 or 512.
/// - Parameter sizeInBits: UInt8 value of the size of the hash in bits.
public init?(sizeInBits: Int) {
switch sizeInBits {
case 160: rawValue = 0
case 192: rawValue = 1
case 224: rawValue = 2
case 256: rawValue = 3
case 320: rawValue = 4
case 384: rawValue = 5
case 448: rawValue = 6
case 512: rawValue = 7
default: return nil
}
}
}
}

extension BitcoinAddress.HashSize {
/// Hash size in bits
public var sizeInBits: Int {
switch rawValue {
case 0: return 160
case 1: return 192
case 2: return 224
case 3: return 256
case 4: return 320
case 5: return 384
case 6: return 448
case 7: return 512
default: fatalError("Unsupported size bits")
}
}

/// Hash size in bytes
public var sizeInBytes: Int {
return sizeInBits / 8
}
}

extension BitcoinAddress.HashSize: Equatable {
// swiftlint:disable operator_whitespace
public static func ==(lhs: BitcoinAddress.HashSize, rhs: BitcoinAddress.HashSize) -> Bool {
return lhs.rawValue == rhs.rawValue
}
}

extension BitcoinAddress.HashSize {
public static let bits160: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 160)!
public static let bits192: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 192)!
public static let bits224: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 224)!
public static let bits256: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 256)!
public static let bits320: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 320)!
public static let bits384: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 384)!
public static let bits448: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 448)!
public static let bits512: BitcoinAddress.HashSize = BitcoinAddress.HashSize(sizeInBits: 512)!
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// AddressType.swift
// BitcoinAddress+HashType.swift
//
// Copyright © 2018 BitcoinKit developers
//
Expand All @@ -24,29 +24,15 @@

import Foundation

public class AddressType {
static let pubkeyHash: AddressType = PubkeyHash()
static let scriptHash: AddressType = ScriptHash()

var versionByte: UInt8 { return 0 }
var versionByte160: UInt8 { return versionByte + 0 }
var versionByte192: UInt8 { return versionByte + 1 }
var versionByte224: UInt8 { return versionByte + 2 }
var versionByte256: UInt8 { return versionByte + 3 }
var versionByte320: UInt8 { return versionByte + 4 }
var versionByte384: UInt8 { return versionByte + 5 }
var versionByte448: UInt8 { return versionByte + 6 }
var versionByte512: UInt8 { return versionByte + 7 }
}

extension AddressType: Equatable {
public static func == (lhs: AddressType, rhs: AddressType) -> Bool {
return lhs.versionByte == rhs.versionByte
extension BitcoinAddress {
/// An object that represents the hash type of a cashaddr.
///
/// The 4 bits, from the second to the fifth [-XXXX---], are the type bits.
/// So the rawValue should be 0, 8, 16, ..., 120. However, only 0 and 8 are
/// supported for now. Further types will be added as new features are added.
/// https://www.bitcoincash.org/spec/cashaddr.html
public enum HashType: UInt8 {
case pubkeyHash = 0
case scriptHash = 8
}
}
public class PubkeyHash: AddressType {
public override var versionByte: UInt8 { return 0 }
}
public class ScriptHash: AddressType {
public override var versionByte: UInt8 { return 8 }
}
Loading

0 comments on commit fd1d400

Please sign in to comment.