Skip to content

Commit eee45a7

Browse files
author
Alex Vlasov
committed
implement checksum verification in address generation if address has various capitalization
1 parent cfd3f90 commit eee45a7

File tree

8 files changed

+77
-17
lines changed

8 files changed

+77
-17
lines changed

web3swift/Concurrency/Classes/Web3+TransactionOperations.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ final class ContractSendOperation: Web3Operation {
106106

107107
convenience init?(_ web3Instance: web3, queue: OperationQueue? = nil, contract: web3.web3contract, method: String = "fallback", parameters: [AnyObject] = [], extraData: Data = Data(), options: Web3Options?, onBlock: String = "pending", password: String = "BANKEXFOUNDATION") {
108108
guard let intermediate = contract.method(method, parameters: parameters, extraData: extraData, options: options) else {return nil}
109-
self.init(web3Instance, queue: queue, inputData: [intermediate, password, onBlock, options] as AnyObject)
109+
self.init(web3Instance, queue: queue, inputData: [intermediate, password, onBlock, options as Any] as AnyObject)
110110
}
111111

112112
convenience init?(_ web3Instance: web3, queue: OperationQueue? = nil, intermediate: TransactionIntermediate, options: Web3Options? = nil, onBlock: String = "pending", password: String = "BANKEXFOUNDATION") {
113-
self.init(web3Instance, queue: queue, inputData: [intermediate, password, onBlock, options] as AnyObject)
113+
self.init(web3Instance, queue: queue, inputData: [intermediate, password, onBlock, options as Any] as AnyObject)
114114
}
115115

116116
override func main() {
@@ -120,7 +120,7 @@ final class ContractSendOperation: Web3Operation {
120120
guard let completion = self.next else {return processError(Web3Error.inputError("Invalid input supplied"))}
121121
guard inputData != nil else {return processError(Web3Error.inputError("Invalid input supplied"))}
122122
guard let input = inputData! as? [AnyObject] else {return processError(Web3Error.inputError("Invalid input supplied"))}
123-
guard input.count == 3 else {return processError(Web3Error.inputError("Invalid input supplied"))}
123+
guard input.count == 4 else {return processError(Web3Error.inputError("Invalid input supplied"))}
124124
guard let intermediate = input[0] as? TransactionIntermediate else {return processError(Web3Error.inputError("Invalid input supplied"))}
125125
guard let password = input[1] as? String else {return processError(Web3Error.inputError("Invalid input supplied"))}
126126
guard let onBlock = input[2] as? String else {return processError(Web3Error.inputError("Invalid input supplied"))}

web3swift/KeystoreManager/Classes/BIP32HDNode.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ public class HDNode {
119119
private static var curveOrder = BigUInt("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", radix: 16)!
120120
public static var defaultPath: String = "m/44'/60'/0'/0"
121121
public static var defaultPathPrefix: String = "m/44'/60'/0'"
122+
public static var defaultPathMetamask: String = "m/44'/60'/0'/0/0"
123+
public static var defaultPathMetamaskPrefix: String = "m/44'/60'/0'/0"
122124
public static var hardenedIndexPrefix: UInt32 = (UInt32(1) << 31)
123125
}
124126

web3swift/KeystoreManager/Classes/BIP32Keystore.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ public class BIP32Keystore: AbstractKeystore {
3333
if let key = self.paths.keyForValue(value: account) {
3434
guard let decryptedRootNode = try? self.getPrefixNodeData(password), decryptedRootNode != nil else {throw AbstractKeystoreError.encryptionError("Failed to decrypt a keystore")}
3535
guard let rootNode = HDNode(decryptedRootNode!) else {throw AbstractKeystoreError.encryptionError("Failed to deserialize a root node")}
36-
guard rootNode.depth == HDNode.defaultPathPrefix.components(separatedBy: "/").count - 1 else {throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")}
36+
guard rootNode.depth == (self.rootPrefix.components(separatedBy: "/").count - 1) else {throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")}
37+
// guard rootNode.depth == HDNode.defaultPathPrefix.components(separatedBy: "/").count - 1 else {throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")}
3738
guard let index = UInt32(key.components(separatedBy: "/").last!) else {throw AbstractKeystoreError.encryptionError("Derivation depth mismatch")}
3839
guard let keyNode = rootNode.derive(index: index, derivePrivateKey: true) else {throw AbstractKeystoreError.encryptionError("Derivation failed")}
3940
guard let privateKey = keyNode.privateKey else {throw AbstractKeystoreError.invalidAccountError}
@@ -69,13 +70,13 @@ public class BIP32Keystore: AbstractKeystore {
6970
rootPrefix = keystoreParams!.rootPath!
7071
}
7172

72-
public convenience init? (mnemonics: String, password: String = "BANKEXFOUNDATION", mnemonicsPassword: String = "", language: BIP39Language = BIP39Language.english, prefixPath: String = HDNode.defaultPathPrefix) throws {
73+
public convenience init? (mnemonics: String, password: String = "BANKEXFOUNDATION", mnemonicsPassword: String = "", language: BIP39Language = BIP39Language.english, prefixPath: String = HDNode.defaultPathMetamaskPrefix) throws {
7374
guard var seed = BIP39.seedFromMmemonics(mnemonics, password: mnemonicsPassword, language: language) else {throw AbstractKeystoreError.noEntropyError}
7475
defer{ Data.zero(&seed) }
7576
try self.init(seed: seed, password: password, prefixPath: prefixPath)
7677
}
7778

78-
public init? (seed: Data, password: String = "BANKEXFOUNDATION", prefixPath: String = HDNode.defaultPathPrefix) throws {
79+
public init? (seed: Data, password: String = "BANKEXFOUNDATION", prefixPath: String = HDNode.defaultPathMetamaskPrefix) throws {
7980
guard let prefixNode = HDNode(seed: seed)?.derive(path: prefixPath, derivePrivateKey: true) else {return nil}
8081
self.rootPrefix = prefixPath
8182
try createNewAccount(parentNode: prefixNode, password: password)
@@ -255,4 +256,11 @@ public class BIP32Keystore: AbstractKeystore {
255256
let data = try JSONEncoder().encode(params)
256257
return data
257258
}
259+
260+
public func serializeRootNodeToString(password: String = "BANKEXFOUNDATION") throws -> String {
261+
guard let decryptedRootNode = try? self.getPrefixNodeData(password), decryptedRootNode != nil else {throw AbstractKeystoreError.encryptionError("Failed to decrypt a keystore")}
262+
guard let rootNode = HDNode(decryptedRootNode!) else {throw AbstractKeystoreError.encryptionError("Failed to deserialize a root node")}
263+
guard let string = rootNode.serializeToString(serializePublic: false) else {throw AbstractKeystoreError.encryptionError("Failed to deserialize a root node")}
264+
return string
265+
}
258266
}

web3swift/KeystoreManager/Classes/EthereumAddress.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,15 @@ public struct EthereumAddress: Equatable {
7474
return ret
7575
}
7676

77-
public init?(_ addressString:String, type: AddressType = .normal) {
77+
public init?(_ addressString:String, type: AddressType = .normal, ignoreChecksum: Bool = false) {
7878
switch type {
7979
case .normal:
8080
guard let data = Data.fromHex(addressString) else {return nil}
8181
guard data.count == 20 else {return nil}
82+
if (!ignoreChecksum && data.toHexString().addHexPrefix() != addressString.lowercased()) {
83+
let checksummedAddress = EthereumAddress.toChecksumAddress(data.toHexString().addHexPrefix())
84+
guard checksummedAddress == addressString else {return nil}
85+
}
8286
self._address = data.toHexString().addHexPrefix()
8387
self.type = .normal
8488
case .contractDeployment:

web3swift/Web3/Classes/Web3+Structures.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,8 @@ public struct Block:Decodable {
289289
let minerAddress = try? container.decode(String.self, forKey: .miner)
290290
var miner:EthereumAddress?
291291
if minerAddress != nil {
292-
miner = EthereumAddress(minerAddress!)
293-
guard miner!.isValid else {throw Web3Error.dataError}
292+
guard let minr = EthereumAddress(minerAddress!) else {throw Web3Error.dataError}
293+
miner = minr
294294
}
295295
self.miner = miner
296296

web3swift/Web3/Classes/Web3+Utils.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,9 @@ extension Web3.Utils {
152152
}
153153
}
154154

155-
public static func formatToPrecision(_ bigNumber: BigInt, numberDecimals: Int = 18, formattingDecimals: Int = 4, decimalSeparator: String = ".") -> String? {
155+
public static func formatToPrecision(_ bigNumber: BigInt, numberDecimals: Int = 18, formattingDecimals: Int = 4, decimalSeparator: String = ".", fallbackToScientific: Bool = false) -> String? {
156156
let magnitude = bigNumber.magnitude
157-
guard let formatted = formatToPrecision(magnitude, numberDecimals: numberDecimals, formattingDecimals: formattingDecimals, decimalSeparator: decimalSeparator) else {return nil}
157+
guard let formatted = formatToPrecision(magnitude, numberDecimals: numberDecimals, formattingDecimals: formattingDecimals, decimalSeparator: decimalSeparator, fallbackToScientific: fallbackToScientific) else {return nil}
158158
switch bigNumber.sign {
159159
case .plus:
160160
return formatted

web3swiftTests/web3swiftTests.swift

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,34 @@ class web3swiftTests: XCTestCase {
142142
XCTAssertNotNil(key)
143143
}
144144

145+
func testBIP32keystoreMatching() {
146+
let mnemonic = "fruit wave dwarf banana earth journey tattoo true farm silk olive fence"
147+
let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana")
148+
XCTAssertNotNil(keystore)
149+
let account = keystore!.addresses![0]
150+
let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account)
151+
let pubKey = Web3.Utils.privateToPublic(key, compressed: true);
152+
XCTAssert(pubKey?.toHexString() == "027160bd3a4d938cac609ff3a11fe9233de7b76c22a80d2b575e202cbf26631659")
153+
}
154+
155+
func testBIP32keystoreMatchingRootNode() {
156+
let mnemonic = "fruit wave dwarf banana earth journey tattoo true farm silk olive fence"
157+
let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana")
158+
XCTAssertNotNil(keystore)
159+
let rootNode = try! keystore!.serializeRootNodeToString(password: "")
160+
XCTAssert(rootNode == "xprvA2KM71v838kPwE8Lfr12m9DL939TZmPStMnhoFcZkr1nBwDXSG7c3pjYbMM9SaqcofK154zNSCp7W7b4boEVstZu1J3pniLQJJq7uvodfCV")
161+
}
162+
163+
func testBIP32keystoreCustomPathMatching() {
164+
let mnemonic = "fruit wave dwarf banana earth journey tattoo true farm silk olive fence"
165+
let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "banana", prefixPath:"m/44'/60'/0'/0")
166+
XCTAssertNotNil(keystore)
167+
let account = keystore!.addresses![0]
168+
let key = try! keystore!.UNSAFE_getPrivateKeyData(password: "", account: account)
169+
let pubKey = Web3.Utils.privateToPublic(key, compressed: true);
170+
XCTAssert(pubKey?.toHexString() == "027160bd3a4d938cac609ff3a11fe9233de7b76c22a80d2b575e202cbf26631659")
171+
}
172+
145173
func testByBIP32keystoreCreateChildAccount() {
146174
let mnemonic = "normal dune pole key case cradle unfold require tornado mercy hospital buyer"
147175
let keystore = try! BIP32Keystore(mnemonics: mnemonic, password: "", mnemonicsPassword: "")
@@ -458,6 +486,15 @@ class web3swiftTests: XCTestCase {
458486
let output = EthereumAddress.toChecksumAddress(input);
459487
XCTAssert(output == "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", "Failed to checksum address")
460488
}
489+
490+
func testChecksumAddressParsing() {
491+
let input = "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
492+
let addr = EthereumAddress(input);
493+
XCTAssert(addr != nil);
494+
let invalidInput = "0xfb6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
495+
let invalidAddr = EthereumAddress(invalidInput);
496+
XCTAssert(invalidAddr == nil);
497+
}
461498

462499
func testTransaction() {
463500
do {
@@ -1924,7 +1961,7 @@ class web3swiftTests: XCTestCase {
19241961
case .failure(let error):
19251962
print(error)
19261963
XCTFail()
1927-
fatalError()
1964+
// fatalError()
19281965
}
19291966
OperationQueue.current?.underlyingQueue?.async {
19301967
expected = expected - 1
@@ -2013,8 +2050,9 @@ class web3swiftTests: XCTestCase {
20132050
print(error)
20142051
if case .nodeError(_) = error {
20152052
fail = false
2053+
break
20162054
}
2017-
// XCTFail()
2055+
XCTFail()
20182056
// fatalError()
20192057
}
20202058
semaphore.signal()
@@ -2233,7 +2271,9 @@ class web3swiftTests: XCTestCase {
22332271
print(error)
22342272
XCTFail()
22352273
}
2236-
let bkxBalanceSend = intermediate!.call(options: nil)
2274+
var options = Web3Options();
2275+
options.gasLimit = gasEstimate.value!
2276+
let bkxBalanceSend = intermediate!.call(options: options)
22372277
switch bkxBalanceSend {
22382278
case .success(let result):
22392279
print(result)
@@ -2260,7 +2300,7 @@ class web3swiftTests: XCTestCase {
22602300
func testNumberFormattingUtil() {
22612301
let balance = BigInt("-1000000000000000000")!
22622302
let formatted = Web3.Utils.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 4, decimalSeparator: ",")
2263-
XCTAssert(formatted == "-1,0000")
2303+
XCTAssert(formatted == "-1")
22642304
}
22652305

22662306
func testNumberFormattingUtil2() {
@@ -2283,7 +2323,7 @@ class web3swiftTests: XCTestCase {
22832323

22842324
func testNumberFormattingUtil5() {
22852325
let balance = BigInt("-1")!
2286-
let formatted = Web3.Utils.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 9, decimalSeparator: ",")
2326+
let formatted = Web3.Utils.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 9, decimalSeparator: ",", fallbackToScientific: true)
22872327
XCTAssert(formatted == "-1e-18")
22882328
}
22892329

@@ -2293,6 +2333,12 @@ class web3swiftTests: XCTestCase {
22932333
XCTAssert(formatted == "0")
22942334
}
22952335

2336+
func testNumberFormattingUtil7() {
2337+
let balance = BigInt("-1100000000000000000")!
2338+
let formatted = Web3.Utils.formatToPrecision(balance, numberDecimals: 18, formattingDecimals: 4, decimalSeparator: ",")
2339+
XCTAssert(formatted == "-1,1000")
2340+
}
2341+
22962342
func testPerformanceExample() {
22972343
// This is an example of a performance test case.
22982344
self.measure {

web3swiftTests/web3swift_remote_Tests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ class web3swift_remote_Tests: XCTestCase {
131131
let blockNumber = web3.eth.getBlockNumber()
132132
guard case .success(let currentBlock) = blockNumber else {return XCTFail()}
133133
let currentBlockAsInt = UInt64(currentBlock)
134-
for i in currentBlockAsInt-3 ... currentBlockAsInt {
134+
for i in currentBlockAsInt-1 ... currentBlockAsInt {
135135
let present = eventParser.parseBlockByNumber(i)
136136
guard case .success(let pres) = present else {return XCTFail()}
137137
for p in pres {

0 commit comments

Comments
 (0)