-
Notifications
You must be signed in to change notification settings - Fork 0
feature/ethereum-service #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
a3ff0c0
8a1a04c
94c70d8
aa7b2f7
531f96a
d2c77c7
4a67b61
f6759d7
60cc8d4
cd08716
01f35f0
18e9781
9610444
96d7335
664006c
e9aa0c9
4acd5b8
192a91e
2701f54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| import Foundation | ||
|
|
||
| extension Data { | ||
| /// A hexadecimal string representation of the bytes. | ||
| func hexEncodedString() -> String { | ||
| let hexDigits = Array("0123456789abcdef".utf16) | ||
| var hexChars = [UTF16.CodeUnit]() | ||
| hexChars.reserveCapacity(count * 2) | ||
|
|
||
| for byte in self { | ||
| let (index1, index2) = Int(byte).quotientAndRemainder(dividingBy: 16) | ||
| hexChars.append(hexDigits[index1]) | ||
| hexChars.append(hexDigits[index2]) | ||
| } | ||
|
|
||
| return String(utf16CodeUnits: hexChars, count: hexChars.count) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import Foundation | ||
|
|
||
| extension String { | ||
| /// A data representation of the hexadecimal bytes in this string. | ||
| func hexDecodedData() -> Data { | ||
| // Get the UTF8 characters of this string | ||
| let chars = Array(utf8) | ||
|
|
||
| // Keep the bytes in an UInt8 array and later convert it to Data | ||
| var bytes = [UInt8]() | ||
| bytes.reserveCapacity(count / 2) | ||
|
|
||
| // It is a lot faster to use a lookup map instead of strtoul | ||
| let map: [UInt8] = [ | ||
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567 | ||
| 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>? | ||
| 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, // @ABCDEFG | ||
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // HIJKLMNO | ||
| ] | ||
|
|
||
| // Grab two characters at a time, map them and turn it into a byte | ||
| for i in stride(from: 0, to: count, by: 2) { | ||
| let index1 = Int(chars[i] & 0x1F ^ 0x10) | ||
| let index2 = Int(chars[i + 1] & 0x1F ^ 0x10) | ||
| bytes.append(map[index1] << 4 | map[index2]) | ||
| } | ||
|
|
||
| return Data(bytes) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| import Foundation | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar blocks of code found in 2 locations. Consider refactoring. |
||
| import UB | ||
|
|
||
| public class EthereumService: Service { | ||
| let url: URL | ||
|
|
||
| /// Initializes an Ethereum Service with a RPC HTTP url | ||
ec2 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /// | ||
| /// - Parameters: | ||
| /// - url: Ethereum JSONRPC endpoint | ||
| public init(url: URL) { | ||
| self.url = url | ||
| } | ||
|
|
||
| public func handle(message: Message, node: Node) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function body should span 40 lines or less excluding comments and whitespace: currently spans 53 lines There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Function body should span 40 lines or less excluding comments and whitespace: currently spans 48 lines |
||
| // The first btye of the message is the JSONRPC method | ||
| let methodID = message.message[0] | ||
|
|
||
| switch methodID { | ||
| case 0: | ||
| let address = "0x" + message.message[1 ..< message.message.count].hexEncodedString() | ||
| getBalance(address: address) { result in | ||
| switch result { | ||
| case let .success(balance): | ||
| let messageResponse = Message( | ||
| proto: UBID(repeating: 1, count: 1), | ||
| recipient: message.origin, | ||
| from: message.recipient, | ||
| origin: message.recipient, | ||
| message: balance | ||
| ) | ||
| node.send(messageResponse) | ||
| case let .failure(error): | ||
| print(error) | ||
| } | ||
| } | ||
| case 1: | ||
| let address = "0x" + message.message[1 ..< message.message.count].hexEncodedString() | ||
| getTransactionCount(address: address) { result in | ||
| switch result { | ||
| case let .success(nonce): | ||
| let messageResponse = Message( | ||
| proto: UBID(repeating: 1, count: 1), | ||
| recipient: message.origin, | ||
| from: message.recipient, | ||
| origin: message.recipient, | ||
| message: nonce | ||
| ) | ||
| node.send(messageResponse) | ||
| case let .failure(error): | ||
| print(error) | ||
| } | ||
| } | ||
| case 2: | ||
| let signedTransaction = "0x" + message.message[1 ..< message.message.count].hexEncodedString() | ||
| sendRawTransaction(signedTransaction: signedTransaction) { result in | ||
| switch result { | ||
| case let .success(balance): | ||
| let messageResponse = Message( | ||
| proto: UBID(repeating: 1, count: 1), | ||
| recipient: message.origin, | ||
| from: message.recipient, | ||
| origin: message.recipient, | ||
| message: balance | ||
| ) | ||
| node.send(messageResponse) | ||
| case let .failure(error): | ||
| print(error) | ||
| } | ||
| } | ||
| return | ||
| default: | ||
| print("default") | ||
| return | ||
| } | ||
| } | ||
|
|
||
| internal func httpRequest(method: String, params: [String], completion: @escaping (Result<Data, Error>) -> Void) { | ||
| var request = URLRequest(url: url) | ||
| request.httpMethod = "POST" | ||
| let payload: [String: Any] = [ | ||
| "jsonrpc": "2.0", | ||
| "method": method, | ||
| "params": params, | ||
| "id": 1, | ||
| ] | ||
| guard let jsonPayload = (try? JSONSerialization.data(withJSONObject: payload)) else { | ||
| print("Error: JSON could not serialize") | ||
| return | ||
| } | ||
| request.httpBody = jsonPayload | ||
|
|
||
| _ = URLSession.shared.dataTask(with: request) { data, _, error in | ||
| guard let data = data, error == nil else { | ||
| print(error?.localizedDescription ?? "No data") | ||
| completion(.failure(error!)) | ||
| return | ||
| } | ||
| completion(.success(data)) | ||
| }.resume() | ||
| } | ||
|
|
||
| internal func getBalance(address: String, completion: @escaping (Result<Data, Error>) -> Void) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar blocks of code found in 2 locations. Consider refactoring. |
||
| httpRequest(method: "eth_getBalance", params: [address, "latest"]) { result in | ||
| switch result { | ||
| case let .success(balance): | ||
| completion(.success(balance)) | ||
| case let .failure(error): | ||
| completion(.failure(error)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| internal func getTransactionCount(address: String, completion: @escaping (Result<Data, Error>) -> Void) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar blocks of code found in 2 locations. Consider refactoring. |
||
| httpRequest(method: "eth_getTransactionCount", params: [address, "latest"]) { result in | ||
| switch result { | ||
| case let .success(nonce): | ||
| completion(.success(nonce)) | ||
| case let .failure(error): | ||
| completion(.failure(error)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| internal func sendRawTransaction(signedTransaction: String, completion: @escaping (Result<Data, Error>) -> Void) { | ||
| httpRequest(method: "eth_sendRawTransaction", params: [signedTransaction]) { result in | ||
| switch result { | ||
| case let .success(txHash): | ||
| completion(.success(txHash)) | ||
| case let .failure(error): | ||
| completion(.failure(error)) | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,5 @@ | ||
| import XCTest | ||
|
|
||
|
|
||
| var tests = [XCTestCaseEntry]() | ||
|
|
||
| XCTMain(tests) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| @testable import RelayerFramework | ||
| import XCTest | ||
|
|
||
| final class EthereumServiceTests: XCTestCase { | ||
| func testGetBalance() { | ||
| let didFinish = expectation(description: #function) | ||
| let url: URL = URL(string: "https://rinkeby.infura.io/f7a08ae0242843f1b1cf480454a6bba5")! | ||
| let ethService = EthereumService(url: url) | ||
| var bal = "" | ||
| ethService.getBalance(address: "0x0F64928EcA02147075c7614A7d67B0C3Cb37D5DA") { result in | ||
| switch result { | ||
| case let .success(balance): | ||
| bal = String(data: balance, encoding: .utf8)! | ||
| didFinish.fulfill() | ||
| case let .failure(error): | ||
| print(error) | ||
| } | ||
| } | ||
|
|
||
| wait(for: [didFinish], timeout: 5) | ||
| XCTAssertEqual(bal, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"0x1a55734bf06dc800\"}") | ||
| } | ||
|
|
||
| static var allTests = [ | ||
| ("testGetBalance", testGetBalance), | ||
| ] | ||
| } |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variable name should be between 3 and 40 characters long: 'i'