Skip to content

Commit

Permalink
feat: add string abi decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
as-iotex authored and as-iotex committed Apr 17, 2022
1 parent 4a83ff7 commit 4a571b0
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 9 deletions.
4 changes: 2 additions & 2 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"tasks": [
{
"type": "shell",
"label": "Arduino: Build unit tests",
"command": "cd ${workspaceRoot}/build && cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON ../ && make",
"label": "IoTeX SDK: Build unit tests",
"command": "cd ${workspaceRoot}/build && cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON ../ && make -j 6 ",
"options": {
"cwd": "${workspaceRoot}/"
},
Expand Down
40 changes: 33 additions & 7 deletions src/abi/abiDecode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,24 +74,50 @@ int64_t iotex::abi::decode::decodeInt64(const char pData[64])
ResultCode iotex::abi::decode::decodeUintGeneric(const char* pData, size_t uintSize, uint64_t* out)
{
uint8_t bytes[8] = {0};
ResultCode res = signer.str2hex(pData, bytes + sizeof(bytes) - uintSize, uintSize);

ResultCode res = signer.str2hex(pData, bytes + sizeof(bytes) - uintSize, uintSize, uintSize*2);
if (res != ResultCode::SUCCESS) { return res; }

IotexHelpers.endianSwap(bytes, 8);
*out = *((uint64_t*) bytes);

return ResultCode::SUCCESS;
}

ResultCode iotex::abi::decode::decodeIntGeneric(const char* pData, size_t uintSize, int64_t* out)
{
uint8_t bytes[8];
memset(bytes, 0xFF, sizeof(bytes));
ResultCode res = signer.str2hex(pData, bytes + sizeof(bytes) - uintSize, uintSize);

ResultCode res = signer.str2hex(pData, bytes + sizeof(bytes) - uintSize, uintSize, uintSize*2);
if (res != ResultCode::SUCCESS) { return res; }

IotexHelpers.endianSwap(bytes, 8);
*out = *((int64_t*) bytes);

return ResultCode::SUCCESS;
}
}

ResultCode iotex::abi::decode::decodeString(const char* pData, size_t size, IotexString& out)
{
// A string has n 32 byte words where n>=3.
// Word 1: header (offset to the data area)
// Word 2: size of the string, encoded as uint
// Words 3-n: the string contents.
const uint wordSize = 64;
if (size < 3*wordSize) { return ResultCode::ERROR_BAD_PARAMETER; }

// Parse the string size to get the string size.
// We only parse the last 8 bytpDataes from the size word. The size will never overflow a 64b uint.
const char* pStringLenStart = pData + (wordSize*2) - 16;
uint64_t stringLength = 0;
ResultCode res = decodeUintGeneric(pStringLenStart, 8, &stringLength);
if (res != ResultCode::SUCCESS) { return res; }
// Check the size of the passed data is enough to contain the string size
if ((size-64) < stringLength) { return ResultCode::ERROR_BAD_PARAMETER; }

// Create a buffer of string size. Add 1 extra byte for the null terminator.
out.reserve(stringLength);
const char* pString = pData + wordSize*2;
char buf[stringLength+1] = {0};
signer.str2hex(pString, (uint8_t*) buf, sizeof(buf), stringLength*2);
out = buf;

return ResultCode::SUCCESS;
}
3 changes: 3 additions & 0 deletions src/abi/abiDecode.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef IOTEX_ABI_DECODE_H
#define IOTEX_ABI_DECODE_H

#include "helpers/client_helper.h"
#include "IoTeXConstants.h"
#include "IoTeXResultCodes.h"

Expand Down Expand Up @@ -88,6 +89,8 @@ bool decodeBool(const char data[64]);

iotex::ResultCode decodeUintGeneric(const char* pData, size_t uintSize, uint64_t* out);
iotex::ResultCode decodeIntGeneric(const char* pData, size_t uintSize, int64_t* out);
iotex::ResultCode decodeString(const char* pData, size_t size, IotexString& out);

template<uint8_t size>
ResultCode decodeUint(const char* pData, size_t dataSize, uint64_t* out)
{
Expand Down
65 changes: 65 additions & 0 deletions tests/src/abi/decode/abiDecodeStringTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "abi/abiDecode.h"
#include "contract/contract.h"
#include "signer/signer.h"

using namespace std;
using namespace testing;
using namespace iotex;
using namespace iotex::abi::decode;

class AbiDecodeStringTests : public Test
{
void SetUp() override {}

void TearDown() override {}
};

TEST_F(AbiDecodeStringTests, DecodesCorrectly)
{
char encoded[] =
// Line 1(head): - offset to the encoded string parameter
// Line 2: string length (encoded as uint)
// Line 3: the string
"0000000000000000000000000000000000000000000000000000000000000020"
"0000000000000000000000000000000000000000000000000000000000000020"
"6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546";
std::string decoded;
ResultCode res = decodeString(encoded, sizeof(encoded), decoded);
ASSERT_EQ(ResultCode::SUCCESS, res);
ASSERT_STREQ("abcdefghijklmnopqrstuvwxyzABCDEF", decoded.c_str());
}

TEST_F(AbiDecodeStringTests, DecodesLongString)
{
char encoded[] = "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000406162636465666768696a6b6c6d6e6f707172737475767778797a4142434445464748494a4b4c4d4e4f505152535455565758595a303132333435363738392a2a";
std::string decoded;
ResultCode res = decodeString(encoded, sizeof(encoded), decoded);
ASSERT_EQ(ResultCode::SUCCESS, res);
ASSERT_STREQ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789**", decoded.c_str());
}

TEST_F(AbiDecodeStringTests, LessThan3Words_Fails)
{
// The encoded string is missing the header
char encoded[] =
"000000000000000000000000000000000000000000000000000000000000000a"
"74657374537472696e6700000000000000000000000000000000000000000000";
std::string decoded;
ResultCode res = decodeString(encoded, sizeof(encoded), decoded);
ASSERT_EQ(ResultCode::ERROR_BAD_PARAMETER, res);
}

TEST_F(AbiDecodeStringTests, WrongEncodedStringSize_Fails)
{
char encoded[] =
// The first line indicates a string size of 33 bytes (0x21)
"0000000000000000000000000000000000000000000000000000000000000021"
// But we are only passing 1 word after (32 bytes)
"6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546";
std::string decoded;
ResultCode res = decodeString(encoded, sizeof(encoded), decoded);
ASSERT_EQ(ResultCode::ERROR_BAD_PARAMETER, res);
}

0 comments on commit 4a571b0

Please sign in to comment.