Skip to content

Commit

Permalink
WIP: CallParams part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
Zachery Gyurkovitz committed Nov 10, 2019
1 parent 33997b8 commit 257d728
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Examples/SimpleTransferCrypto/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ public func clientFromEnvironment() -> Client {
let client = clientFromEnvironment()
.setMaxTransactionFee(100_000_000)

try! client.transferCryptoTo(recipient: AccountId("0.0.2")!, amount: 10000)
_ = try! client.transferCryptoTo(recipient: AccountId("0.0.2")!, amount: 10000)

print("Crypto transferred successfully")
9 changes: 9 additions & 0 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@
"version": "0.8.0"
}
},
{
"package": "CryptoSwift",
"repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift",
"state": {
"branch": null,
"revision": "90e5b7af823d869fa8dea5a3abc7d95b6cb04c8c",
"version": "1.1.3"
}
},
{
"package": "SwiftGRPC",
"repositoryURL": "https://github.com/grpc/grpc-swift",
Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ let package = Package(
.package(url: "https://github.com/jedisct1/swift-sodium", .exact("0.8.0")),
.package(url: "https://github.com/apple/swift-protobuf", .exact("1.6.0")),
.package(url: "https://github.com/grpc/grpc-swift", .exact("0.9.1")),
.package(url: "https://github.com/krzyzanowskim/CryptoSwift", .exact("1.1.3"))
],
targets: [
.target(
name: "Hedera",
dependencies: ["Sodium", "SwiftProtobuf", "SwiftGRPC"]),
dependencies: ["Sodium", "SwiftProtobuf", "SwiftGRPC", "CryptoSwift"]),
.testTarget(
name: "HederaTests",
dependencies: ["Hedera"]),
Expand Down
140 changes: 140 additions & 0 deletions Sources/Hedera/CallParams.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import CryptoSwift

public final class CallParams {
private let functionSelector: FunctionSelector?
private var args: [Argument]

public init(funcSelector: FunctionSelector?) {
self.functionSelector = funcSelector
args = []
}

private func addParamType(paramType: String) {
_ = try! functionSelector?.addParamType(typeName: paramType)
}

public func addString(param: String) -> CallParams {
addParamType(paramType: "string")
args.append(Argument(value: encodeString(param)))

return self
}

public final class FunctionSelector {
private var state: SelectorState
private var needsComma: Bool

private enum SelectorState {
case unfinished([UInt8])
case finished([UInt8])
}

public init(_ funcName: String) {
needsComma = false
// 0x28 = '('
state = .unfinished(Array<UInt8>(funcName.utf8) + [0x28])
}

public func addParamType(typeName: String) throws -> FunctionSelector {
guard case var .unfinished(bytes) = state else {
throw HederaError(message: "Can't add type params to FunctionSelectors once they have finished")
}

bytes += (needsComma ? [0x23] : []) + Array<UInt8>(typeName.utf8)

state = .unfinished(bytes)
needsComma = true

return self
}

public func finishIntermediate() -> [UInt8] {
switch state {
case let .unfinished(bytes):
// 0x29 = ')'
return Digest.sha3(bytes + [0x29], variant: .keccak256)

case let .finished(hash):
return hash
}
}

public func finish() -> [UInt8] {
let hash = finishIntermediate()
state = .finished(hash)

return hash
}
}
}

fileprivate final class Argument {
private let value: [UInt8]
private let isDynamic: Bool

// this is a different init function so that it avoids the throws annotation
init(value: [UInt8]) {
self.value = value
self.isDynamic = true

}

init(value: [UInt8], isDynamic: Bool) throws {
guard isDynamic || value.count == 32 else {
throw HederaError(message: "invalid argument: value argument that was not 32 bytes")
}

self.value = value
self.isDynamic = isDynamic
}
}

private func encodeString(_ param: String) -> [UInt8] {
let strBytes = Array<UInt8>(param.utf8)

// prepend the size of the string.
return int256(val: Int64(strBytes.count), bitwidth: 32) + padRight(strBytes)
}

private func encodeBytes(bytes: [UInt8]) -> [UInt8] {
return int256(val: Int64(bytes.count), bitwidth: 32) + padRight(bytes)
}

private func encodeArray(elements: [[UInt8]], prependLen: Bool) -> [UInt8] {
if (prependLen) {
return int256(val: Int64(elements.count), bitwidth: 32) + elements.joined()
} else {
return Array(elements.joined())
}
}

private func int256(val: Int64, bitwidth: UInt8) -> [UInt8] {
// this uses int internally for convinence,
// but uses UInt8 as a param type to ensure
// that nothing tries to use a bitwidth too big.
let bytes = Int(min(bitwidth, 64) / 8)
let remainder = rem(bytes)
var output = Array<UInt8>(repeating: val < 0 ? 0xff : 0x00, count: bytes + remainder)

for i in stride(from: bytes - 1, through: 0, by: -1) {
output[remainder + i] = UInt8(val >> (i * 8))
}

return output
}

private func padLeft(_ bytes: [UInt8], signExtend: Bool = false) -> [UInt8] {
return Array<UInt8>(repeating: signExtend ? 0xff : 0x00, count: rem(bytes.count)) + bytes
}

private func padRight(_ bytes: [UInt8]) -> [UInt8] {
return bytes + Array<UInt8>(repeating: 0x00, count: rem(bytes.count))
}

private func rem(_ val: Int) -> Int {
guard val % 32 == 0 else {
return 0
}

return 32 - val % 32
}

0 comments on commit 257d728

Please sign in to comment.