-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathLDKNode+AddressType.swift
More file actions
138 lines (119 loc) · 4.87 KB
/
Copy pathLDKNode+AddressType.swift
File metadata and controls
138 lines (119 loc) · 4.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import LDKNode
extension LDKNode.AddressType {
// MARK: - All cases (ordered)
static let allAddressTypes: [LDKNode.AddressType] = [.legacy, .nestedSegwit, .nativeSegwit, .taproot]
/// All address types with `selected` first, remaining in standard order.
static func prioritized(selected: LDKNode.AddressType) -> [LDKNode.AddressType] {
var types = [selected]
for type in allAddressTypes where type != selected {
types.append(type)
}
return types
}
// MARK: - Storage string (UserDefaults / BitkitCore APIs)
/// String value used in UserDefaults and BitkitCore APIs.
var stringValue: String {
switch self {
case .legacy: return "legacy"
case .nestedSegwit: return "nestedSegwit"
case .nativeSegwit: return "nativeSegwit"
case .taproot: return "taproot"
}
}
/// Parses storage string; returns nil for invalid or unknown values.
static func from(string: String) -> LDKNode.AddressType? {
switch string {
case "legacy": return .legacy
case "nestedSegwit": return .nestedSegwit
case "nativeSegwit": return .nativeSegwit
case "taproot": return .taproot
default: return nil
}
}
/// Parses storage string; returns `.nativeSegwit` for nil or invalid (backward compatibility).
static func fromStorage(_ string: String?) -> LDKNode.AddressType {
guard let s = string, let type = from(string: s) else { return .nativeSegwit }
return type
}
/// Parses a comma-separated string of address types; filters invalid values.
static func parseCommaSeparated(_ string: String) -> [LDKNode.AddressType] {
string.split(separator: ",")
.map { String($0).trimmingCharacters(in: .whitespaces) }
.compactMap { from(string: $0) }
}
// MARK: - Derivation path
/// BIP derivation path using current network (Env.network) for coin type.
var derivationPath: String {
let coinType = Env.network == .bitcoin ? "0" : "1"
return derivationPath(coinType: coinType)
}
/// BIP derivation path for the given coin type ("0" mainnet, "1" testnet).
func derivationPath(coinType: String) -> String {
switch self {
case .legacy: return "m/44'/\(coinType)'/0'/0" // BIP 44
case .nestedSegwit: return "m/49'/\(coinType)'/0'/0" // BIP 49
case .nativeSegwit: return "m/84'/\(coinType)'/0'/0" // BIP 84
case .taproot: return "m/86'/\(coinType)'/0'/0" // BIP 86
}
}
// MARK: - Localized display
var localizedTitle: String {
switch self {
case .legacy: return "Legacy"
case .nestedSegwit: return "Nested Segwit"
case .nativeSegwit: return "Native Segwit"
case .taproot: return "Taproot"
}
}
var localizedDescription: String {
switch self {
case .legacy: return "Pay-to-public-key-hash (1x...)"
case .nestedSegwit: return "Pay-to-Script-Hash (3x...)"
case .nativeSegwit: return "Pay-to-witness-public-key-hash (bc1x...)"
case .taproot: return "Pay-to-Taproot (bc1px...)"
}
}
var example: String {
switch self {
case .legacy: return "(1x...)"
case .nestedSegwit: return "(3x...)"
case .nativeSegwit: return "(bc1x...)"
case .taproot: return "(bc1px...)"
}
}
var shortExample: String {
switch self {
case .legacy: return "1x..."
case .nestedSegwit: return "3x..."
case .nativeSegwit: return "bc1q..."
case .taproot: return "bc1p..."
}
}
/// Accessibility / UI test identifier.
var testId: String {
switch self {
case .legacy: return "p2pkh"
case .nestedSegwit: return "p2sh-p2wpkh"
case .nativeSegwit: return "p2wpkh"
case .taproot: return "p2tr"
}
}
// MARK: - Address format validation
/// Returns true if the address has the expected prefix for this address type on the given network.
/// Defensive check only; not a full script/checksum validation.
func matchesAddressFormat(_ address: String, network: LDKNode.Network) -> Bool {
let trimmed = address.trimmingCharacters(in: .whitespaces)
guard !trimmed.isEmpty else { return false }
let isMainnet = network == .bitcoin
switch self {
case .legacy:
return isMainnet ? trimmed.hasPrefix("1") : trimmed.hasPrefix("m") || trimmed.hasPrefix("n")
case .nestedSegwit:
return isMainnet ? trimmed.hasPrefix("3") : trimmed.hasPrefix("2")
case .nativeSegwit:
return isMainnet ? trimmed.hasPrefix("bc1q") : trimmed.hasPrefix("tb1q") || trimmed.hasPrefix("bcrt1q")
case .taproot:
return isMainnet ? trimmed.hasPrefix("bc1p") : trimmed.hasPrefix("tb1p") || trimmed.hasPrefix("bcrt1p")
}
}
}