Skip to content
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

Cleaning up ABI encoding to correctly encode function calls as tuples #36

Merged
merged 2 commits into from
Sep 14, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
108 changes: 101 additions & 7 deletions EtherKit/ABI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public indirect enum ABIType: CustomStringConvertible {
case address(value: Address)
case bytes(count: UnformattedDataMode, value: Data)
case array(count: UnformattedDataMode, type: ABIType, value: [ABIType])
case functionSelector(name: String, parameterTypes: [ABIType], contract: Address?)
case tuple(value: [ABIType])
case function(name: String, parameters: [ABIType], contract: Address?)

public var description: String {
switch self {
Expand Down Expand Up @@ -43,11 +44,13 @@ public indirect enum ABIType: CustomStringConvertible {
case let .constrained(count):
return String(describing: type) + "[\(count)]"
}
case let .functionSelector(name, parameterTypes, _):
let parameterString = parameterTypes
case let .function(name, parameters, _):
let parameterString = parameters
.compactMap { String(describing: $0) }
.joined(separator: ",")
return "\(name)(\(parameterString))"
case .tuple:
return "tuple[]"
}
}

Expand Down Expand Up @@ -77,7 +80,14 @@ public indirect enum ABIType: CustomStringConvertible {
case .constrained:
return type.isDynamic
}
case .functionSelector:
case .function:
return false
case let .tuple(value):
for subValue in value {
if subValue.isDynamic {
return true
}
}
return false
}
}
Expand Down Expand Up @@ -174,7 +184,7 @@ public indirect enum ABIType: CustomStringConvertible {
data.append(elemData)
}
}
case let .functionSelector(_, _, contract):
case let .function(_, _, contract):
let funcSig = String(describing: self)

if let contract = contract {
Expand All @@ -185,6 +195,42 @@ public indirect enum ABIType: CustomStringConvertible {
}
let fullHash = asciiBytes.sha3(.keccak256)
data.append(fullHash[0 ..< 4])
case let .tuple(value):
var headDatas: [Data] = []
var valueDatas: [Data] = []
var prefixLength = 0

for tupleElem in value {
let elemData = tupleElem.encode()

if tupleElem.isDynamic {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: probably better as a guard

// placeholder to be filled in later
let placeholder = BigUInt(1).abiType.encode()
headDatas.append(placeholder)
valueDatas.append(elemData)
prefixLength = prefixLength + placeholder.count
} else {
headDatas.append(elemData)
valueDatas.append(Data())
prefixLength = prefixLength + elemData.count
}
}

for i in 0 ... (value.count - 1) {
let tupleElem = value[i]

if tupleElem.isDynamic {
headDatas[i] = BigUInt(prefixLength).abiType.encode()
prefixLength = prefixLength + valueDatas[i].count
}
}

for headData in headDatas {
data.append(headData)
pr0zac marked this conversation as resolved.
Show resolved Hide resolved
}
for valueData in valueDatas {
data.append(valueData)
pr0zac marked this conversation as resolved.
Show resolved Hide resolved
}
}

return data
Expand Down Expand Up @@ -223,12 +269,60 @@ extension Int: ABIValueType {
}
}

extension Int8: ABIValueType {
public var abiType: ABIType {
return .int(size: 8, value: BigInt(self))
}
}

extension Int16: ABIValueType {
public var abiType: ABIType {
return .int(size: 16, value: BigInt(self))
}
}

extension Int32: ABIValueType {
public var abiType: ABIType {
return .int(size: 32, value: BigInt(self))
}
}

extension Int64: ABIValueType {
public var abiType: ABIType {
return .int(size: 64, value: BigInt(self))
}
}

extension UInt: ABIValueType {
public var abiType: ABIType {
return .uint(size: 64, value: BigUInt(self))
}
}

extension UInt8: ABIValueType {
public var abiType: ABIType {
return .uint(size: 8, value: BigUInt(self))
}
}

extension UInt16: ABIValueType {
public var abiType: ABIType {
return .uint(size: 16, value: BigUInt(self))
}
}

extension UInt32: ABIValueType {
public var abiType: ABIType {
return .uint(size: 32, value: BigUInt(self))
}
}

extension UInt64: ABIValueType {
public var abiType: ABIType {
return .uint(size: 64, value: BigUInt(self))
}
}

extension Address: ABIValueType {
public var abiType: ABIType {
return .address(value: self)
Expand Down Expand Up @@ -264,12 +358,12 @@ extension Array: ABIValueType where Element: ABIValueType {
}
}

extension FunctionSelector: ABIValueType {
extension Function: ABIValueType {
public var isDynamic: Bool {
return false
}

public var abiType: ABIType {
return .functionSelector(name: name, parameterTypes: parameterTypes, contract: contract)
return .function(name: name, parameters: parameters, contract: contract)
}
}
34 changes: 12 additions & 22 deletions EtherKit/Models/Function.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,29 @@
import BigInt
import Foundation

public struct FunctionSelector {
public struct Function {
public var name: String
public var parameterTypes: [ABIType]
public var parameters: [ABIType]
public var contract: Address?

public init(name: String, parameterTypes: [ABIType], contract: Address? = nil) {
public init(name: String, parameters: [ABIType], contract: Address? = nil) {
self.name = name
self.parameterTypes = parameterTypes
self.contract = contract
}
}

public struct Function {
public var functionSelector: FunctionSelector
public var parameters: [ABIValueType]

public init(functionSelector: FunctionSelector, parameters: [ABIValueType]) {
self.functionSelector = functionSelector
self.parameters = parameters
self.contract = contract
}

public init(name: String, parameters: [ABIValueType]) {
let parameterTypes: [ABIType] = parameters.compactMap { $0.abiType }
functionSelector = FunctionSelector(name: name, parameterTypes: parameterTypes)
self.parameters = parameters
public init(name: String, parameters: [ABIValueType], contract: Address? = nil) {
self.name = name
self.parameters = parameters.compactMap { $0.abiType }
self.contract = contract
}

public func encodeToCall() -> Data {
var data = Data()
data.append(functionSelector.abiType.encode())
for parameter in parameters {
data.append(parameter.abiType.encode())
}
data.append(abiType.encode())

var paramTuple = ABIType.tuple(value: parameters)
data.append(paramTuple.encode())
return data
}
}
2 changes: 2 additions & 0 deletions Example/EtherKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@
A16A63C6B34BCC2C66991033 /* Pods */,
374062D57F115FAEB32E6109 /* Frameworks */,
);
indentWidth = 2;
sourceTree = "<group>";
tabWidth = 2;
};
607FACD11AFB9204008FA782 /* Products */ = {
isa = PBXGroup;
Expand Down
12 changes: 6 additions & 6 deletions Example/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ PODS:
- BigInt (3.1.0):
- SipHash (~> 1.2)
- CryptoSwift (0.10.0)
- EtherKit (0.1.5):
- EtherKit/Core (= 0.1.5)
- EtherKit/PromiseKit (= 0.1.5)
- EtherKit/Core (0.1.5):
- EtherKit (0.2.0-beta1):
- EtherKit/Core (= 0.2.0-beta1)
- EtherKit/PromiseKit (= 0.2.0-beta1)
- EtherKit/Core (0.2.0-beta1):
- BigInt
- CryptoSwift
- Marshal
- Result (~> 4.0.0)
- secp256k1.swift
- Starscream
- EtherKit/PromiseKit (0.1.5):
- EtherKit/PromiseKit (0.2.0-beta1):
- EtherKit/Core
- PromiseKit/CorePromise
- Marshal (1.2.4)
Expand Down Expand Up @@ -46,7 +46,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
BigInt: 76b5dfdfa3e2e478d4ffdf161aeede5502e2742f
CryptoSwift: 6c778d69282bed3b4e975ff97a79d074f20bb011
EtherKit: f0ea0a27a5996b898261892c421a8284ebf15c29
EtherKit: 2e07c1d3f30f12cb31e721d92c747437ee2ea1aa
Marshal: 8e04e6624e506921db7143b0bfd83caee03f32d6
PromiseKit: cf84bbb1235a61473b326c5cf0b41f6828f87ba5
Result: 7645bb3f50c2ce726dd0ff2fa7b6f42bbe6c3713
Expand Down