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

New supporting functions for eth-sn-contracts #11

Merged
merged 6 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
8 changes: 5 additions & 3 deletions include/ethyl/provider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#pragma once

#include <string>
#include <string_view>
#include <optional>
#include <chrono>

Expand Down Expand Up @@ -38,9 +39,10 @@ class Provider {
void connectToNetwork();
void disconnectFromNetwork();

uint64_t getTransactionCount(const std::string& address, const std::string& blockTag);
std::string callReadFunction(const ReadCallData& callData, uint64_t blockNumberInt);
std::string callReadFunction(const ReadCallData& callData, const std::string& blockNumber = "latest");
uint64_t getTransactionCount(const std::string& address, const std::string& blockTag);
nlohmann::json callReadFunctionJSON(const ReadCallData& callData, std::string_view blockNumber = "latest");
std::string callReadFunction(const ReadCallData& callData, std::string_view blockNumber = "latest");
std::string callReadFunction(const ReadCallData& callData, uint64_t blockNumberInt);

uint32_t getNetworkChainId();
std::string evm_snapshot();
Expand Down
3 changes: 2 additions & 1 deletion include/ethyl/signer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class Signer {

// Returns <Pubkey, Seckey>
std::pair<std::vector<unsigned char>, std::vector<unsigned char>> generate_key_pair();
std::string addressFromPrivateKey(const std::vector<unsigned char>& seckey);
std::array<unsigned char, 20> secretKeyToAddress(const std::vector<unsigned char>& seckey);
std::string secretKeyToAddressString(const std::vector<unsigned char>& seckey);

std::vector<unsigned char> sign(const std::array<unsigned char, 32>& hash, const std::vector<unsigned char>& seckey);
std::vector<unsigned char> sign(const std::string& hash, const std::vector<unsigned char>& seckey);
Expand Down
13 changes: 7 additions & 6 deletions include/ethyl/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,19 @@ namespace utils
return oss.str();
}

std::string decimalToHex(uint64_t decimal);
std::string decimalToHex(uint64_t decimal);
std::string_view trimPrefix(std::string_view src, std::string_view prefix);
std::string_view trimLeadingZeros(std::string_view src);

std::vector<unsigned char> fromHexString(std::string hex_str);
uint64_t fromHexStringToUint64(std::string hex_str);

std::array<unsigned char, 32> fromHexString32Byte(std::string hex_str);
std::vector<unsigned char> fromHexString(std::string_view hexStr);
uint64_t fromHexStringToUint64(std::string_view hexStr);
std::array<unsigned char, 32> fromHexString32Byte(std::string_view hexStr);

std::array<unsigned char, 32> hash(std::string in);

std::string getFunctionSignature(const std::string& function);

std::string padToNBytes(const std::string& input, size_t byte_count, PaddingDirection direction = PaddingDirection::LEFT);
std::string padToNBytes(const std::string& input, size_t byteCount, PaddingDirection direction = PaddingDirection::LEFT);
std::string padTo8Bytes(const std::string& input, PaddingDirection direction = PaddingDirection::LEFT);
std::string padTo32Bytes(const std::string& input, PaddingDirection direction = PaddingDirection::LEFT);

Expand Down
42 changes: 28 additions & 14 deletions src/provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,30 +48,44 @@ cpr::Response Provider::makeJsonRpcRequest(const std::string& method, const nloh
return session.Post();
}

std::string Provider::callReadFunction(const ReadCallData& callData, uint64_t blockNumberInt) {
std::stringstream stream;
stream << "0x" << std::hex << blockNumberInt; // Convert uint64_t to hex string
std::string blockNumberHex = stream.str();

return callReadFunction(callData, blockNumberHex); // Call the original function
}
nlohmann::json Provider::callReadFunctionJSON(const ReadCallData& callData, std::string_view blockNumber) {
nlohmann::json result = {};

std::string Provider::callReadFunction(const ReadCallData& callData, const std::string& blockNumber) {
// Prepare the params for the eth_call request
nlohmann::json params = nlohmann::json::array();
params[0]["to"] = callData.contractAddress;
params[0]["data"] = callData.data;
params[1] = blockNumber; // use the provided block number or default to "latest"
nlohmann::json params = nlohmann::json::array();
params[0]["to"] = callData.contractAddress;
params[0]["data"] = callData.data;
params[1] = blockNumber; // use the provided block number or default to "latest"
cpr::Response response = makeJsonRpcRequest("eth_call", params);

if (response.status_code == 200) {
nlohmann::json responseJson = nlohmann::json::parse(response.text);
if (!responseJson["result"].is_null()) {
return responseJson["result"];
result = responseJson["result"];
return result;
}
}

throw std::runtime_error("Unable to get the result of the function call");
std::stringstream stream;
stream << "'eth_call' invoked on node for block '" << blockNumber
<< "' to '" << callData.contractAddress
<< "' with data payload '" << callData.data
<< "' however it returned a response that does not have a result: "
<< response.text;
throw std::runtime_error(stream.str());
}

std::string Provider::callReadFunction(const ReadCallData& callData, std::string_view blockNumber) {
std::string result = callReadFunctionJSON(callData, blockNumber);
return result;
}

std::string Provider::callReadFunction(const ReadCallData& callData, uint64_t blockNumberInt) {
std::stringstream stream;
stream << "0x" << std::hex << blockNumberInt; // Convert uint64_t to hex string
std::string blockNumberHex = stream.str();
std::string result = callReadFunctionJSON(callData, blockNumberHex);
return result;
}

uint32_t Provider::getNetworkChainId() {
Expand Down
18 changes: 13 additions & 5 deletions src/signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ std::pair<std::vector<unsigned char>, std::vector<unsigned char>> Signer::genera
std::vector<unsigned char>(compressed_pubkey, compressed_pubkey + sizeof(compressed_pubkey))};
}

std::string Signer::addressFromPrivateKey(const std::vector<unsigned char>& seckey) {
std::array<unsigned char, 20> Signer::secretKeyToAddress(const std::vector<unsigned char>& seckey) {
std::string address;

// Verify the private key.
Expand All @@ -84,10 +84,18 @@ std::string Signer::addressFromPrivateKey(const std::vector<unsigned char>& seck
auto hashed_pub = utils::hash(pub_string);

// The last 20 bytes of the Keccak-256 hash of the public key in hex is the address.
address = utils::toHexString(hashed_pub);
address = address.substr(address.size() - 40);
std::array<unsigned char, 20> result = {};
std::memcpy(result.data(), hashed_pub.data() + hashed_pub.size() - result.size(), result.size());
return result;
}

return "0x" + address;
std::string Signer::secretKeyToAddressString(const std::vector<unsigned char>& seckey) {
std::array<unsigned char, 20> address = secretKeyToAddress(seckey);
std::string result = {};
result.reserve(2 + (address.max_size() * 2));
result += "0x";
result += utils::toHexString(address);
return result;
}


Expand Down Expand Up @@ -173,7 +181,7 @@ std::string Signer::signTransaction(Transaction& txn, const std::vector<unsigned

// Populates the txn, signs and sends
std::string Signer::sendTransaction(Transaction& txn, const std::vector<unsigned char>& seckey) {
const auto senders_address = addressFromPrivateKey(seckey);
const auto senders_address = secretKeyToAddressString(seckey);

populateTransaction(txn, senders_address);
const auto signature_hex = utils::toHexString(sign(txn.hash(), seckey));
Expand Down
109 changes: 80 additions & 29 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string>
#include <cstdlib>
#include <ctime>
#include <cassert>

extern "C" {
#include "crypto/keccak.h"
Expand All @@ -15,37 +16,86 @@ std::string utils::decimalToHex(uint64_t decimal) {
return ss.str();
}

std::vector<unsigned char> utils::fromHexString(std::string hex_str) {
std::vector<unsigned char> bytes;

// Check for "0x" prefix and remove it
if(hex_str.size() >= 2 && hex_str[0] == '0' && hex_str[1] == 'x') {
hex_str = hex_str.substr(2);
std::string_view utils::trimPrefix(std::string_view src, std::string_view prefix)
{
std::string_view result = src;
if (result.size() >= prefix.size()) {
if (result.substr(0, prefix.size()) == prefix) {
result = result.substr(prefix.size(), result.size() - prefix.size());
}
}
return result;
}

for (unsigned int i = 0; i < hex_str.length(); i += 2) {
std::string byteString = hex_str.substr(i, 2);
//if (byteString[0] == 0) byteString[0] = '0';
//if (byteString[1] == 0) byteString[1] = '0';
unsigned char byte = static_cast<unsigned char>(strtol(byteString.c_str(), nullptr, 16));
bytes.push_back(byte);
std::string_view utils::trimLeadingZeros(std::string_view src)
{
std::string_view result = src;
while (result.size() && result[0] == '0') {
result = result.substr(1, result.size() - 1);
}
return result;
}

struct HexToU8Result {
bool success;
uint8_t u8;
};

static HexToU8Result hexToU8(char ch) {
HexToU8Result result = {};
result.success = true;

if (ch >= 'a' && ch <= 'f')
result.u8 = static_cast<uint8_t>(ch - 'a' + 10);
else if (ch >= 'A' && ch <= 'F')
result.u8 = static_cast<uint8_t>(ch - 'A' + 10);
else if (ch >= '0' && ch <= '9')
result.u8 = static_cast<uint8_t>(ch - '0');
else
result.success = false;

return bytes;
return result;
}

uint64_t utils::fromHexStringToUint64(std::string hex_str) {
// Check for "0x" prefix and remove it
if(hex_str.size() >= 2 && hex_str[0] == '0' && hex_str[1] == 'x') {
hex_str = hex_str.substr(2);
std::vector<unsigned char> utils::fromHexString(std::string_view hexStr) {
hexStr = trimPrefix(hexStr, "0x");
assert(hexStr.size() % 2 == 0);

std::vector<unsigned char> result;
for (size_t i = 0; i < hexStr.length(); i += 2) {
std::string_view byteString = hexStr.substr(i, 2);
HexToU8Result hi = hexToU8(byteString[0]);
HexToU8Result lo = hexToU8(byteString[1]);
unsigned char byte = static_cast<unsigned char>(hi.u8 << 4 | lo.u8 << 0);
result.push_back(byte);
}
return result;
}

uint64_t value = std::stoull(hex_str, nullptr, 16);
return value;
uint64_t utils::fromHexStringToUint64(std::string_view hexStr) {
std::string_view realHex = trimPrefix(hexStr, "0x");

// NOTE: Trim leading '0's
while (realHex.size() && realHex[0] == '0') {
realHex = realHex.substr(1, realHex.size() - 1);
}

size_t maxHexSize = sizeof(uint64_t) * 2 /*hex chars per byte*/;
assert(realHex.size() <= maxHexSize);

size_t size = std::min(maxHexSize, realHex.size());
uint64_t result = 0;
for (size_t index = 0; index < size; index++) {
char ch = realHex[index];
HexToU8Result hexResult = hexToU8(ch);
assert(hexResult.success);
result = (result << 4) | hexResult.u8;
}
return result;
}

std::array<unsigned char, 32> utils::fromHexString32Byte(std::string hex_str) {
std::vector<unsigned char> bytesVec = fromHexString(hex_str);
std::array<unsigned char, 32> utils::fromHexString32Byte(std::string_view hexStr) {
std::vector<unsigned char> bytesVec = fromHexString(hexStr);

if(bytesVec.size() != 32) {
throw std::invalid_argument("Input string length should be 64 characters for 32 bytes");
Expand Down Expand Up @@ -81,7 +131,7 @@ std::string utils::getFunctionSignature(const std::string& function) {
return "0x" + hashHex.substr(0, 8);
}

std::string utils::padToNBytes(const std::string& input, size_t byte_count, utils::PaddingDirection direction) {
std::string utils::padToNBytes(const std::string& input, size_t byteCount, utils::PaddingDirection direction) {
std::string output = input;
bool has0xPrefix = false;

Expand All @@ -92,20 +142,21 @@ std::string utils::padToNBytes(const std::string& input, size_t byte_count, util
}

// Calculate padding size based on byteCount * 2 (since each byte is represented by 2 hex characters)
size_t targetHexStringSize = byte_count * 2;
size_t nextMultiple = (output.size() + targetHexStringSize - 1) / targetHexStringSize * targetHexStringSize;
size_t paddingSize = nextMultiple - output.size();
std::string padding(paddingSize, '0');
const size_t targetHexStringSize = byteCount * 2;
const size_t startingSize = std::max(output.size(), static_cast<size_t>(1)); // Size is atleast 1 element such that we handle when output.size == 0
const size_t startingSizeRoundedUp = startingSize + (targetHexStringSize - 1);
const size_t nextMultiple = /*floor*/ (startingSizeRoundedUp / targetHexStringSize) * targetHexStringSize;
const size_t paddingSize = nextMultiple - output.size();

if (direction == PaddingDirection::LEFT) {
output = padding + output;
output.insert(0, paddingSize, '0');
} else {
output += padding;
output.append(paddingSize, '0');
}

// If input started with "0x", add it back
if (has0xPrefix) {
output = "0x" + output;
output.insert(0, "0x");
}

return output;
Expand Down
2 changes: 1 addition & 1 deletion test/src/ethereum_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ TEST_CASE( "SigningTest", "[signer]" ) {
TEST_CASE( "Get address from private key", "[signer]" ) {
std::vector<unsigned char> seckey = utils::fromHexString(std::string(PRIVATE_KEY));
Signer signer;
std::string created_address = signer.addressFromPrivateKey(seckey);
std::string created_address = signer.secretKeyToAddressString(seckey);
REQUIRE( created_address == ADDRESS );
}

Expand Down
Loading