From 604bc8a9c3179d8f394c7f44155ddb7e9293cbb1 Mon Sep 17 00:00:00 2001 From: Leonardo Amaral Date: Wed, 8 Jan 2025 09:44:15 -0300 Subject: [PATCH] ec recover precompile implemented --- src/contract/contracthost.cpp | 52 ++++++++++++++++++++++++++++++++++- src/contract/contracthost.h | 8 ++++++ src/utils/dynamicexception.h | 1 + 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/contract/contracthost.cpp b/src/contract/contracthost.cpp index 08e64dfb..3e59ce5a 100644 --- a/src/contract/contracthost.cpp +++ b/src/contract/contracthost.cpp @@ -14,6 +14,29 @@ static inline bool isCall(const evmc_message& msg) { return msg.kind == EVMC_CALL || msg.kind == EVMC_DELEGATECALL; } +static Address ecrecover(const Hash& hash, uint8_t v, const Hash& r, const Hash& s) { + if (v == 27) { + v = 0; + } else if (v == 28) { + v = 1; + } else { + return Address{}; + } + + Signature sig; + auto it = std::copy(r.begin(), r.end(), sig.begin()); + it = std::copy(s.begin(), s.end(), it); + *it = v; + + auto pubkey = Secp256k1::recover(sig, hash); + + if (!pubkey) { + return Address{}; + } + + return Secp256k1::toAddress(pubkey); +} + void ContractHost::transfer(const Address& from, const Address& to, const uint256_t& value) { // the from account **Must exist** on the unordered_map. // unordered_map references to values are valid **until** you insert a new element @@ -243,6 +266,11 @@ evmc::Result ContractHost::processBDKPrecompile(const evmc_message& msg) { * function getRandom() external view returns (uint256); * } */ + + if (msg.recipient == ECRECOVER_PRECOMPILE) { + return this->processEcRecoverPrecompile(msg); + } + try { this->leftoverGas_ = msg.gas; this->deduceGas(1000); // CPP contract call is 1000 gas @@ -261,6 +289,21 @@ evmc::Result ContractHost::processBDKPrecompile(const evmc_message& msg) { return evmc::Result(EVMC_PRECOMPILE_FAILURE, this->leftoverGas_, 0, nullptr, 0); } +evmc::Result ContractHost::processEcRecoverPrecompile(const evmc_message& msg) { + try { + this->deduceGas(3000); // The cost for a ecrecover call: https://www.evm.codes/precompiled + const auto [hash, v, r, s] = ABI::Decoder::decodeData(bytes::View(msg.input_data, msg.input_size)); + const Address result = ecrecover(hash, v, r, s); + const Bytes resultEncoded = ABI::Encoder::encodeData(result); + return evmc::Result{EVMC_SUCCESS, this->leftoverGas_, 0, resultEncoded.data(), resultEncoded.size()}; + } catch (const std::exception& e) { + this->evmcThrows_.emplace_back(e.what()); + this->evmcThrow_ = true; + } + + return evmc::Result(EVMC_PRECOMPILE_FAILURE, this->leftoverGas_, 0, nullptr, 0); +} + void ContractHost::execute(const evmc_message& msg, const ContractType& type) { const Address from(msg.sender); const Address to(msg.recipient); @@ -619,7 +662,14 @@ ContractType ContractHost::decodeContractCallType(const evmc_message& msg) const return ContractType::CREATE2; } default: - if (msg.recipient == BDK_PRECOMPILE) return ContractType::PRECOMPILED; + if (msg.recipient == BDK_PRECOMPILE) { + return ContractType::PRECOMPILED; + } + + if (msg.recipient == ECRECOVER_PRECOMPILE) { + return ContractType::PRECOMPILED; + } + Address recipient(msg.recipient); // we need to take a reference to the account, not a reference to the pointer const auto& recipientAccount = *accounts_[recipient]; diff --git a/src/contract/contracthost.h b/src/contract/contracthost.h index c2d74820..542783f0 100644 --- a/src/contract/contracthost.h +++ b/src/contract/contracthost.h @@ -44,6 +44,7 @@ See the LICENSE.txt file in the project root for more information. using namespace evmc::literals; const auto ZERO_ADDRESS = 0x0000000000000000000000000000000000000000_address; const auto BDK_PRECOMPILE = 0x1000000000000000000000000000100000000001_address; +const auto ECRECOVER_PRECOMPILE = 0x0000000000000000000000000000000000000001_address; /// Abstraction for a contract host. class ContractHost : public evmc::Host { @@ -152,6 +153,13 @@ class ContractHost : public evmc::Host { */ evmc::Result processBDKPrecompile(const evmc_message& msg); + /** + * Process a call to EC RECOVER precompile. + * @param msg The call message to process. + * @return The result of the call. + */ + evmc::Result processEcRecoverPrecompile(const evmc_message& msg); + /** * Process an EVM CREATE call. * @param msg The call message to process. diff --git a/src/utils/dynamicexception.h b/src/utils/dynamicexception.h index 9a29bf6d..7e58b626 100644 --- a/src/utils/dynamicexception.h +++ b/src/utils/dynamicexception.h @@ -9,6 +9,7 @@ See the LICENSE.txt file in the project root for more information. #define DYNAMIC_EXCEPTION_H #include // ctime +#include #include // ostringstream /// Abstraction of a custom exception class for dynamic message building and timestamping.