Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// swift-tools-version:5.0
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
Expand Down
14 changes: 13 additions & 1 deletion Sources/Relayer/main.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation
import UB
import RelayerFramework
import UB

let handler = Handler()

Expand All @@ -9,5 +9,17 @@ node.delegate = handler
node.add(transport: CoreBluetoothTransport())

// @todo handle CLI input, repl
let service = EthereumService(url: URL(string: "https://rinkeby.infura.io/f7a08ae0242843f1b1cf480454a6bba5")!)

// Get Balance
let msg = Message(
proto: UBID(repeating: 1, count: 1),
recipient: Addr(repeating: 1, count: 1),
from: Addr(repeating: 1, count: 1),
origin: UBID(repeating: 1, count: 1),
message: "000F64928EcA02147075c7614A7d67B0C3Cb37D5DA".hexDecodedData()
)

service.handle(message: msg, node: node)

RunLoop.current.run()
18 changes: 18 additions & 0 deletions Sources/RelayerFramework/Extensions/Data+HexEncodedString.swift
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)
}
}
30 changes: 30 additions & 0 deletions Sources/RelayerFramework/Extensions/String+HexDecodedData.swift
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) {

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'

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)
}
}
6 changes: 3 additions & 3 deletions Sources/RelayerFramework/Handler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ public class Handler: UB.NodeDelegate {
fileprivate var services = [UB.UBID: Service]()

/// Initializes a new Handler.
public init() { }
public init() {}

// @todo there probably should be more stuff here at one point.

/// :nodoc:
public func node(_: Node, didReceiveMessage message: Message) {
public func node(_ node: Node, didReceiveMessage message: Message) {
// @todo check if the message was just sent to us
if message.proto.count == 0 {
return
}

guard let service = services[message.proto] else { return }
service.handle(message: message)
service.handle(message: message, node: node)
}
}
135 changes: 135 additions & 0 deletions Sources/RelayerFramework/Services/EthereumService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import Foundation

Choose a reason for hiding this comment

The 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
///
/// - Parameters:
/// - url: Ethereum JSONRPC endpoint
public init(url: URL) {
self.url = url
}

public func handle(message: Message, node: Node) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function handle has 63 lines of code (exceeds 25 allowed). Consider refactoring.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function handle has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.

Choose a reason for hiding this comment

The 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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function handle has 58 lines of code (exceeds 25 allowed). Consider refactoring.

Choose a reason for hiding this comment

The 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) {

Choose a reason for hiding this comment

The 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) {

Choose a reason for hiding this comment

The 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))
}
}
}
}
2 changes: 1 addition & 1 deletion Sources/RelayerFramework/Services/Service.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ public protocol Service: AnyObject {
///
/// - Parameters:
/// - message: The received message to handle.
func handle(message: UB.Message)
func handle(message: UB.Message, node: UB.Node)
}
1 change: 0 additions & 1 deletion Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import XCTest


var tests = [XCTestCaseEntry]()

XCTMain(tests)
27 changes: 27 additions & 0 deletions Tests/RelayerFrameworkTests/EthereumServiceTests.swift
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),
]
}
47 changes: 0 additions & 47 deletions Tests/RelayerFrameworkTests/RelayerTests.swift

This file was deleted.