Skip to content

Commit

Permalink
Small fixes. (#11)
Browse files Browse the repository at this point in the history
- Eliminates warnings in the generated code by using special handling for methods that would result in a `Void` return.
- Fix crash when address does respond to a certain function (e.g. if address is not a HumanStandardToken decoding the`0x` return data for `Name` function would crash).
- Revert back to official CryptoSwift.
- Improve testing guide.
- Add `unwrap()` to some types to allow for getting the wrapped value out of the solidity wrapper.
  • Loading branch information
zweigraf authored Nov 8, 2017
1 parent 357dec2 commit 339701f
Show file tree
Hide file tree
Showing 22 changed files with 99 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,8 @@ fastlane/test_output
# Xcodeproj generated with `swift package generate-xcodeproj`.
# Can be safely ignored and recreated, as it's only used for development.
Bivrost.xcodeproj

# Local test data should not be committed.
testcontracts
testoutput

2 changes: 1 addition & 1 deletion .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,4 @@ opt_in_rules: # some rules are only opt-in
# Make certain rules give out an error
force_cast: error
force_try: error
force_unwrapping: error
force_unwrapping: warning
10 changes: 5 additions & 5 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
},
{
"package": "CryptoSwift",
"repositoryURL": "https://github.com/zweigraf/CryptoSwift.git",
"repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift",
"state": {
"branch": null,
"revision": "fdd115318528c9c25a06c0a89553f71bde7233da",
"version": null
"revision": "d0084e4a6fe9490b3baab3d3c2aad58d3a852daf",
"version": "0.8.0"
}
},
{
Expand All @@ -51,8 +51,8 @@
"repositoryURL": "https://github.com/Quick/Quick.git",
"state": {
"branch": null,
"revision": "c498edf4aabb694a5b8a861ec3d69f0c5ab57d9e",
"version": null
"revision": "0ff81f2c665b4381f526bd656f8708dd52a9ea2f",
"version": "1.2.0"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/krzyzanowskim/CryptoSwift", .upToNextMinor(from: "0.8.0")),
.package(url: "https://github.com/attaswift/BigInt.git", .upToNextMajor(from: "3.0.0")),
.package(url: "https://github.com/Quick/Nimble.git", .upToNextMajor(from: "7.0.2")),
.package(url: "https://github.com/Quick/Quick.git", .revision("c498edf4aabb694a5b8a861ec3d69f0c5ab57d9e")),
.package(url: "https://github.com/Quick/Nimble.git", .upToNextMinor(from: "7.0.2")),
.package(url: "https://github.com/Quick/Quick.git", .upToNextMinor(from: "1.2.0")),
.package(url: "https://github.com/kylef/Commander.git", .revision("e0cbee1bd73778c1076c675eaf660e97d09f3b32")),
// PathKit fork supporting SPM4
.package(url: "https://github.com/PoissonBallon/PathKit.git", .branch("master")),
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

🔥 🌈 Bridge between Solidity Contracts and Swift

[![CI Status](http://img.shields.io/travis/gnosis/bivrost-swift.svg?style=flat)](https://travis-ci.org/gnosis/bivrost-swift)

Bivrost is in very early development. Do not use this for anything important.

## Description
Expand Down Expand Up @@ -116,4 +114,8 @@ Bivrost is available under the MIT license. See the LICENSE file for more info.

## Issues

Tests currently do not work when generating an Xcode project via `swift package generate-xcodeproj`. This issue is described at the bottom of <https://github.com/Quick/Quick/issues/707>. Drop to the CMD and use `swift test` for testing.
Tests currently do not work when generating an Xcode project via `swift package generate-xcodeproj`. This issue is described in <https://github.com/Quick/Quick/issues/751>.

Workarounds:
- Drop to the CMD and use `swift test` for testing.
- Go to the `QuickSpecBase` target build settings in the generated Xcode project. Set `Enables Module` to `YES` (`CLANG_ENABLE_MODULES=YES`). Now testing should work.
2 changes: 2 additions & 0 deletions Resources/BivrostError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import Foundation

enum BivrostError: Error {
enum Decoder: Error {
case endOfSourceData

// Decoding
case invalidBool(hex: String)
case invalidUInt(hex: String)
Expand Down
11 changes: 7 additions & 4 deletions Resources/Coding/BaseDecoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,14 @@ struct BaseDecoder {
}

static func decodeBytes(source: PartitionData) throws -> Data {
let sizePart = source.consume()
let sizePart = try source.consume()
guard let size = Int(sizePart, radix: 16) else {
throw BivrostError.Decoder.invalidBytesLength(hex: sizePart)
}

var byteHolder = Data()
while byteHolder.count < size {
if let data = Data(fromHexEncodedString: source.consume()) {
if let data = Data(fromHexEncodedString: try source.consume()) {
byteHolder.append(data)
}
}
Expand Down Expand Up @@ -107,7 +107,7 @@ struct BaseDecoder {
}
// We have dynamic types, we need to check the dynamic array
// Consume all locations to jump cursor ahead to dynamic section
(0..<capacity).forEach { _ in _ = source.consume() }
try (0..<capacity).forEach { _ in _ = try source.consume() }
return try (0..<capacity).map { _ in try decoder(source) }
}
}
Expand All @@ -126,7 +126,10 @@ extension BaseDecoder {

var index: Int = 0

func consume() -> String {
func consume() throws -> String {
guard index < lines.count else {
throw BivrostError.Decoder.endOfSourceData
}
let returnValue = lines[index]
index += 1
return returnValue
Expand Down
10 changes: 10 additions & 0 deletions Resources/Extensions/StringExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,13 @@ extension String {
return forBytes * 2
}
}

// MARK: - Hex Helper
extension String {
/// Returns a new string, removing a '0x' prefix if present.
var withoutHexPrefix: String {
return hasPrefix("0x")
? String(dropFirst(2))
: self
}
}
4 changes: 1 addition & 3 deletions Resources/Types/Address.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ public extension Solidity {
private let bigInt: BigUInt

init(_ address: Swift.String) throws {
let hex = address.hasPrefix("0x")
? Swift.String(address[address.index(address.startIndex, offsetBy: 2)...])
: address
let hex = address.withoutHexPrefix
guard let bigInt = BigUInt(hex, radix: 16) else {
throw BivrostError.Address.invalidAddress(hex)
}
Expand Down
2 changes: 1 addition & 1 deletion Resources/Types/ArrayX.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
extension _DoNotUse {
// swiftlint:disable:next type_name
class _ArrayX<T: SolidityCodable & Equatable> {
private let items: [T]
let items: [T]
class var length: UInt {
fatalError("Not meant to be called directly.")
}
Expand Down
6 changes: 6 additions & 0 deletions Resources/Types/Bool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,20 @@ import BigInt

public extension Solidity {
struct Bool {
private let value: Swift.Bool
private let wrapper: UInt8

init(_ value: Swift.Bool) {
self.value = value
guard let wrapper = try? UInt8(BigUInt(value ? 1 : 0)) else {
fatalError("Solidity.Bool could not be created with value of \(value). This should not happen.")
}
self.wrapper = wrapper
}

func unwrap() -> Swift.Bool {
return value
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions Resources/Types/Bytes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ public extension Solidity {
self.value = value
self.length = length
}

func unwrap() -> Data {
return value
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions Resources/Types/BytesX.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ public extension _DoNotUse {
}
self.value = value
}

func unwrap() -> Data {
return value
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Resources/Types/Function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ extension Solidity.Function: StaticType {
}

static func decode(source: BaseDecoder.PartitionData) throws -> Solidity.Function {
let line = source.consume()
let line = try source.consume()
// 20 bytes / 40 chars for Address as UInt160
let addressHex = String(line[line.startIndex ..< line.index(startDistance: 40)])
let uint = try BaseDecoder.decodeUInt(data: addressHex)
Expand Down
4 changes: 4 additions & 0 deletions Resources/Types/IntX.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public extension _DoNotUse {
}
value = int
}

func unwrap() -> BigInt {
return value
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions Resources/Types/String.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,21 @@
public extension Solidity {
struct String {
let wrapper: Solidity.Bytes
let value: Swift.String

init?(_ value: Swift.String) {
self.value = value

guard let data = value.data(using: .utf8),
let bytes = Solidity.Bytes(data) else {
return nil
}
self.wrapper = bytes
}

func unwrap() -> Swift.String {
return value
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions Resources/Types/UIntX.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public extension _DoNotUse {
}
value = uint
}

func unwrap() -> BigUInt {
return value
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion Resources/Types/VariableArray.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extension Solidity {
// MARK: - DynamicType
extension Solidity.VariableArray: DynamicType {
static func decode(source: BaseDecoder.PartitionData) throws -> Solidity.VariableArray<T> {
let sizePart = source.consume()
let sizePart = try source.consume()
guard let size = UInt(sizePart, radix: 16) else {
throw BivrostError.Decoder.invalidArrayLength(hex: sizePart)
}
Expand Down
8 changes: 7 additions & 1 deletion Sources/BivrostKit/Generating/ContractTemplateModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ struct ContractTemplateModel {

let decodeReturnReturnValue: String // Name to be returned at the end of decode(returnData:)
let decodeReturnTypes: [DecodeType]
let hasEmptyDecodeReturnFunction: Bool

let decodeArgumentsReturnValue: String // Name to be returned at the end of decode(argumentsData:)
let decodeArgumentsTypes: [DecodeType]
let hasEmptyDecodeArgumentsFunction: Bool

struct DecodeType {
let name: String
Expand Down Expand Up @@ -61,12 +63,16 @@ struct ContractTemplateModel {
let outputString = tupleString(for: object.outputs)

let encodeArgumentsString = encodeArguments(for: object.inputs)

let decodeReturnTypesArray = decodeTypes(for: object.outputs)
let hasEmptyDecodeReturnFunction = decodeReturnTypesArray.isEmpty
let decodeReturnReturnValueString = decodeReturnReturnValue(for: decodeReturnTypesArray)

let decodeArgumentsTypesArray = decodeTypes(for: object.inputs)
let hasEmptyDecodeArgumentsFunction = decodeArgumentsTypesArray.isEmpty
let decodeArgumentsReturnValueString = decodeArgumentsReturnValue(for: decodeArgumentsTypesArray)

return ContractTemplateModel.Function(name: preparedFunctionName, methodId: functionMethodId, input: inputString, output: outputString, encodeArguments: encodeArgumentsString, decodeReturnReturnValue: decodeReturnReturnValueString, decodeReturnTypes: decodeReturnTypesArray, decodeArgumentsReturnValue: decodeArgumentsReturnValueString, decodeArgumentsTypes: decodeArgumentsTypesArray)
return ContractTemplateModel.Function(name: preparedFunctionName, methodId: functionMethodId, input: inputString, output: outputString, encodeArguments: encodeArgumentsString, decodeReturnReturnValue: decodeReturnReturnValueString, decodeReturnTypes: decodeReturnTypesArray, hasEmptyDecodeReturnFunction: hasEmptyDecodeReturnFunction, decodeArgumentsReturnValue: decodeArgumentsReturnValueString, decodeArgumentsTypes: decodeArgumentsTypesArray, hasEmptyDecodeArgumentsFunction: hasEmptyDecodeArgumentsFunction)
}
}
}
Expand Down
31 changes: 20 additions & 11 deletions Sources/BivrostKit/Generating/Templates/ContractTemplate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,54 +17,63 @@ extension Templates {
struct {{ contract.name }} {
{% for function in contract.functions %}
struct {{ function.name }}: SolidityFunction {
static let methodId = "{{ function.methodId }}"
typealias Return = {{ function.output }}
typealias Arguments = {{ function.input }}
static func encodeCall(arguments: Arguments) -> String {
return "0x\\(methodId)\\(BaseEncoder.encode(arguments: {{ function.encodeArguments }}))"
}
{% if function.hasEmptyDecodeReturnFunction %}
static func decode(returnData: String) throws -> Return {}
{% else %}
static func decode(returnData: String) throws -> Return {
let source = BaseDecoder.partition(returnData)
// Static Types & Location
// Decode Static Types & Locations for Dynamic Types
{% for decodedType in function.decodeReturnTypes %}
{% if decodedType.isDynamic %}
// Ignore location for dynamic type
_ = source.consume()
// Ignore location for {{ decodedType.name }} (dynamic type)
_ = try source.consume()
{% else %}
let {{ decodedType.name }} = try {{ decodedType.type }}.decode(source: source)
{% endif %}
{% endfor %}
// Dynamic Types
// Dynamic Types (if any)
{% for decodedType in function.decodeReturnTypes %}
{% if decodedType.isDynamic %}
let {{ decodedType.name }} = try {{ decodedType.type }}.decode(source: source)
{% endif %}
{% endfor %}
return {{ function.decodeReturnReturnValue }}
}
{% endif %}
{% if function.hasEmptyDecodeArgumentsFunction %}
static func decode(argumentsData: String) throws -> Arguments {}
{% else %}
static func decode(argumentsData: String) throws -> Arguments {
let source = BaseDecoder.partition(argumentsData)
// Static Types & Location
// Decode Static Types & Locations for Dynamic Types
{% for decodedType in function.decodeArgumentsTypes %}
{% if decodedType.isDynamic %}
// Ignore location for dynamic type
_ = source.consume()
// Ignore location for {{ decodedType.name }} (dynamic type)
_ = try source.consume()
{% else %}
let {{ decodedType.name }} = try {{ decodedType.type }}.decode(source: source)
{% endif %}
{% endfor %}
// Dynamic Types
// Dynamic Types (if any)
{% for decodedType in function.decodeArgumentsTypes %}
{% if decodedType.isDynamic %}
let {{ decodedType.name }} = try {{ decodedType.type }}.decode(source: source)
{% endif %}
{% endfor %}
return {{ function.decodeArgumentsReturnValue }}
}
{% endif %}
}
{% endfor %}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/BivrostKit/Parsing/ContractParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct ContractParser {
/// - Throws: Throws if the json was malformed, e.g. a required field was missing.
static func parseContract(from json: [String: Any]) throws -> Contract {
guard let name = json[.contractName] as? String else {
throw ParsingError.contractNameInvalid
throw ParsingError.contractNameInvalid(json: json)
}
guard let elementsJson = json[.abi] as? [[String: Any]] else {
throw ParsingError.contractAbiInvalid
Expand Down
2 changes: 1 addition & 1 deletion Sources/BivrostKit/Parsing/ParsingError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ enum ParsingError: Error {
case eventInputInvalid
case parameterTypeInvalid
case parameterTypeNotFound
case contractNameInvalid
case contractNameInvalid(json: [String: Any])
case contractAbiInvalid
}

0 comments on commit 339701f

Please sign in to comment.