diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 000000000..e4faacdf8 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,53 @@ +# This workflow will build a Swift project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift + +name: Ubuntu + +on: + push: + branches: + - master + - develop + - hotfix + - unstable + paths: + - Packag*.swift + - web3swift.podspec + - Cartfile + - Sources/** + - 'Tests/**' + - 'web3swift*/**' + - '.github/workflows/**' + pull_request: + branches: + - master + - develop + - unstable + +jobs: + build: + + runs-on: ubuntu-latest + container: swift:5.7-focal + + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Resolve dependencies + run: swift package resolve + - name: Build + run: swift build + - name: Install ganache + run: npm install ganache --global + - name: Start ganache in background + run: ganache & + - name: Wait till ganache starts + run: sleep 1 + - name: install nc + run: apt-get update && apt-get install -y netcat + - name: Ping + run: nc -vz 127.0.0.1 8545 + - name: Run tests + run: swift test diff --git a/Package.swift b/Package.swift index faf66d529..e82c25956 100755 --- a/Package.swift +++ b/Package.swift @@ -3,12 +3,12 @@ import PackageDescription -#if os(macOS) +#if os(iOS) +let excludeFiles: String = [] +#else let excludeFiles = [ "./Browser/BrowserViewController.swift" // Because of inheriting iOS only class failed to build on macOS. ] -#elseif os(iOS) -let excludeFiles: String = [] #endif let package = Package( diff --git a/Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift b/Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift index 127bd8e6b..c457b2ef8 100644 --- a/Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift +++ b/Sources/Core/EthereumNetwork/Request/APIRequest+Methods.swift @@ -5,6 +5,9 @@ // Created by Yaroslav Yashin on 12.07.2022. // +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif import Foundation import BigInt diff --git a/Sources/Core/EthereumNetwork/Utility/Async+BackwardCapability.swift b/Sources/Core/EthereumNetwork/Utility/Async+BackwardCapability.swift index 46658db72..22e0980e0 100644 --- a/Sources/Core/EthereumNetwork/Utility/Async+BackwardCapability.swift +++ b/Sources/Core/EthereumNetwork/Utility/Async+BackwardCapability.swift @@ -5,6 +5,9 @@ // Created by Yaroslav Yashin on 05.06.2022. // +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif import Foundation @available(iOS, obsoleted: 15.0, message: "Use the built-in API instead") diff --git a/Sources/Core/Structure/SECP256k1.swift b/Sources/Core/Structure/SECP256k1.swift index 787c5ee34..818a9221e 100755 --- a/Sources/Core/Structure/SECP256k1.swift +++ b/Sources/Core/Structure/SECP256k1.swift @@ -336,6 +336,10 @@ extension SECP256K1 { } internal static func randomBytes(length: Int) -> Data? { + #if os(Linux) +// return Data(URandom().bytes(count: length)) + return try? Data.random(length: length) + #else for _ in 0...1024 { var data = Data(repeating: 0, count: length) let result = data.withUnsafeMutableBytes { (mutableRBBytes) -> Int32? in @@ -353,6 +357,7 @@ extension SECP256K1 { } } return nil + #endif } internal static func toByteArray(_ value: T) -> [UInt8] { diff --git a/Sources/Core/Structure/Web3ProviderProtocol.swift b/Sources/Core/Structure/Web3ProviderProtocol.swift index 68deae031..1f743a6f2 100644 --- a/Sources/Core/Structure/Web3ProviderProtocol.swift +++ b/Sources/Core/Structure/Web3ProviderProtocol.swift @@ -5,6 +5,9 @@ // Created by Yaroslav Yashin on 11.07.2022. // +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif import Foundation public protocol Web3Provider { diff --git a/Sources/Core/Utility/Data+Extension.swift b/Sources/Core/Utility/Data+Extension.swift index 4bc94645d..049573f93 100755 --- a/Sources/Core/Utility/Data+Extension.swift +++ b/Sources/Core/Utility/Data+Extension.swift @@ -5,6 +5,7 @@ import Foundation + extension Data { init(fromArray values: [T]) { let values = values @@ -41,6 +42,10 @@ extension Data { } public static func randomBytes(length: Int) -> Data? { + #if os(Linux) +// return Data(URandom().bytes(count: length)) + return try? Data.random(length: length) + #else for _ in 0...1024 { var data = Data(repeating: 0, count: length) let result = data.withUnsafeMutableBytes { (body: UnsafeMutableRawBufferPointer) -> Int32? in @@ -56,6 +61,7 @@ extension Data { } } return nil + #endif } public func bitsInRange(_ startingBit: Int, _ length: Int) -> UInt64? { // return max of 8 bytes for simplicity, non-public diff --git a/Sources/Core/Utility/URandom.swift b/Sources/Core/Utility/URandom.swift new file mode 100644 index 000000000..12edccab4 --- /dev/null +++ b/Sources/Core/Utility/URandom.swift @@ -0,0 +1,75 @@ +// +// File.swift +// +// +// Created by Ronald Mannak on 10/29/22. +// + +import Foundation +#if canImport(libc) +import libc + + +///// URandom represents a file connection to /dev/urandom on Unix systems. +///// /dev/urandom is a cryptographically secure random generator provided by the OS. +//public final class URandom: RandomProtocol { +// public enum Error: Swift.Error { +// case open(Int32) +// case read(Int32) +// } +// +// private let file: UnsafeMutablePointer +// +// /// Initialize URandom +// public init(path: String) throws { +// guard let file = fopen(path, "rb") else { +// // The Random protocol doesn't allow init to fail, so we have to +// // check whether /dev/urandom was successfully opened here +// throw Error.open(errno) +// } +// self.file = file +// } +// +// deinit { +// fclose(file) +// } +// +// private func read(numBytes: Int) throws -> [Int8] { +// +// +// // Initialize an empty array with space for numBytes bytes +// var bytes = [Int8](repeating: 0, count: numBytes) +// guard fread(&bytes, 1, numBytes, file) == numBytes else { +// // If the requested number of random bytes couldn't be read, +// // we need to throw an error +// throw Error.read(errno) +// } +// +// return bytes +// } +// +// /// Get a random array of Bytes +// public func bytes(count: Int) throws -> Bytes { +// return try read(numBytes: count).map({ Byte(bitPattern: $0) }) +// } +//} +// +//extension URandom: EmptyInitializable { +// public convenience init() throws { +// try self.init(path: "/dev/urandom") +// } +//} + +#endif + + +extension Data { + /// Returns cryptographically secure random data. + /// Not safe. Needs to be replaced. See https://forums.swift.org/t/random-data-uint8-random-or-secrandomcopybytes/56165 + /// + /// - Parameter length: Length of the data in bytes. + /// - Returns: Generated data of the specified length. + public static func random(length: Int) throws -> Data { + return Data((0 ..< length).map { _ in UInt8.random(in: UInt8.min ... UInt8.max) }) + } +} diff --git a/Sources/web3swift/Browser/Bridge.swift b/Sources/web3swift/Browser/Bridge.swift index 597277003..7a56afe41 100644 --- a/Sources/web3swift/Browser/Bridge.swift +++ b/Sources/web3swift/Browser/Bridge.swift @@ -6,6 +6,7 @@ // Copyright © 2017 Samaritan. All rights reserved. // +#if !os(Linux) import WebKit /// Bridge for WKWebView and JavaScript @@ -248,3 +249,4 @@ fileprivate extension WKWebView { evaluateJavaScript(jsString, completionHandler: completionHandler) } } +#endif diff --git a/Sources/web3swift/Utils/EIP/EIP67Code.swift b/Sources/web3swift/Utils/EIP/EIP67Code.swift index 7cbfd0af3..1dcf4b2ae 100755 --- a/Sources/web3swift/Utils/EIP/EIP67Code.swift +++ b/Sources/web3swift/Utils/EIP/EIP67Code.swift @@ -4,7 +4,9 @@ // import Foundation +#if !os(Linux) import CoreImage +#endif import BigInt import Core @@ -77,11 +79,14 @@ extension Web3 { return mainPart } + #if !os(Linux) public func toImage(scale: Double = 1.0) -> CIImage { return EIP67CodeGenerator.createImage(from: self, scale: scale) } + #endif } + #if !os(Linux) public struct EIP67CodeGenerator { public static func createImage(from: EIP67Code, scale: Double = 1.0) -> CIImage { @@ -94,6 +99,7 @@ extension Web3 { return image } } + #endif public struct EIP67CodeParser { public static func parse(_ data: Data) -> EIP67Code? { diff --git a/Sources/web3swift/Web3/Web3+HttpProvider.swift b/Sources/web3swift/Web3/Web3+HttpProvider.swift index b3db61b0f..9418c37b3 100755 --- a/Sources/web3swift/Web3/Web3+HttpProvider.swift +++ b/Sources/web3swift/Web3/Web3+HttpProvider.swift @@ -3,6 +3,9 @@ // Copyright © 2018 Alex Vlasov. All rights reserved. // +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif import Foundation import BigInt import Core diff --git a/Tests/web3swiftTests/localTests/EIP67Tests.swift b/Tests/web3swiftTests/localTests/EIP67Tests.swift index 420919286..a7c94791a 100755 --- a/Tests/web3swiftTests/localTests/EIP67Tests.swift +++ b/Tests/web3swiftTests/localTests/EIP67Tests.swift @@ -21,6 +21,7 @@ class EIP67Tests: LocalTestCase { print(encoding) } + #if !os(Linux) func testEIP67codeGeneration() throws { var eip67Data = Web3.EIP67Code.init(address: EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")!) eip67Data.gasLimit = BigUInt(21000) @@ -29,6 +30,7 @@ class EIP67Tests: LocalTestCase { let encoding = eip67Data.toImage(scale: 5.0) XCTAssert(encoding != CIImage()) } + #endif func testEIP67decoding() throws { var eip67Data = Web3.EIP67Code.init(address: EthereumAddress("0xe22b8979739D724343bd002F9f432F5990879901")!) diff --git a/Tests/web3swiftTests/localTests/LocalTestCase.swift b/Tests/web3swiftTests/localTests/LocalTestCase.swift index 38b9198e1..6eb3891b6 100644 --- a/Tests/web3swiftTests/localTests/LocalTestCase.swift +++ b/Tests/web3swiftTests/localTests/LocalTestCase.swift @@ -8,31 +8,39 @@ import web3swift // SuperClass that all local tests should be derived from // while this class does show up in the navigator, it has no associated tests class LocalTestCase: XCTestCase { + + enum TestError: Error { + case testError + } static let url = URL(string: "http://127.0.0.1:8545")! override func setUp() async throws { - let web3 = try! await Web3.new(LocalTestCase.url) + let web3 = try await Web3.new(LocalTestCase.url) - let block = try! await web3.eth.blockNumber() + let block = try await web3.eth.blockNumber() if block >= 25 { return } print("\n ****** Preloading Ganache (\(25 - block) blocks) *****\n") - let allAddresses = try! await web3.eth.ownedAccounts() + let allAddresses = try await web3.eth.ownedAccounts() let sendToAddress = allAddresses[0] - let contract = web3.contract(Web3.Utils.coldWalletABI, at: sendToAddress, abiVersion: 2) - let value = Utilities.parseToBigUInt("1.0", units: .eth)! + guard let contract = web3.contract(Web3.Utils.coldWalletABI, at: sendToAddress, abiVersion: 2), + let value = Utilities.parseToBigUInt("1.0", units: .eth) else { + throw TestError.testError + } let from = allAddresses[0] - let writeTX = contract!.createWriteOperation("fallback")! + guard let writeTX = contract.createWriteOperation("fallback") else { + throw TestError.testError + } writeTX.transaction.from = from writeTX.transaction.value = value writeTX.transaction.gasLimitPolicy = .manual(78423) writeTX.transaction.gasPricePolicy = .manual(20000000000) for _ in block..<25 { - let _ = try! await writeTX.writeToChain(password: "") + let _ = try await writeTX.writeToChain(password: "") } } }