-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from soramitsu/feature/qr-decoding
add qr code encoding/decoding
- Loading branch information
Showing
11 changed files
with
273 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import Foundation | ||
|
||
extension Data { | ||
func matchAccountId(_ accountId: Data) -> Bool { | ||
if accountId == self { | ||
return true | ||
} | ||
|
||
return accountId == (try? self.blake2b32()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import Foundation | ||
|
||
public struct SubstrateQRInfo: Equatable { | ||
public let prefix: String | ||
public let address: String | ||
public let rawPublicKey: Data | ||
public let username: String? | ||
|
||
public init(prefix: String = SubstrateQR.prefix, | ||
address: String, | ||
rawPublicKey: Data, | ||
username: String?) { | ||
self.prefix = prefix | ||
self.address = address | ||
self.rawPublicKey = rawPublicKey | ||
self.username = username | ||
} | ||
} | ||
|
||
public protocol SubstrateQREncodable { | ||
func encode(info: SubstrateQRInfo) throws -> Data | ||
} | ||
|
||
public protocol SubstrateQRDecodable { | ||
func decode(data: Data) throws -> SubstrateQRInfo | ||
} | ||
|
||
public enum SubstrateQREncoderError: Error, Equatable { | ||
case brokenData | ||
} | ||
|
||
public enum SubstrateQRDecoderError: Error, Equatable { | ||
case brokenFormat | ||
case unexpectedNumberOfFields | ||
case undefinedPrefix | ||
case accountIdMismatch | ||
} | ||
|
||
public struct SubstrateQR { | ||
public static let prefix: String = "substrate" | ||
public static let fieldsSeparator: String = ":" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import Foundation | ||
import IrohaCrypto | ||
|
||
open class SubstrateQRDecoder: SubstrateQRDecodable { | ||
public let networkType: SNAddressType | ||
public let separator: String | ||
public let prefix: String | ||
|
||
private lazy var addressFactory = SS58AddressFactory() | ||
|
||
public init(networkType: SNAddressType, | ||
prefix: String = SubstrateQR.prefix, | ||
separator: String = SubstrateQR.fieldsSeparator) { | ||
self.prefix = prefix | ||
self.networkType = networkType | ||
self.separator = separator | ||
} | ||
|
||
public func decode(data: Data) throws -> SubstrateQRInfo { | ||
guard let fields = String(data: data, encoding: .utf8)? | ||
.components(separatedBy: separator) else { | ||
throw SubstrateQRDecoderError.brokenFormat | ||
} | ||
|
||
guard fields.count >= 3, fields.count <= 4 else { | ||
throw SubstrateQRDecoderError.unexpectedNumberOfFields | ||
} | ||
|
||
guard fields[0] == prefix else { | ||
throw SubstrateQRDecoderError.undefinedPrefix | ||
} | ||
|
||
let address = fields[1] | ||
let accountId = try addressFactory.accountId(fromAddress: address, type: networkType) | ||
let publicKey = try Data(hexString: fields[2]) | ||
|
||
guard publicKey.matchAccountId(accountId) else { | ||
throw SubstrateQRDecoderError.accountIdMismatch | ||
} | ||
|
||
let username = fields.count > 3 ? fields[3] : nil | ||
|
||
return SubstrateQRInfo(prefix: prefix, | ||
address: address, | ||
rawPublicKey: publicKey, | ||
username: username) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import Foundation | ||
import IrohaCrypto | ||
|
||
open class SubstrateQREncoder: SubstrateQREncodable { | ||
let separator: String | ||
|
||
public init(separator: String = SubstrateQR.fieldsSeparator) { | ||
self.separator = separator | ||
} | ||
|
||
public func encode(info: SubstrateQRInfo) throws -> Data { | ||
var fields: [String] = [ | ||
info.prefix, | ||
info.address, | ||
info.rawPublicKey.toHex(includePrefix: true) | ||
] | ||
|
||
if let username = info.username { | ||
fields.append(username) | ||
} | ||
|
||
guard let data = fields.joined(separator: separator).data(using: .utf8) else { | ||
throw SubstrateQREncoderError.brokenData | ||
} | ||
|
||
return data | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import XCTest | ||
import FearlessUtils | ||
|
||
class SubstrateQRDecoderTests: XCTestCase { | ||
func testSuccessfullDecodingWithName() throws { | ||
let data = try Data(hexString: "7375627374726174653a354633794b66354b7651343978356b346e5258624157674a485368516a51544245655a3950754e646d6562614e7837343a3078383432353834306636633337306637663264383239316333653639303436376530613866626238623036343866633964363439356439396463613763376133663a425642") | ||
|
||
let publicKey = try Data(hexString: "0x8425840f6c370f7f2d8291c3e690467e0a8fbb8b0648fc9d6495d99dca7c7a3f") | ||
let expectedInfo = SubstrateQRInfo(prefix: SubstrateQR.prefix, | ||
address: "5F3yKf5KvQ49x5k4nRXbAWgJHShQjQTBEeZ9PuNdmebaNx74", | ||
rawPublicKey: publicKey, | ||
username: "BVB") | ||
|
||
let decodedInfo = try SubstrateQRDecoder(networkType: .genericSubstrate).decode(data: data) | ||
|
||
XCTAssertEqual(decodedInfo, expectedInfo) | ||
} | ||
|
||
func testSuccessfullDecodingWithoutName() throws { | ||
let data = try Data(hexString: "7375627374726174653a354633794b66354b7651343978356b346e5258624157674a485368516a51544245655a3950754e646d6562614e7837343a307838343235383430663663333730663766326438323931633365363930343637653061386662623862303634386663396436343935643939646361376337613366") | ||
|
||
let publicKey = try Data(hexString: "0x8425840f6c370f7f2d8291c3e690467e0a8fbb8b0648fc9d6495d99dca7c7a3f") | ||
let expectedInfo = SubstrateQRInfo(prefix: SubstrateQR.prefix, | ||
address: "5F3yKf5KvQ49x5k4nRXbAWgJHShQjQTBEeZ9PuNdmebaNx74", | ||
rawPublicKey: publicKey, | ||
username: nil) | ||
|
||
let decodedInfo = try SubstrateQRDecoder(networkType: .genericSubstrate).decode(data: data) | ||
|
||
XCTAssertEqual(decodedInfo, expectedInfo) | ||
} | ||
|
||
func testUndefinedPrefix() throws { | ||
let data = try Data(hexString: "73756273747261743a354633794b66354b7651343978356b346e5258624157674a485368516a51544245655a3950754e646d6562614e7837343a307838343235383430663663333730663766326438323931633365363930343637653061386662623862303634386663396436343935643939646361376337613366") | ||
|
||
perforErrorTest(data, expectedError: .undefinedPrefix) | ||
} | ||
|
||
func testUnexpectedNumberOfFields() throws { | ||
let data = try Data(hexString: "7375627374726174653a354633794b66354b7651343978356b346e5258624157674a485368516a51544245655a3950754e646d6562614e7837343a3078383432353834306636633337306637663264383239316333653639303436376530613866626238623036343866633964363439356439396463613763376133663a31323a313233") | ||
|
||
perforErrorTest(data, expectedError: .unexpectedNumberOfFields) | ||
} | ||
|
||
func testAccountIdMismatch() throws { | ||
let data = try Data(hexString: "7375627374726174653a354633794b66354b7651343978356b346e5258624157674a485368516a51544245655a3950754e646d6562614e7837343a3078383432353834306636633337306637663364383239316333653639303436376530613866626238623036343866633964363439356439396463613763376133663a425642") | ||
|
||
perforErrorTest(data, expectedError: .accountIdMismatch) | ||
} | ||
|
||
// MARK: Private | ||
|
||
private func perforErrorTest(_ data: Data, expectedError: SubstrateQRDecoderError) { | ||
do { | ||
_ = try SubstrateQRDecoder(networkType: .genericSubstrate).decode(data: data) | ||
|
||
XCTFail("Exception expected") | ||
} catch { | ||
guard let decodingError = error as? SubstrateQRDecoderError else { | ||
XCTFail("Decoding error expected") | ||
return | ||
} | ||
|
||
XCTAssertEqual(decodingError, expectedError) | ||
} | ||
} | ||
} |
Oops, something went wrong.