From 91e9d54c718b01bff740a93539e36d3513977f9f Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 4 May 2020 18:15:55 -0500 Subject: [PATCH] [Sol - Yul] Add support for ripemd160 & ecrecover. --- .../codegen/ir/IRGeneratorForStatements.cpp | 71 ++++++++++++++++--- .../builtinFunctions/ripemd160_empty.sol | 2 + .../builtinFunctions/sha256_empty.sol | 2 + .../semanticTests/ecrecover/ecrecover.sol | 2 + .../ecrecover/ecrecover_abiV2.sol | 2 + .../failing_ecrecover_invalid_input.sol | 2 + .../failing_ecrecover_invalid_input_asm.sol | 2 + ...failing_ecrecover_invalid_input_proper.sol | 2 + 8 files changed, 77 insertions(+), 8 deletions(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 553f618cd..528523227 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -952,14 +952,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) "))\n"; break; } - case FunctionType::Kind::ECRecover: - case FunctionType::Kind::SHA256: - case FunctionType::Kind::RIPEMD160: - { - solAssert(!_functionCall.annotation().tryCall, ""); - appendExternalFunctionCall(_functionCall, arguments); - break; - } case FunctionType::Kind::ArrayPop: { auto const& memberAccessExpression = dynamic_cast(_functionCall.expression()).expression(); @@ -1153,6 +1145,69 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) break; } + case FunctionType::Kind::ECRecover: + case FunctionType::Kind::RIPEMD160: + case FunctionType::Kind::SHA256: + { + solAssert(!_functionCall.annotation().tryCall, ""); + solAssert(!functionType->valueSet(), ""); + solAssert(!functionType->gasSet(), ""); + solAssert(!functionType->bound(), ""); + + static map> precompiles = { + {FunctionType::Kind::ECRecover, std::make_tuple(1, 0)}, + {FunctionType::Kind::SHA256, std::make_tuple(2, 0)}, + {FunctionType::Kind::RIPEMD160, std::make_tuple(3, 12)}, + }; + auto [ address, offset ] = precompiles[functionType->kind()]; + TypePointers argumentTypes; + vector argumentStrings; + for (auto const& arg: arguments) + { + argumentTypes.emplace_back(&type(*arg)); + argumentStrings += IRVariable(*arg).stackSlots(); + } + Whiskers templ(R"( + let := () + let := ( ) + + mstore(0, 0) + + let := (,
, 0, , sub(, ), 0, 32) + if iszero() { () } + let := (mload(0)) + )"); + templ("call", m_context.evmVersion().hasStaticCall() ? "staticcall" : "call"); + templ("isCall", !m_context.evmVersion().hasStaticCall()); + templ("shl", m_utils.shiftLeftFunction(offset * 8)); + templ("allocateTemporary", m_utils.allocationTemporaryMemoryFunction()); + templ("pos", m_context.newYulVariable()); + templ("end", m_context.newYulVariable()); + templ("isECRecover", FunctionType::Kind::ECRecover == functionType->kind()); + if (FunctionType::Kind::ECRecover == functionType->kind()) + templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes)); + else + templ("encodeArgs", m_context.abiFunctions().tupleEncoderPacked(argumentTypes, parameterTypes)); + templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); + templ("address", toString(address)); + templ("success", m_context.newYulVariable()); + templ("retVars", IRVariable(_functionCall).commaSeparatedList()); + templ("forwardingRevert", m_utils.forwardingRevertFunction()); + if (m_context.evmVersion().canOverchargeGasForCall()) + // Send all gas (requires tangerine whistle EVM) + templ("gas", "gas()"); + else + { + // @todo The value 10 is not exact and this could be fine-tuned, + // but this has worked for years in the old code generator. + u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10 + evmasm::GasCosts::callNewAccountGas; + templ("gas", "sub(gas(), " + formatNumber(gasNeededByCaller) + ")"); + } + + m_code << templ.render(); + + break; + } default: solUnimplemented("FunctionKind " + toString(static_cast(functionType->kind())) + " not yet implemented"); } diff --git a/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol index c79625d3c..ea0fda845 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/ripemd160_empty.sol @@ -4,5 +4,7 @@ contract C { } } +// ==== +// compileViaYul: also // ---- // f() -> 0x9c1185a5c5e9fc54612808977ee8f548b2258d31000000000000000000000000 diff --git a/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol b/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol index bb9fc3616..ededa2fab 100644 --- a/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol +++ b/test/libsolidity/semanticTests/builtinFunctions/sha256_empty.sol @@ -3,5 +3,7 @@ contract C { return sha256(""); } } +// ==== +// compileViaYul: also // ---- // f() -> 0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 diff --git a/test/libsolidity/semanticTests/ecrecover/ecrecover.sol b/test/libsolidity/semanticTests/ecrecover/ecrecover.sol index 1beb451de..1f462752b 100644 --- a/test/libsolidity/semanticTests/ecrecover/ecrecover.sol +++ b/test/libsolidity/semanticTests/ecrecover/ecrecover.sol @@ -3,6 +3,8 @@ contract test { return ecrecover(h, v, r, s); } } +// ==== +// compileViaYul: also // ---- // a(bytes32,uint8,bytes32,bytes32): // 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c, diff --git a/test/libsolidity/semanticTests/ecrecover/ecrecover_abiV2.sol b/test/libsolidity/semanticTests/ecrecover/ecrecover_abiV2.sol index fa3adbff8..158a85a5e 100644 --- a/test/libsolidity/semanticTests/ecrecover/ecrecover_abiV2.sol +++ b/test/libsolidity/semanticTests/ecrecover/ecrecover_abiV2.sol @@ -4,6 +4,8 @@ contract test { return ecrecover(h, v, r, s); } } +// ==== +// compileViaYul: also // ---- // a(bytes32,uint8,bytes32,bytes32): // 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c, diff --git a/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input.sol b/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input.sol index fe0cbf412..6560baf6a 100644 --- a/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input.sol +++ b/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input.sol @@ -6,5 +6,7 @@ contract C { return ecrecover(bytes32(uint(-1)), 1, bytes32(uint(2)), bytes32(uint(3))); } } +// ==== +// compileViaYul: also // ---- // f() -> 0 diff --git a/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input_asm.sol b/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input_asm.sol index 5e8c3cdbf..607f3ec1a 100644 --- a/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input_asm.sol +++ b/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input_asm.sol @@ -11,5 +11,7 @@ contract C { ); } } +// ==== +// compileViaYul: also // ---- // f() -> 0 diff --git a/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input_proper.sol b/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input_proper.sol index 511bd4e41..4fa1ec514 100644 --- a/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input_proper.sol +++ b/test/libsolidity/semanticTests/ecrecover/failing_ecrecover_invalid_input_proper.sol @@ -16,5 +16,7 @@ contract C { return ecrecover(hash, v, r, s); } } +// ==== +// compileViaYul: also // ---- // f() -> 0