Skip to content

Commit

Permalink
Merge pull request #11 from Doy-lee/new-supporting-functions
Browse files Browse the repository at this point in the history
New supporting functions for eth-sn-contracts
  • Loading branch information
darcys22 authored Apr 18, 2024
2 parents d650323 + 7cde41d commit 9a914dc
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 59 deletions.
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

0 comments on commit 9a914dc

Please sign in to comment.