Skip to content

Commit

Permalink
Merge pull request #219 from franklinsch/omit-capability-checks
Browse files Browse the repository at this point in the history
Optimize runtime caller capability checks
  • Loading branch information
franklinsch authored May 20, 2018
2 parents 2ebf275 + 60d7726 commit d101c43
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 55 deletions.
48 changes: 0 additions & 48 deletions Sources/IRGen/Function/IULIACallerCapabilityChecks.swift

This file was deleted.

4 changes: 1 addition & 3 deletions Sources/IRGen/Function/IULIAFunction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,6 @@ struct IULIAFunctionBody {
}

func rendered() -> String {
// Dynamically check the caller has appropriate caller capabilities.
let callerCapabilityChecks = IULIACallerCapabilityChecks(callerCapabilities: callerCapabilities).rendered(functionContext: functionContext)
let body = renderBody(functionDeclaration.body, functionContext: functionContext)

// Assign a caller capaiblity binding to a local variable.
Expand All @@ -117,7 +115,7 @@ struct IULIAFunctionBody {
capabilityBindingDeclaration = ""
}

return "\(callerCapabilityChecks)\(capabilityBindingDeclaration)\(body)"
return "\(capabilityBindingDeclaration)\(body)"
}

func renderBody<S : RandomAccessCollection & RangeReplaceableCollection>(_ statements: S, functionContext: FunctionContext) -> String where S.Element == AST.Statement, S.Index == Int {
Expand Down
2 changes: 1 addition & 1 deletion Sources/IRGen/IULIAContract.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct IULIAContract {
let publicFunctions = functions.filter { $0.functionDeclaration.isPublic }

// Create a function selector, to determine which function is called in the Ethereum transaction.
let functionSelector = IULIAFunctionSelector(functions: publicFunctions)
let functionSelector = IULIAFunctionSelector(functions: publicFunctions, enclosingType: contractDeclaration.identifier.name, environment: environment)
let selectorCode = functionSelector.rendered().indented(by: 6)

// Generate code for each function in the structs.
Expand Down
52 changes: 49 additions & 3 deletions Sources/IRGen/Runtime/IULIAFunctionSelector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
//

import CryptoSwift
import AST

/// Runtime code in IULIA which determines which function to call based on the Ethereum's transaction payload.
struct IULIAFunctionSelector {
var functions: [IULIAFunction]
var enclosingType: RawTypeIdentifier
var environment: Environment

func rendered() -> String {
let cases = renderCases()
Expand All @@ -30,14 +33,18 @@ struct IULIAFunctionSelector {
return """
case \(functionHash) /* \(function.mangledSignature()) */ {
\(renderCaseBody(function: function))
\(renderCaseBody(function: function).indented(by: 2))
}
"""
}.joined()
}

func renderCaseBody(function: IULIAFunction) -> String {
// Dynamically check the caller has appropriate caller capabilities.
let callerCapabilities = function.callerCapabilities
let callerCapabilityChecks = IULIACallerCapabilityChecks(callerCapabilities: callerCapabilities).rendered(enclosingType: enclosingType, environment: environment)

let arguments = function.parameterCanonicalTypes.enumerated().map { arg -> String in
let (index, type) = arg
switch type {
Expand All @@ -50,10 +57,49 @@ struct IULIAFunctionSelector {

if let resultType = function.resultCanonicalType {
switch resultType {
case .address, .uint256, .bytes32: return IULIARuntimeFunction.return32Bytes(value: call)
case .address, .uint256, .bytes32: return callerCapabilityChecks + "\n" + IULIARuntimeFunction.return32Bytes(value: call)
}
}

return call
return callerCapabilityChecks + "\n" + call
}
}

/// Checks whether the caller of a function has appropriate caller capabilities.
struct IULIACallerCapabilityChecks {
var callerCapabilities: [CallerCapability]

func rendered(enclosingType: RawTypeIdentifier, environment: Environment) -> String {
let checks = callerCapabilities.compactMap { callerCapability -> String? in
guard !callerCapability.isAny else { return nil }

let type = environment.type(of: callerCapability.identifier.name, enclosingType: enclosingType)
let offset = environment.propertyOffset(for: callerCapability.name, enclosingType: enclosingType)!

switch type {
case .fixedSizeArrayType(_, let size):
return (0..<size).map { index in
let check = IULIARuntimeFunction.isValidCallerCapability(address: "sload(add(\(offset), \(index)))")
return "_flintCallerCheck := add(_flintCallerCheck, \(check)"
}.joined(separator: "\n")
case .arrayType(_):
let check = IULIARuntimeFunction.isCallerCapabilityInArray(arrayOffset: offset)
return "_flintCallerCheck := add(_flintCallerCheck, \(check))"
default:
let check = IULIARuntimeFunction.isValidCallerCapability(address: "sload(\(offset)))")
return "_flintCallerCheck := add(_flintCallerCheck, \(check)"
}
}

if !checks.isEmpty {
return """
let _flintCallerCheck := 0
\(checks.joined(separator: "\n"))
if eq(_flintCallerCheck, 0) { revert(0, 0) }
""" + "\n"
}

return ""
}
}

0 comments on commit d101c43

Please sign in to comment.