Skip to content

Commit

Permalink
📝 Add detailed documentation for HashSize, HashType and VersionByte
Browse files Browse the repository at this point in the history
  • Loading branch information
usatie committed Sep 21, 2019
1 parent f7adda2 commit e4ae246
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 24 deletions.
8 changes: 4 additions & 4 deletions BitcoinKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@
29D8F12C2333B6BD00E72008 /* LegacyAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12B2333B6BC00E72008 /* LegacyAddress.swift */; };
29D8F12E2333B6C600E72008 /* Cashaddr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12D2333B6C600E72008 /* Cashaddr.swift */; };
29D8F1302333BED500E72008 /* BitcoinAddress+VersionByte.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F12F2333BED500E72008 /* BitcoinAddress+VersionByte.swift */; };
29D8F1322333BF3000E72008 /* BitcoinAddress+Size.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1312333BF3000E72008 /* BitcoinAddress+Size.swift */; };
29D8F1322333BF3000E72008 /* BitcoinAddress+HashSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1312333BF3000E72008 /* BitcoinAddress+HashSize.swift */; };
29D8F1342333E10B00E72008 /* BitcoinScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1332333E10B00E72008 /* BitcoinScheme.swift */; };
29D8F1382334A18200E72008 /* BitcoinAddress+Legacy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1372334A18200E72008 /* BitcoinAddress+Legacy.swift */; };
29D8F13A2334A1A300E72008 /* BitcoinAddress+Cashaddr.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29D8F1392334A1A300E72008 /* BitcoinAddress+Cashaddr.swift */; };
Expand Down Expand Up @@ -474,7 +474,7 @@
29D8F12B2333B6BC00E72008 /* LegacyAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyAddress.swift; sourceTree = "<group>"; };
29D8F12D2333B6C600E72008 /* Cashaddr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cashaddr.swift; sourceTree = "<group>"; };
29D8F12F2333BED500E72008 /* BitcoinAddress+VersionByte.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+VersionByte.swift"; sourceTree = "<group>"; };
29D8F1312333BF3000E72008 /* BitcoinAddress+Size.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+Size.swift"; sourceTree = "<group>"; };
29D8F1312333BF3000E72008 /* BitcoinAddress+HashSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+HashSize.swift"; sourceTree = "<group>"; };
29D8F1332333E10B00E72008 /* BitcoinScheme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinScheme.swift; sourceTree = "<group>"; };
29D8F1372334A18200E72008 /* BitcoinAddress+Legacy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+Legacy.swift"; sourceTree = "<group>"; };
29D8F1392334A1A300E72008 /* BitcoinAddress+Cashaddr.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BitcoinAddress+Cashaddr.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1058,7 +1058,7 @@
14CDC3892021881A00C01556 /* BitcoinAddress.swift */,
29D8F13D2334B4EE00E72008 /* BitcoinAddress+deprecated.swift */,
297DB97220EB12E60077EEEE /* BitcoinAddress+HashType.swift */,
29D8F1312333BF3000E72008 /* BitcoinAddress+Size.swift */,
29D8F1312333BF3000E72008 /* BitcoinAddress+HashSize.swift */,
29D8F12F2333BED500E72008 /* BitcoinAddress+VersionByte.swift */,
29D8F1372334A18200E72008 /* BitcoinAddress+Legacy.swift */,
29D8F1392334A1A300E72008 /* BitcoinAddress+Cashaddr.swift */,
Expand Down Expand Up @@ -1497,7 +1497,7 @@
0C09002C2116A1C30077E9BC /* OP_NEGATE.swift in Sources */,
1463E6B42025E9480033DAAE /* BlockStore.swift in Sources */,
14839A8D202FE66A00A6CB34 /* PingMessage.swift in Sources */,
29D8F1322333BF3000E72008 /* BitcoinAddress+Size.swift in Sources */,
29D8F1322333BF3000E72008 /* BitcoinAddress+HashSize.swift in Sources */,
0C0900362116A62E0077E9BC /* OP_0NOTEQUAL.swift in Sources */,
0C1DE15D211E6EB400FE8E43 /* OP_DROP.swift in Sources */,
299CB47320F0185500B1245C /* TransactionSignatureSerializer.swift in Sources */,
Expand Down
12 changes: 8 additions & 4 deletions Sources/BitcoinKit/Core/Address/BitcoinAddress+Cashaddr.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ extension BitcoinAddress {
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.
/// 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")
Expand All @@ -54,10 +55,10 @@ extension BitcoinAddress {
/// - 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
}
let payload = decoded.data

switch BitcoinScheme(scheme: decoded.prefix) {
case .some(.bitcoincash):
Expand All @@ -68,13 +69,16 @@ extension BitcoinAddress {
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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,30 @@
import Foundation

extension BitcoinAddress {
/// Bitcoin address size. Usually it's 160 bis(20 bytes).
/// But more size variation were introduced in cashaddr.
/// 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
Expand Down
6 changes: 6 additions & 0 deletions Sources/BitcoinKit/Core/Address/BitcoinAddress+HashType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@
import Foundation

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
Expand Down
16 changes: 9 additions & 7 deletions Sources/BitcoinKit/Core/Address/BitcoinAddress+Legacy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,24 @@ extension BitcoinAddress {
}
}

/// Creates a new LegacyAddress instance with Base58Check encoded address. The network
/// will be .mainnetBTC or .testnetBTC. This initializer perform base58check and hash
/// size validation.
/// Creates a new BitcoinAddress instance with Base58Check encoded address.
///
/// The network will be .mainnetBTC or .testnetBTC. This initializer performs
/// base58check and hash size validation.
/// ```
/// let address = try BitcoinAddress(legacy: "1AC4gh14wwZPULVPCdxUkgqbtPvC92PQPN")
/// ```
///
/// - Parameter legacy: Base58Check encoded String value to use as the source of the new
/// instance. It must be without scheme.
public init(legacy: String) throws {
// Hash size is 160 bits
self.hashSize = .bits160

// Base58Check decode
guard let pubKeyHash = Base58Check.decode(legacy) else {
throw AddressError.invalid
}
self.hashSize = .bits160

let networkVersionByte = pubKeyHash[0]

Expand All @@ -74,9 +78,7 @@ extension BitcoinAddress {

self.data = pubKeyHash.dropFirst()

// cashaddr

// data is 20 byte
// validate data size
guard data.count == hashSize.sizeInBytes else {
throw AddressError.invalid
}
Expand Down
25 changes: 18 additions & 7 deletions Sources/BitcoinKit/Core/Address/BitcoinAddress+VersionByte.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,40 @@
import Foundation

public extension BitcoinAddress {
/// The version byte’s most signficant bit is reserved and must be 0. The 4 next bits indicate the type of address and the 3 least significant bits indicate the size of the hash.
/// An object that represents the version byte of a cashaddr.
///
/// The most signficant bit is reserved and must be 0. The 4 next bits indicate the type of address and the 3 least significant bits indicate the size of the hash.
/// https://www.bitcoincash.org/spec/cashaddr.html
struct VersionByte {
/// Raw version byte value
/// Version byte raw value
public let rawValue: UInt8
/// Hash type (P2PKH or P2SH)
public let hashType: HashType
/// Hash Size
public let hashSize: HashSize

/// Creates a new VersionByte instance from type and size.
///
/// - Parameters:
/// - hashType: The type of the hash
/// - hashSize: The size of the hash
public init(_ hashType: HashType, _ hashSize: HashSize) {
self.rawValue = hashType.rawValue + hashSize.rawValue
self.hashType = hashType
self.hashSize = hashSize
}

public init?(_ value: UInt8) {
/// Creates a new VersionByte instance from a raw UInt8 byte value.
///
/// - Parameters:
/// - rawValue: The actual version byte
public init?(_ rawValue: UInt8) {
// X------- (The first bit) is zero
// -XXXX--- (Next four bits) are type bits
// -----XXX (The least three bits) are size bits
let firstBit: UInt8 = value & 0b10000000
let typeBits: UInt8 = value & 0b01111000
let sizeBits: UInt8 = value & 0b00000111
let firstBit: UInt8 = rawValue & 0b10000000
let typeBits: UInt8 = rawValue & 0b01111000
let sizeBits: UInt8 = rawValue & 0b00000111
guard firstBit == 0 else {
return nil
}
Expand All @@ -57,7 +68,7 @@ public extension BitcoinAddress {
guard let hashSize = HashSize(rawValue: sizeBits) else {
return nil
}
self.rawValue = value
self.rawValue = rawValue
self.hashType = hashType
self.hashSize = hashSize
}
Expand Down

0 comments on commit e4ae246

Please sign in to comment.