Skip to content
Permalink
Browse files

♻️ Refactor Address

- Braek down address related files into smaller files
- Introduce Address struct instead of protocol
- Add BitcoinScheme
- Add Address.VersionByte
- Add Address.HashSize
- Add Address.HashType (AddressType is renamed)
- Deprecate Cashaddr, LegacyAddress
- Add documentation for Address, Cashaddr, LegacyAddress
- Rename Network.mainnet to Network.mainnetBCH
- Rename Network.testnetBCH to Network.testnetBCH
- Modify Address.cashaddr and .legacy to be computed properties
  • Loading branch information...
usatie committed Sep 19, 2019
1 parent ed4e43f commit f7adda22e6a18adbed8dba410d3eccf843c0e7e2
Showing with 979 additions and 501 deletions.
  1. +57 −13 BitcoinKit.xcodeproj/project.pbxproj
  2. +19 −176 Sources/BitcoinKit/Core/Address/Address.swift
  3. +32 −0 Sources/BitcoinKit/Core/Address/AddressError.swift
  4. +82 −0 Sources/BitcoinKit/Core/Address/BitcoinAddress+Cashaddr.swift
  5. +5 −25 Sources/BitcoinKit/Core/Address/{AddressType.swift → BitcoinAddress+HashType.swift}
  6. +84 −0 Sources/BitcoinKit/Core/Address/BitcoinAddress+Legacy.swift
  7. +94 −0 Sources/BitcoinKit/Core/Address/BitcoinAddress+Size.swift
  8. +72 −0 Sources/BitcoinKit/Core/Address/BitcoinAddress+VersionByte.swift
  9. +35 −0 Sources/BitcoinKit/Core/Address/BitcoinAddress+deprecated.swift
  10. +96 −0 Sources/BitcoinKit/Core/Address/BitcoinAddress.swift
  11. +36 −0 Sources/BitcoinKit/Core/Address/BitcoinScheme.swift
  12. +2 −0 Sources/BitcoinKit/Core/Address/Encoding/Base58Check.swift
  13. +0 −88 Sources/BitcoinKit/Core/Address/VersionByte.swift
  14. +1 −1 Sources/BitcoinKit/Core/BlockChain.swift
  15. +23 −6 Sources/BitcoinKit/Core/BlockStore.swift
  16. +1 −1 Sources/BitcoinKit/Core/Keys/HDPublicKey.swift
  17. +6 −6 Sources/BitcoinKit/Core/Keys/PrivateKey.swift
  18. +46 −0 Sources/BitcoinKit/Core/Keys/PublicKey+Address.swift
  19. +0 −31 Sources/BitcoinKit/Core/Keys/PublicKey.swift
  20. +7 −6 Sources/BitcoinKit/Core/Network.swift
  21. +17 −11 Sources/BitcoinKit/Core/PaymentURI.swift
  22. +5 −6 Sources/BitcoinKit/{Core/Address → Deprecated}/AddressFactory.swift
  23. +28 −0 Sources/BitcoinKit/Deprecated/AddressType.swift
  24. +39 −0 Sources/BitcoinKit/Deprecated/Cashaddr.swift
  25. +39 −0 Sources/BitcoinKit/Deprecated/LegacyAddress.swift
  26. +2 −2 Sources/BitcoinKit/Mock/MockHelper.swift
  27. +2 −2 Sources/BitcoinKit/Networking/Peer.swift
  28. +15 −17 Sources/BitcoinKit/Scripts/Script.swift
  29. +7 −7 Sources/BitcoinKit/Wallet/HDWallet.swift
  30. +1 −1 Sources/BitcoinKit/Wallet/TransactionBuilder.swift
  31. +6 −6 Tests/BitcoinKitTests/Core/Address/AddressFactoryTests.swift
  32. +16 −16 Tests/BitcoinKitTests/Core/Address/AddressTests.swift
  33. +11 −9 Tests/BitcoinKitTests/Core/Address/LegacyAddressTests.swift
  34. +2 −2 Tests/BitcoinKitTests/Core/BloomFilterTests.swift
  35. +1 −1 Tests/BitcoinKitTests/Core/Keys/HDKeyChainTests.swift
  36. +6 −6 Tests/BitcoinKitTests/Core/Keys/HDPrivateKeyTests.swift
  37. +3 −3 Tests/BitcoinKitTests/Core/Keys/PrivateKeyTests.swift
  38. +2 −2 Tests/BitcoinKitTests/Core/MnemonicTests.swift
  39. +57 −35 Tests/BitcoinKitTests/Core/PaymentURITests.swift
  40. +2 −2 Tests/BitcoinKitTests/Script/ScriptTests.swift
  41. +4 −4 Tests/BitcoinKitTests/TransactionTests.swift
  42. +2 −2 Tests/BitcoinKitTests/Wallet/BCHSignatureHashHelperTests.swift
  43. +2 −2 Tests/BitcoinKitTests/Wallet/BTCSignatureHashHelperTests.swift
  44. +4 −4 Tests/BitcoinKitTests/Wallet/HDWalletTests.swift
  45. +4 −4 Tests/BitcoinKitTests/Wallet/TransactionBuilderTests.swift
  46. +2 −2 Tests/BitcoinKitTests/Wallet/TransactionSignerTests.swift
  47. +2 −2 WalletExample/WalletExample/ViewController.swift

Large diffs are not rendered by default.

@@ -1,207 +1,50 @@
//
// Address.swift
//
// Copyright © 2018 Kishikawa Katsumi
// Copyright © 2018 BitcoinKit developers
//
//
// 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
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation

public protocol AddressProtocol {
public protocol Address: CustomStringConvertible {
var network: Network { get }
var type: AddressType { get }
var hashType: BitcoinAddress.HashType { get }
var data: Data { get }
var publicKey: Data? { get }

var base58: String { get }
var legacy: String { get }
var cashaddr: String { get }
}

#if os(iOS) || os(tvOS) || os(watchOS)
public typealias Address = AddressProtocol & QRCodeConvertible
#else
public typealias Address = AddressProtocol
#endif

public enum AddressError: Error {
case invalid
case invalidScheme
case invalidVersionByte
}

public struct LegacyAddress: Address {
public let network: Network
public let type: AddressType
public let data: Data
public let base58: String
public let cashaddr: String
public let publicKey: Data?

public init(data: Data, type: AddressType, network: Network, base58: String, bech32: String, publicKey: Data?) {
self.data = data
self.type = type
self.network = network
self.base58 = base58
self.cashaddr = bech32
self.publicKey = publicKey
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
}

public init(_ base58: String) throws {
guard let pubKeyHash = Base58Check.decode(base58) else {
throw AddressError.invalid
}

let network: Network
let type: AddressType
let addressPrefix = pubKeyHash[0]
switch addressPrefix {
case Network.mainnet.pubkeyhash:
network = .mainnet
type = .pubkeyHash
case Network.testnet.pubkeyhash:
network = .testnet
type = .pubkeyHash
case Network.mainnet.scripthash:
network = .mainnet
type = .scriptHash
case Network.testnet.scripthash:
network = .testnet
type = .scriptHash
default:
throw AddressError.invalidVersionByte
}

self.network = network
self.type = type
self.publicKey = nil
self.data = pubKeyHash.dropFirst()
self.base58 = base58

// cashaddr
switch type {
case .pubkeyHash, .scriptHash:
let payload = Data([type.versionByte160]) + self.data
self.cashaddr = Bech32.encode(payload, prefix: network.scheme)
default:
self.cashaddr = ""
}
@available(*, deprecated, renamed: "legacy")
public var base58: String {
return legacy
}
public init(data: Data, type: AddressType, network: Network) {
let addressData: Data = [type.versionByte] + data
self.data = data
self.type = type
self.network = network
self.publicKey = nil
self.base58 = Base58Check.encode(addressData)
self.cashaddr = Bech32.encode(addressData, prefix: network.scheme)
}
}

extension LegacyAddress: Equatable {
public static func == (lhs: LegacyAddress, rhs: LegacyAddress) -> Bool {
return lhs.network == rhs.network && lhs.data == rhs.data && lhs.type == rhs.type
}
}

extension LegacyAddress: CustomStringConvertible {
public var description: String {
return base58
}
}

public struct Cashaddr: Address {
public let network: Network
public let type: AddressType
public let data: Data
public let base58: String
public let cashaddr: CashaddrWithScheme
public let publicKey: Data?

public typealias CashaddrWithScheme = String

public init(data: Data, type: AddressType, network: Network, base58: String, bech32: CashaddrWithScheme, publicKey: Data?) {
self.data = data
self.type = type
self.network = network
self.base58 = base58
self.cashaddr = bech32
self.publicKey = publicKey
}

public init(_ cashaddr: CashaddrWithScheme) throws {
guard let decoded = Bech32.decode(cashaddr) else {
throw AddressError.invalid
}
let (prefix, raw) = (decoded.prefix, decoded.data)
self.cashaddr = cashaddr
self.publicKey = nil

switch prefix {
case Network.mainnet.scheme:
network = .mainnet
case Network.testnet.scheme:
network = .testnet
default:
throw AddressError.invalidScheme
}

let versionByte = raw[0]
let hash = raw.dropFirst()

guard hash.count == VersionByte.getSize(from: versionByte) else {
throw AddressError.invalidVersionByte
}
self.data = hash
guard let typeBits = VersionByte.TypeBits(rawValue: (versionByte & 0b01111000)) else {
throw AddressError.invalidVersionByte
}

switch typeBits {
case .pubkeyHash:
type = .pubkeyHash
base58 = Base58Check.encode([network.pubkeyhash] + data)
case .scriptHash:
type = .scriptHash
base58 = Base58Check.encode([network.scripthash] + data)
}
}
public init(data: Data, type: AddressType, network: Network) {
let addressData: Data = [type.versionByte] + data
self.data = data
self.type = type
self.network = network
self.publicKey = nil
self.base58 = Base58Check.encode(addressData)
self.cashaddr = Bech32.encode(addressData, prefix: network.scheme)
}
}

extension Cashaddr: Equatable {
public static func == (lhs: Cashaddr, rhs: Cashaddr) -> Bool {
return lhs.network == rhs.network && lhs.data == rhs.data && lhs.type == rhs.type
}
}

extension Cashaddr: CustomStringConvertible {
public var description: String {
return cashaddr
@available(*, deprecated, renamed: "hashType")
public var type: BitcoinAddress.HashType {
return hashType
}
}
@@ -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
}
@@ -0,0 +1,82 @@
//
// 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:
fatalError("cashaddr is only supported in BitcoinCash network.")
}
return Bech32.encode([versionByte.rawValue] + data, prefix: scheme.rawValue)
}

/// Creates a new Cashaddr with the bech32 encoded address with scheme The network
/// will be .mainnetBTC or .testnetBTC. This initializer perform 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 {
guard let decoded = Bech32.decode(cashaddr) else {
throw AddressError.invalid
}
let payload = decoded.data

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

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

self.data = payload.dropFirst()
guard data.count == hashSize.sizeInBytes else {
throw AddressError.invalidVersionByte
}
}
}
@@ -1,5 +1,5 @@
//
// AddressType.swift
// BitcoinAddress+HashType.swift
//
// Copyright © 2018 BitcoinKit developers
//
@@ -24,29 +24,9 @@
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 {
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 }
}

0 comments on commit f7adda2

Please sign in to comment.
You can’t perform that action at this time.