Skip to content

Commit 45439ec

Browse files
Merge branch 'fix/bip32-tests' of https://github.com/1inch/web3swift into pr-278-review
2 parents a3b2acf + f5334ac commit 45439ec

File tree

6 files changed

+101
-52
lines changed

6 files changed

+101
-52
lines changed

73a65a1766e605eaeb5d699fcaa8da02

Lines changed: 0 additions & 1 deletion
This file was deleted.

Sources/web3swift/KeystoreManager/BIP32Keystore.swift

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,27 @@ public class BIP32Keystore: AbstractKeystore {
1717
public var isHDKeystore: Bool = true
1818

1919
public var keystoreParams: KeystoreParamsBIP32?
20+
21+
@available(*, deprecated, message: "Please use addressStorage instead")
2022
public var paths: [String: EthereumAddress] = [String: EthereumAddress]()
2123

2224
public var rootPrefix: String
2325

2426
public var addresses: [EthereumAddress]? {
2527
get {
26-
if self.paths.count == 0 {
28+
let addresses = self.addressStorage.addresses
29+
if addresses.count == 0 {
2730
return nil
2831
}
29-
var allAccounts = [EthereumAddress]()
30-
for (_, address) in paths {
31-
allAccounts.append(address)
32-
}
33-
return allAccounts
32+
return addresses
3433
}
3534
}
3635

3736
public func UNSAFE_getPrivateKeyData(password: String, account: EthereumAddress) throws -> Data {
38-
if let key = self.paths.keyForValue(value: account) {
39-
guard let decryptedRootNode = try? self.getPrefixNodeData(password) else {
40-
throw AbstractKeystoreError.encryptionError("Failed to decrypt a keystore")
41-
}
42-
guard let rootNode = HDNode(decryptedRootNode) else {
43-
throw AbstractKeystoreError.encryptionError("Failed to deserialize a root node")
44-
}
45-
guard rootNode.depth == (self.rootPrefix.components(separatedBy: "/").count - 1) else {
46-
throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")
47-
}
37+
if let key = addressStorage.path(by: account) {
38+
guard let decryptedRootNode = try? self.getPrefixNodeData(password) else {throw AbstractKeystoreError.encryptionError("Failed to decrypt a keystore")}
39+
guard let rootNode = HDNode(decryptedRootNode) else {throw AbstractKeystoreError.encryptionError("Failed to deserialize a root node")}
40+
guard rootNode.depth == (self.rootPrefix.components(separatedBy: "/").count - 1) else {throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")}
4841
// guard rootNode.depth == HDNode.defaultPathPrefix.components(separatedBy: "/").count - 1 else {throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")}
4942
guard let index = UInt32(key.components(separatedBy: "/").last!) else {
5043
throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")
@@ -61,8 +54,11 @@ public class BIP32Keystore: AbstractKeystore {
6154
}
6255

6356
// --------------
64-
65-
57+
58+
private static let KeystoreParamsBIP32Version = 4
59+
60+
private (set) var addressStorage: PathAddressStorage
61+
6662
public convenience init?(_ jsonString: String) {
6763
let lowercaseJSON = jsonString.lowercased()
6864
guard let jsonData = lowercaseJSON.data(using: .utf8) else {
@@ -72,21 +68,16 @@ public class BIP32Keystore: AbstractKeystore {
7268
}
7369

7470
public init?(_ jsonData: Data) {
75-
guard var keystorePars = try? JSONDecoder().decode(KeystoreParamsBIP32.self, from: jsonData) else {
76-
return nil
77-
}
78-
if (keystorePars.version != 3) {
79-
return nil
80-
}
81-
if (keystorePars.crypto.version != nil && keystorePars.crypto.version != "1") {
82-
return nil
83-
}
84-
if (!keystorePars.isHDWallet) {
85-
return nil
86-
}
71+
guard var keystorePars = try? JSONDecoder().decode(KeystoreParamsBIP32.self, from: jsonData) else {return nil}
72+
if (keystorePars.version != Self.KeystoreParamsBIP32Version) {return nil}
73+
if (keystorePars.crypto.version != nil && keystorePars.crypto.version != "1") {return nil}
74+
if (!keystorePars.isHDWallet) {return nil}
75+
8776
for (p, ad) in keystorePars.pathToAddress {
8877
paths[p] = EthereumAddress(ad)
8978
}
79+
addressStorage = PathAddressStorage(pathAddressPairs: keystorePars.pathAddressPairs)
80+
9081
if keystorePars.rootPath == nil {
9182
keystorePars.rootPath = HDNode.defaultPathPrefix
9283
}
@@ -103,11 +94,10 @@ public class BIP32Keystore: AbstractKeystore {
10394
}
10495
try self.init(seed: seed, password: password, prefixPath: prefixPath, aesMode: aesMode)
10596
}
106-
107-
public init?(seed: Data, password: String = "web3swift", prefixPath: String = HDNode.defaultPathMetamaskPrefix, aesMode: String = "aes-128-cbc") throws {
108-
guard let rootNode = HDNode(seed: seed)?.derive(path: prefixPath, derivePrivateKey: true) else {
109-
return nil
110-
}
97+
98+
public init? (seed: Data, password: String = "web3swift", prefixPath: String = HDNode.defaultPathMetamaskPrefix, aesMode: String = "aes-128-cbc") throws {
99+
addressStorage = PathAddressStorage()
100+
guard let rootNode = HDNode(seed: seed)?.derive(path: prefixPath, derivePrivateKey: true) else {return nil}
111101
self.rootPrefix = prefixPath
112102
try createNewAccount(parentNode: rootNode, password: password)
113103
guard let serializedRootNode = rootNode.serialize(serializePublic: false) else {
@@ -136,10 +126,8 @@ public class BIP32Keystore: AbstractKeystore {
136126

137127
func createNewAccount(parentNode: HDNode, password: String = "web3swift") throws {
138128
var newIndex = UInt32(0)
139-
for (p, _) in paths {
140-
guard let idx = UInt32(p.components(separatedBy: "/").last!) else {
141-
continue
142-
}
129+
for p in addressStorage.paths {
130+
guard let idx = UInt32(p.components(separatedBy: "/").last!) else {continue}
143131
if idx >= newIndex {
144132
newIndex = idx + 1
145133
}
@@ -157,7 +145,7 @@ public class BIP32Keystore: AbstractKeystore {
157145
} else {
158146
newPath = prefixPath + "/" + String(newNode.index)
159147
}
160-
paths[newPath] = newAddress
148+
addressStorage.add(address: newAddress, for: newPath)
161149
}
162150

163151
public func createNewCustomChildAccount(password: String = "web3swift", path: String) throws {guard let decryptedRootNode = try? self.getPrefixNodeData(password) else {
@@ -208,9 +196,8 @@ public class BIP32Keystore: AbstractKeystore {
208196
newPath = prefixPath + "/" + pathAppendix!
209197
}
210198
paths[newPath] = newAddress
211-
guard let serializedRootNode = rootNode.serialize(serializePublic: false) else {
212-
throw AbstractKeystoreError.keyDerivationError
213-
}
199+
addressStorage.add(address: newAddress, for: newPath)
200+
guard let serializedRootNode = rootNode.serialize(serializePublic: false) else {throw AbstractKeystoreError.keyDerivationError}
214201
try encryptDataToStorage(password, data: serializedRootNode, aesMode: self.keystoreParams!.crypto.cipher)
215202
}
216203

@@ -261,8 +248,8 @@ public class BIP32Keystore: AbstractKeystore {
261248
for (path, address) in paths {
262249
pathToAddress[path] = address.address
263250
}
264-
var keystorePars = KeystoreParamsBIP32(crypto: crypto, id: UUID().uuidString.lowercased(), version: 3)
265-
keystorePars.pathToAddress = pathToAddress
251+
var keystorePars = KeystoreParamsBIP32(crypto: crypto, id: UUID().uuidString.lowercased(), version: Self.KeystoreParamsBIP32Version)
252+
keystorePars.pathAddressPairs = addressStorage.toPathAddressPairs()
266253
keystorePars.rootPath = self.rootPrefix
267254
keystoreParams = keystorePars
268255
}

Sources/web3swift/KeystoreManager/KeystoreParams.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ public protocol AbstractKeystoreParams: Codable {
3838

3939
}
4040

41+
public struct PathAddressPair: Codable {
42+
let path: String
43+
let address: String
44+
}
4145

4246
public struct KeystoreParamsBIP32: AbstractKeystoreParams {
4347
public var crypto: CryptoParamsV3
@@ -46,13 +50,15 @@ public struct KeystoreParamsBIP32: AbstractKeystoreParams {
4650
public var isHDWallet: Bool
4751

4852
var pathToAddress: [String: String]
53+
var pathAddressPairs: [PathAddressPair]
4954
var rootPath: String?
5055

5156
public init(crypto cr: CryptoParamsV3, id i: String, version ver: Int = 32, rootPath: String? = nil) {
5257
self.crypto = cr
5358
self.id = i
5459
self.version = ver
5560
pathToAddress = [String: String]()
61+
pathAddressPairs = [PathAddressPair]()
5662
self.rootPath = rootPath
5763
self.isHDWallet = true
5864
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// PathAddressStorage.swift
3+
// web3swift
4+
//
5+
// Created by Andrew Podkovyrin on 08.08.2020.
6+
// Copyright © 2020 Matter Labs. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
public struct PathAddressStorage {
12+
private(set) var addresses: [EthereumAddress]
13+
private(set) var paths: [String]
14+
15+
init() {
16+
addresses = []
17+
paths = []
18+
}
19+
20+
mutating func add(address: EthereumAddress, for path: String) {
21+
addresses.append(address)
22+
paths.append(path)
23+
}
24+
25+
func path(by address: EthereumAddress) -> String? {
26+
guard let index = addresses.firstIndex(of: address) else { return nil }
27+
return paths[index]
28+
}
29+
}
30+
31+
extension PathAddressStorage {
32+
init(pathAddressPairs: [PathAddressPair]) {
33+
var addresses = [EthereumAddress]()
34+
var paths = [String]()
35+
for pair in pathAddressPairs {
36+
guard let address = EthereumAddress(pair.address) else { continue }
37+
addresses.append(address)
38+
paths.append(pair.path)
39+
}
40+
41+
assert(addresses.count == paths.count)
42+
43+
self.addresses = addresses
44+
self.paths = paths
45+
}
46+
47+
func toPathAddressPairs() -> [PathAddressPair] {
48+
var pathAddressPairs = [PathAddressPair]()
49+
for (index, path) in paths.enumerated() {
50+
let address = addresses[index]
51+
let pair = PathAddressPair(path: path, address: address.address)
52+
pathAddressPairs.append(pair)
53+
}
54+
return pathAddressPairs
55+
}
56+
}

Tests/web3swiftTests/local_tests/web3swift_keystores_Tests.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class web3swift_Keystores_tests: XCTestCase {
136136
let account = keystore!.addresses![1]
137137
let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account)
138138
XCTAssertNotNil(key)
139-
print(keystore!.paths)
139+
print(keystore!.addressStorage.paths)
140140
}
141141

142142
func testByBIP32keystoreSaveAndDeriva() throws {
@@ -154,9 +154,8 @@ class web3swift_Keystores_tests: XCTestCase {
154154
print(keystore!.addresses![1].address)
155155
print(recreatedStore!.addresses![0].address)
156156
print(recreatedStore!.addresses![1].address)
157-
// It fails if run tests in batch and succeeds if run it separately
158-
XCTAssert(keystore?.addresses![1] == recreatedStore?.addresses![0])
159-
XCTAssert(keystore?.addresses![0] == recreatedStore?.addresses![1])
157+
XCTAssert(keystore?.addresses![0] == recreatedStore?.addresses![0])
158+
XCTAssert(keystore?.addresses![1] == recreatedStore?.addresses![1])
160159
}
161160

162161
func testPBKDF2() throws {

web3swift.xcodeproj/project.pbxproj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
13C3392521B6C62400F33F5E /* secp256k1_ec_mult_static_context.h in Headers */ = {isa = PBXBuildFile; fileRef = 13C338F621B6C62400F33F5E /* secp256k1_ec_mult_static_context.h */; };
5454
13C3392621B6C62400F33F5E /* scratch.h in Headers */ = {isa = PBXBuildFile; fileRef = 13C338F721B6C62400F33F5E /* scratch.h */; };
5555
13C3392821B6C68900F33F5E /* secp256k1.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 13C3388E21B6C2DD00F33F5E /* secp256k1.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
56+
2AC22E362525C2000072F037 /* PathAddressStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AC22E352525C2000072F037 /* PathAddressStorage.swift */; };
5657
3A7EA35E2280EA9A005120C2 /* Encodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7EA35D2280EA9A005120C2 /* Encodable+Extensions.swift */; };
5758
3A7EA3602280EB27005120C2 /* Decodable+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7EA35F2280EB27005120C2 /* Decodable+Extensions.swift */; };
5859
3AA8151C2276E42F00F5DB52 /* EventFiltering.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AA815172276E42F00F5DB52 /* EventFiltering.swift */; };
@@ -260,6 +261,7 @@
260261
13C338F721B6C62400F33F5E /* scratch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scratch.h; sourceTree = "<group>"; };
261262
13CE02B021FC846800CE7148 /* RELEASE_GUIDE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = RELEASE_GUIDE.md; sourceTree = "<group>"; };
262263
13CE02B121FC846900CE7148 /* BUILD_GUIDE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = BUILD_GUIDE.md; sourceTree = "<group>"; };
264+
2AC22E352525C2000072F037 /* PathAddressStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PathAddressStorage.swift; sourceTree = "<group>"; };
263265
3A7EA35D2280EA9A005120C2 /* Encodable+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Encodable+Extensions.swift"; sourceTree = "<group>"; };
264266
3A7EA35F2280EB27005120C2 /* Decodable+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Decodable+Extensions.swift"; sourceTree = "<group>"; };
265267
3AA815172276E42F00F5DB52 /* EventFiltering.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventFiltering.swift; sourceTree = "<group>"; };
@@ -285,7 +287,6 @@
285287
3AA815352276E44100F5DB52 /* BIP39+WordLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BIP39+WordLists.swift"; sourceTree = "<group>"; };
286288
3AA815362276E44100F5DB52 /* BIP32Keystore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BIP32Keystore.swift; sourceTree = "<group>"; };
287289
3AA815372276E44100F5DB52 /* EthereumKeystoreV3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EthereumKeystoreV3.swift; sourceTree = "<group>"; };
288-
3AA815382276E44100F5DB52 /* BIP32KeystoreJSONStructure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BIP32KeystoreJSONStructure.swift; sourceTree = "<group>"; };
289290
3AA815392276E44100F5DB52 /* PlainKeystore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlainKeystore.swift; sourceTree = "<group>"; };
290291
3AA8153A2276E44100F5DB52 /* AbstractKeystore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AbstractKeystore.swift; sourceTree = "<group>"; };
291292
3AA8153B2276E44100F5DB52 /* KeystoreV3JSONStructure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeystoreV3JSONStructure.swift; sourceTree = "<group>"; };
@@ -657,14 +658,14 @@
657658
isa = PBXGroup;
658659
children = (
659660
4E2DFEF325485B53001AF561 /* KeystoreParams.swift */,
661+
2AC22E352525C2000072F037 /* PathAddressStorage.swift */,
660662
3AA815312276E44100F5DB52 /* KeystoreManager.swift */,
661663
3AA815322276E44100F5DB52 /* IBAN.swift */,
662664
3AA815332276E44100F5DB52 /* BIP39.swift */,
663665
3AA815342276E44100F5DB52 /* BIP32HDNode.swift */,
664666
3AA815352276E44100F5DB52 /* BIP39+WordLists.swift */,
665667
3AA815362276E44100F5DB52 /* BIP32Keystore.swift */,
666668
3AA815372276E44100F5DB52 /* EthereumKeystoreV3.swift */,
667-
3AA815382276E44100F5DB52 /* BIP32KeystoreJSONStructure.swift */,
668669
3AA815392276E44100F5DB52 /* PlainKeystore.swift */,
669670
3AA8153A2276E44100F5DB52 /* AbstractKeystore.swift */,
670671
3AA8153B2276E44100F5DB52 /* KeystoreV3JSONStructure.swift */,
@@ -1355,6 +1356,7 @@
13551356
3AA815D62276E44100F5DB52 /* Web3+ReadingTransaction.swift in Sources */,
13561357
3AA815AC2276E44100F5DB52 /* NameHash.swift in Sources */,
13571358
3AA8151C2276E42F00F5DB52 /* EventFiltering.swift in Sources */,
1359+
2AC22E362525C2000072F037 /* PathAddressStorage.swift in Sources */,
13581360
);
13591361
runOnlyForDeploymentPostprocessing = 0;
13601362
};

0 commit comments

Comments
 (0)