Skip to content

Commit

Permalink
Allow multiple dynamic params on function calls, fix for param length…
Browse files Browse the repository at this point in the history
… on TCPBridge comms
  • Loading branch information
JamesSmartCell committed Jun 23, 2023
1 parent f2fd05c commit 4a3b4c6
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 48 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@

What's New:

v1.41:
- Allow multiple dynamic params on function calls.
- Fix/allow unlimited length params on TcpBridge.

v1.4:
- Add Sepolia testnet.
- Ensure Polygon and Mumbai are working correctly.
- Optimse and fix contract call result handling.
- Optimise and fix contract call result handling.
- Handle String return correctly.

v1.34:
Expand Down
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
"Smart Tokens"
],
"name": "Web3E",
"version": "1.4",
"version": "1.41",
"homepage": "https://www.smarttokenlabs.com/"
}
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=Web3E
version=1.4
version=1.41
author=James Brown<[email protected]>
maintainer=James Brown <[email protected]>
sentence=Web3E (Ethereum For Embedded) interface for Arduino and compitable devices.
Expand Down
87 changes: 76 additions & 11 deletions src/Contract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,43 +64,91 @@ string Contract::SetupContractData(const char* func, ...)
}
}

vector<string> abiBlocks;
vector<bool> isDynamic;
int dynamicStartPointer = 0;

va_list args;
va_start(args, func);
for( int i = 0; i < paramCount; ++i ) {
if (strstr(params[i].c_str(), "uint") != NULL && strstr(params[i].c_str(), "[]") != NULL)
{
// value array
string output = GenerateBytesForUIntArray(va_arg(args, vector<uint32_t> *));
ret = ret + output;
abiBlocks.push_back(output);
isDynamic.push_back(false);
dynamicStartPointer += 0x20;
}
else if (strncmp(params[i].c_str(), "uint", sizeof("uint")) == 0 || strncmp(params[i].c_str(), "uint256", sizeof("uint256")) == 0)
{
string output = GenerateBytesForUint(va_arg(args, uint256_t *));
ret = ret + output;
abiBlocks.push_back(output);
isDynamic.push_back(false);
dynamicStartPointer += 0x20;
}
else if (strncmp(params[i].c_str(), "int", sizeof("int")) == 0 || strncmp(params[i].c_str(), "bool", sizeof("bool")) == 0)
{
string output = GenerateBytesForInt(va_arg(args, int32_t));
ret = ret + string(output);
abiBlocks.push_back(output);
isDynamic.push_back(false);
dynamicStartPointer += 0x20;
}
else if (strncmp(params[i].c_str(), "address", sizeof("address")) == 0)
{
string output = GenerateBytesForAddress(va_arg(args, string *));
ret = ret + string(output);
abiBlocks.push_back(output);
isDynamic.push_back(false);
dynamicStartPointer += 0x20;
}
else if (strncmp(params[i].c_str(), "string", sizeof("string")) == 0)
{
string output = GenerateBytesForString(va_arg(args, string *));
ret = ret + string(output);
abiBlocks.push_back(output);
isDynamic.push_back(true);
dynamicStartPointer += 0x20;
}
else if (strncmp(params[i].c_str(), "bytes", sizeof("bytes")) == 0) //if sending bytes, take the value in hex
{
string output = GenerateBytesForHexBytes(va_arg(args, string *));
ret = ret + string(output);
abiBlocks.push_back(output);
isDynamic.push_back(true);
dynamicStartPointer += 0x20;
}
else if (strncmp(params[i].c_str(), "struct", sizeof("struct")) == 0) //if sending bytes, take the value in hex
{
string output = GenerateBytesForStruct(va_arg(args, string *));
abiBlocks.push_back(output);
isDynamic.push_back(true);
dynamicStartPointer += 0x20;
}
}
va_end(args);

uint256_t abiOffet = uint256_t(dynamicStartPointer);
//now build output - parse 1, standard params
for( int i = 0; i < paramCount; ++i )
{
if (isDynamic[i])
{
ret = ret + abiOffet.str(16, 64);
string *outputHex = &abiBlocks[i];
abiOffet += outputHex->size() / 2;
}
else
{
ret = ret + abiBlocks[i];
}
}

//parse 2: add dynamic params
for( int i = 0; i < paramCount; ++i )
{
if (isDynamic[i])
{
ret = ret + abiBlocks[i];
}
}

return ret;
}

Expand Down Expand Up @@ -137,6 +185,15 @@ string Contract::SendTransaction(uint32_t nonceVal, unsigned long long gasPriceV
return web3->EthSendSignedTransaction(&paramStr, param.size());
}

/**
* Utility functions
**/

void Contract::ReplaceFunction(std::string &param, const char* func)
{
param = GenerateContractBytes(func) + param.substr(10);
}

/**
* Private functions
**/
Expand Down Expand Up @@ -217,18 +274,26 @@ string Contract::GenerateBytesForHexBytes(const string *value)
else if (value->at(1) == 'x') cleaned = value->substr(2);
string digitsStr = Util::intToHex(cleaned.length() / 2); //bytes length will be hex length / 2
string lengthDesignator = string(64 - digitsStr.length(), '0') + digitsStr;
//write designator. TODO: This should be done for multiple inputs not just single inputs
lengthDesignator = "0000000000000000000000000000000000000000000000000000000000000020" + lengthDesignator;
cleaned = lengthDesignator + cleaned;
size_t digits = cleaned.length() % 64;
return cleaned + string(64 - digits, '0');
return cleaned + (digits > 0 ? string(64 - digits, '0') : "");
}

string Contract::GenerateBytesForStruct(const string *value)
{
//struct has no length params: not required
string cleaned = *value;
if (value->at(0) == 'x') cleaned = value->substr(1);
else if (value->at(1) == 'x') cleaned = value->substr(2);
size_t digits = cleaned.length() % 64;
return cleaned + (digits > 0 ? string(64 - digits, '0') : "");
}

string Contract::GenerateBytesForBytes(const char *value, const int len)
{
string bytesStr = Util::ConvertBytesToHex((const uint8_t *)value, len).substr(2); //clean hex prefix;
size_t digits = bytesStr.length();
return bytesStr + string(64 - digits, '0');
size_t digits = bytesStr.length() % 64;
return bytesStr + (digits > 0 ? string(64 - digits, '0') : "");
}

vector<uint8_t> Contract::RlpEncode(
Expand Down
5 changes: 4 additions & 1 deletion src/Contract.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,23 @@ class Contract {
string SendTransaction(uint32_t nonceVal, unsigned long long gasPriceVal, uint32_t gasLimitVal,
string *toStr, uint256_t *valueStr, string *dataStr);

static void ReplaceFunction(std::string &param, const char* func);

private:
Web3* web3;
const char * contractAddress;
Crypto* crypto;

private:
string GenerateContractBytes(const char *func);
static string GenerateContractBytes(const char *func);
string GenerateBytesForInt(const int32_t value);
string GenerateBytesForUint(const uint256_t *value);
string GenerateBytesForAddress(const string *value);
string GenerateBytesForString(const string *value);
string GenerateBytesForBytes(const char* value, const int len);
string GenerateBytesForUIntArray(const vector<uint32_t> *v);
string GenerateBytesForHexBytes(const string *value);
string GenerateBytesForStruct(const string *value);

void GenerateSignature(uint8_t* signature, int* recid, uint32_t nonceVal, unsigned long long gasPriceVal, uint32_t gasLimitVal,
string* toStr, uint256_t* valueStr, string* dataStr);
Expand Down
85 changes: 55 additions & 30 deletions src/TcpBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,40 +53,51 @@ void TcpBridge::checkClientAPI(TcpBridgeCallback callback)
{
maintainComms(millis());

if (!available())
uint16_t rLen = available();

if (rLen <= 0)
return;

int len = read(packetBuffer, PACKET_BUFFER_SIZE);
Serial.print("Available: ");
Serial.println(rLen);

int type = read();

if (len > 0)
if (type > 0)
{
BYTE type = packetBuffer[0];
int len;
lastComms = millis();
std::string result;

Serial.print("RCV: ");
Serial.println(len);

switch (type)
{
case 0x02:
signChallenge(&packetBuffer[1], len - 1);
Serial.println("Challenge");
len = read(packetBuffer, PACKET_BUFFER_SIZE);
signChallenge(packetBuffer, len);
// challenge, we sign it
connectionState = confirmed;
break;

case 0x04:
// API call
scanAPI(packetBuffer + 1, apiReturn, len - 1);
scanAPI(apiReturn, rLen - 1);
result = callback(apiReturn);
Serial.print("Result ");
Serial.println(result.c_str());
sendResponse(result);
break;

case 0x06:
Serial.println("Keep Alive");
read(packetBuffer, PACKET_BUFFER_SIZE);
SendKeepAlive();
break;

default:
Serial.print("Unknown: ");
Serial.println(type);
break;
}
}
}
Expand Down Expand Up @@ -146,54 +157,68 @@ void TcpBridge::SendKeepAlive()
write(packetBuffer, 1);
}

void TcpBridge::scanAPI(const BYTE *packet, APIReturn *apiReturn, int payloadLength)
void TcpBridge::scanAPI(APIReturn *apiReturn, int available)
{
apiReturn->clear();

int index = 0;

// read length of API description
apiReturn->apiName = getArg(packet, index, payloadLength);
apiReturn->clear();
apiReturn->apiName = getArg(index);
Serial.print("API: ");
Serial.println(apiReturn->apiName.c_str());

while (index < payloadLength)
while (index < available)
{
std::string key = getArg(packet, index, payloadLength);
std::string param = getArg(packet, index, payloadLength);
std::string key = getArg(index);
std::string param = getArg(index);

apiReturn->params[key] = param;
Serial.print("PAIR: ");
/*Serial.print("PAIR: ");
Serial.print(key.c_str());
Serial.print(" ");
Serial.println(param.c_str());
Serial.print("Index: ");
Serial.println(index);*/
}
}

int TcpBridge::getArglen(const BYTE *packet, int &index)
int TcpBridge::getArgLen(int &index)
{
int byteArgLen = packet[index++] & 0xFF;
int argLen = byteArgLen;
int byteArgLen = 0xFF;
int argLen = 0;

while ((byteArgLen & 0xFF) == 0xFF)
{
byteArgLen = packet[index++] & 0xFF;
byteArgLen = read();
argLen += byteArgLen;
index++;
}

return argLen;
}

std::string TcpBridge::getArg(const BYTE *packet, int &index, int payloadLength)
std::string TcpBridge::bufferToString(const BYTE *packet, int endIndex)
{
int argLen = getArglen(packet, index);
std::string retVal = "";
int endIndex = index + argLen;
if (endIndex > payloadLength)
endIndex = payloadLength;
for (; index < endIndex; index++)
for (int index = 0; index < endIndex; index++)
{
retVal = retVal + (char)packet[index];
}

return retVal;
}
}

std::string TcpBridge::getArg(int &index)
{
int argLen = getArgLen(index);
std::string retVal = "";

while (argLen > 0)
{
int readAmount = argLen > PACKET_BUFFER_SIZE ? PACKET_BUFFER_SIZE : argLen;
int len = read(packetBuffer, readAmount);
index += len;
argLen -= len;
retVal += bufferToString(packetBuffer, len);
}

return retVal;
}
7 changes: 4 additions & 3 deletions src/TcpBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ class TcpBridge : public WiFiClient
int getPort() { return port; }

private:
void scanAPI(const BYTE *packet, APIReturn *apiReturn, int payloadLength);
std::string getArg(const BYTE *packet, int &index, int payloadLength);
void scanAPI(APIReturn *apiReturn, int available);
std::string getArg(int &index);
std::string bufferToString(const BYTE *packet, int index);
void closeConnection();
void SendKeepAlive();
void maintainComms(long currentMillis);
Expand All @@ -31,7 +32,7 @@ class TcpBridge : public WiFiClient
inline boolean isNewSession();
void sendPing();
void sendResponse(std::string resp);
int getArglen(const BYTE *packet, int &index);
int getArgLen(int &index);

Web3 *web3;
KeyID *keyID;
Expand Down
8 changes: 8 additions & 0 deletions src/Web3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ bool Web3::getBool(const string* json) {
return v > 0;
}

string Web3::getResult(const string* json) {
TagReader reader;
string res = reader.getTag(json, "result");
if (res.at(0) == 'x') res = res.substr(1);
else if (res.at(1) == 'x') res = res.substr(2);
return res;
}

//Currently only works for string return eg: function name() returns (string)
string Web3::getString(const string *json)
{
Expand Down
1 change: 1 addition & 0 deletions src/Web3.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class Web3 {
string getString(const string* json);
int getInt(const string* json);
uint256_t getUint256(const string* json);
string getResult(const string* json);

private:
string exec(const string* data);
Expand Down

0 comments on commit 4a3b4c6

Please sign in to comment.