How to compare an ascii string with a uint8 array in Solidity? - ethereum

I have a uint8 array containing ASCII codes for characters and a string variable, and I wish to make a comparison between them. For example:
uint8[3] memory foo = [98, 97, 122]; // baz
string memory bar = "baz";
bool result = keccak256(abi.encodePacked(foo)) == keccak256(abi.encodePacked(bytes(bar))); // false
Here I want the comparison to succeed, but it's a failure because encodePacked will keep the padding of all the uint8 elements in the array when encoding it.
How can I do it instead?

You are currently comparing encoded value abi.encodePacked(foo)) to hashed value keccak256(abi.encodePacked(bytes(bar)), which would never equal.
The uint8 fixed-size array is stored in memory in three separate slots - one for each item - and each of the items is ordered right to left (little endian).
0x
0000000000000000000000000000000000000000000000000000000000000062
0000000000000000000000000000000000000000000000000000000000000061
000000000000000000000000000000000000000000000000000000000000007a
But the string literal is stored as a dynamic-size byte array ordered left to right (big endian):
0x
0000000000000000000000000000000000000000000000000000000000000020 # pointer
0000000000000000000000000000000000000000000000000000000000000003 # length
62617a0000000000000000000000000000000000000000000000000000000000 # value
So because the actual data is stored differently, you cannot perform a simple byte comparison of both arrays.
You can, however, loop through all items of the array and compare each item separately.
pragma solidity ^0.8;
contract MyContract {
function compare() external pure returns (bool) {
uint8[3] memory foo = [98, 97, 122]; // baz
string memory bar = "baz";
// typecast the `string` to `bytes` dynamic-length array
// so that you can use its `.length` member property
// and access its items individually (see `barBytes[i]` below, not possible with `bar[i]`)
bytes memory barBytes = bytes(bar);
// prevent accessing out-of-bounds index in the following loop
// as well as false positive if `foo` contains just the beginning of `bar` but not the whole string
if (foo.length != barBytes.length) {
return false;
}
// loop through each item of `foo`
for (uint i; i < foo.length; i++) {
uint8 barItemDecimal = uint8(barBytes[i]);
// and compare it to each decimal value of `bar` character
if (foo[i] != barItemDecimal) {
return false;
}
}
// all items have equal values
return true;
}
}

Related

How to decode an ETH contract output hex as string?

When I make an eth_call to the Usdt smart contract on Eth MainNet, I get a 96-byte hex output.
0000000000000000000000000000000000000000000000000000000000000020 // What is this?
000000000000000000000000000000000000000000000000000000000000000a // Size of the output
5465746865722055534400000000000000000000000000000000000000000000 // Output ("Tether USD")
I understand that the 3rd 32 bytes contain the actual string output with right padding, and the 2nd 32 bytes contain the output size in bytes with left padding. What do the 1st 32 bytes contain?
Rpc Call
{"jsonrpc":"2.0","method":"eth_call","params":[{"To":"0xdAC17F958D2ee523a2206206994597C13D831ec7","Data":"0x06fdde03"},"latest"],"id":1}
The first 32byte slot is an offset that points to the length slot, which is immediately followed by slot(s) containing the actual param value.
The offset is useful in cases when a function returns multiple dynamic-length arrays (a string is represented as a dynamic-length byte array), like in this example:
pragma solidity ^0.8;
contract MyContract {
function foo() external pure returns (string memory, string memory) {
return ("Tether USD", "Ethereum");
}
}
Returned data:
# offset pointing to the length of the 1st param
0x0000000000000000000000000000000000000000000000000000000000000040
# offset pointing to the length of the 2nd param
0x0000000000000000000000000000000000000000000000000000000000000080
# 1st param length
0x000000000000000000000000000000000000000000000000000000000000000a
# followed by 1st param value
0x5465746865722055534400000000000000000000000000000000000000000000
# 2nd param length
0x0000000000000000000000000000000000000000000000000000000000000008
# followed by 2nd param value
0x457468657265756d000000000000000000000000000000000000000000000000
If you had a fixed-length param between those two, the returned data structure would look like this:
offset to the length of the 1st param
the (fixed length) 2nd param actual value
offset to the length of the 3rd param
the rest is the same as above
Docs: https://docs.soliditylang.org/en/latest/abi-spec.html#use-of-dynamic-types

In what slots are variables stored that are defined after an array in Solidity?

So I know how arrays are stored in storage. If I understand it correctly it first stores the number of items in an array in the first slot, and then in the next slots, it stores the hashed values.
My question is what if I define uint after the array and the array during deployment has only 2 values. So it should take up 3 slots. Then in the fourth slot is the uint I defined.
What if there is a function that will push something to the array? How is it stored?
Will it be stored in the next free slot? Or will it push the uint to the next slot and replace it with the new value?
I hope the question is clear if not I will try to rephrase it.
Also if there is some good resource where I can learn all about storage in solidity please share the link.
Thanks a lot!
Fixed-size array stores its values in sequential order, starting with the 0th index. There's no prepended slot that would show the total length. Any unset values use the default value of 0.
pragma solidity ^0.8;
contract MyContract {
address[3] addresses; // storage slots 0, 1, 2
uint256 number; // storage slot 3
constructor(address[2] memory _addresses, uint256 _number) {
addresses = _addresses;
number = _number;
}
}
Passing 2 addresses to the constructor, storage slot values in this case:
0: _addresses[0]
1: _addresses[1]
2: default value of zero (third address was not defined)
3: _number
Dynamic-size array stores its values in keys that are hash of the property storage slot (in example below that's 0, as that's the first storage property), and immediately following slots. In the property storage slot, it stores the array length.
pragma solidity ^0.8;
contract MyContract {
/*
* storage slots:
* p (in this case value 0, as this is the first storage property) = length of the array
* keccak256(p) = value of index 0
* keccak256(p) + 1 = value of index 1
* etc.
*/
address[] addresses;
// storage slot 1
uint256 number;
constructor(address[] memory _addresses, uint256 _number) {
addresses = _addresses;
number = _number;
}
}
Passing 2 addresses to the constructor, storage slot values in this case:
0: value 2 (length of the array)
1: _number
0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 (hash of uint 0): _addresses[0]
0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e564 (hash of uint 0, plus 1): _addresses[1]
Docs: https://docs.soliditylang.org/en/v0.8.13/internals/layout_in_storage.html#mappings-and-dynamic-arrays
So to answer your questions:
What if there is a function that will push something to the array? How is it stored?
Will it be stored in the next free slot? Or will it push the uint to the next slot and replace it with the new value?
Fixed-size arrays cannot be resized. You can only rewrite its values, while the default value of each item is 0.
In case of dynamic-size arrays, it pushes the new value right after the last one. Since they are stored in slots which indexes are based on a hash, the probability of rewriting another value is practically 0 (i.e. that would mean a hash collision).
In both cases, it doesn't affect how other storage properties are stored.

C++ If ("Char" == "value") { do [duplicate]

I have a character array and I'm trying to figure out if it matches a string literal, for example:
char value[] = "yes";
if(value == "yes") {
// code block
} else {
// code block
}
This resulted in the following error: comparison with string literal results in unspecified behavior. I also tried something like:
char value[] = "yes";
if(strcmp(value, "yes")) {
// code block
} else {
// code block
}
This didn't yield any compiler errors but it is not behaving as expected.
Check the documentation for strcmp. Hint: it doesn't return a boolean value.
ETA: == doesn't work in general because cstr1 == cstr2 compares pointers, so that comparison will only be true if cstr1 and cstr2 point to the same memory location, even if they happen to both refer to strings that are lexicographically equal. What you tried (comparing a cstring to a literal, e.g. cstr == "yes") especially won't work, because the standard doesn't require it to. In a reasonable implementation I doubt it would explode, but cstr == "yes" is unlikely to ever succeed, because cstr is unlikely to refer to the address that the string constant "yes" lives in.
std::strcmp returns 0 if strings are equal.
strcmp returns a tri-state value to indicate what the relative order of the two strings are. When making a call like strcmp(a, b), the function returns
a value < 0 when a < b
0 when a == b
a value > 0 when a > b
As the question is tagged with c++, in addition to David Seilers excellent explanation on why strcmp() did not work in your case, I want to point out, that strcmp() does not work on character arrays in general, only on null-terminated character arrays (Source).
In your case, you are assigning a string literal to a character array, which will result in a null-terminated character array automatically, so no problem here. But, if you slice your character array out of e. g. a buffer, it may not be null-terminated. In such cases, it is dangerous to use strcmp() as it will traverse the memory until it finds a null byte ('\0') to form a string.
Another solution to your problem would be (using C++ std::string):
char value[] = "yes";
if (std::string{value} == "yes")) {
// code block
} else {
// code block
}
This will also only work for null-terminated character arrays. If your character array is not null-terminated, tell the std::string constructor how long your character array is:
char value[3] = "yes";
if (std::string{value, 3} == "yes")) {
// code block
} else {
// code block
}

Send arguments to a function with argv and argc

Can someone help me to understand how i need to send the parameters to the function "lora_rf_config" ? Thank you so much !
I try with:
char cfgred[7][10]={'lora_rf_config','915000000','10','0','1','8','14'};
lora_rf_config(7,&cfgred);
The function that im trying to use is:
static void lora_rf_config(int argc, char *argv[])
{
if (argc == 1) {
e_printf("OK%d,%d,%d,%d,%d,%d\r\n", g_lora_config.lorap2p_param.Frequency,
g_lora_config.lorap2p_param.Spreadfact,
g_lora_config.lorap2p_param.Bandwidth,
g_lora_config.lorap2p_param.Codingrate,
g_lora_config.lorap2p_param.Preamlen,
g_lora_config.lorap2p_param.Powerdbm );
return;
} else {
if (argc != 7) {
out_error(RAK_ARG_ERR);
return;
}
if (!(CHECK_P2P_FREQ(atoi(argv[1])) &&
CHECK_P2P_SF(atoi(argv[2])) &&
CHECK_P2P_BDW(atoi(argv[3])) &&
CHECK_P2P_CR(atoi(argv[4])) &&
CHECK_P2P_PREMLEN(atoi(argv[5])) &&
CHECK_P2P_PWR(atoi(argv[6])))) {
out_error(RAK_ARG_ERR);
return;
}
if (read_partition(PARTITION_0, (char *)&g_lora_config, sizeof(g_lora_config)) < 0) {
out_error(RAK_RD_CFG_ERR);
return;
}
g_lora_config.lorap2p_param.Frequency = atoi(argv[1]);
g_lora_config.lorap2p_param.Spreadfact = atoi(argv[2]);
g_lora_config.lorap2p_param.Bandwidth = atoi(argv[3]);
g_lora_config.lorap2p_param.Codingrate = atoi(argv[4]);
g_lora_config.lorap2p_param.Preamlen = atoi(argv[5]);
g_lora_config.lorap2p_param.Powerdbm = atoi(argv[6]);
write_partition(PARTITION_0, (char *)&g_lora_config, sizeof(g_lora_config));
e_printf("OK\r\n");
}
return;
}
The error that i got is:
..\..\..\src\application\RAK811\app.c(107): error: #26: too many characters in character constant
char cfgred[7][10]={'lora_rf_config','915000000','10','0','1','8','14'};
I dont have experience with this kind of arguments.
Thank you for your time.
lora_rf_config expects same arguments than main function: array of pointers to strings, and its length.
Strings in C are pointers to char, where the char buffer they point to has terminating NUL character (if NUL char is missing, then it's not a string, just a character array). In other words, there is no string type in C, but stringiness is determined by the actual data in the char array or buffer. Using "" string literal creates a string, IOW it adds that terminating NUL char in addition to what you write.
// cfgred is array([) of 7 pointers(*) to char.
// Note: string literals are read-only, so you must not modify these
// strings. If you want a modifiable string, this would be a bit more complex,
// but I think this is out of the scope of your question.
char *cfgred[7] = { "lora_rf_config" , "915000000", "10","0", "1", "8", "14"};
// you can get the number of elements in array by dividing its sizeof size (bytes)
// with the size of it's elements in bytes. Just make sure cfgred here is array...
// in the function it is pointer already (arrays get converted to pointers, so
// you can't do this inside the function, you have to do it where you still have
// the original array
int cfgred_len = sizeof cfgred / sizeof(cfgred[0]);
// when you pass array to function, it is automatically converted to pointer,
// so you must not use & when passing an array like this, otherwise types don't
// match
lora_rf_config(cfgred_len, cfgred);
As a side note, always turn on compiler warnings... They help you a lot, fix them. For gcc and clagn, use -Wall -Wextra, for Visual Studio use /W3 or prefereably /W4. And then fix any warnings you get, because they are probably something that doesn't do what you expect.
Your initialization is not done correctly, try changing
char cfgred[7][10]={'lora_rf_config','915000000','10','0','1','8','14'};
into
char cfgred[7][16]={"lora_rf_config","915000000","10","0","1","8","14"};

Cuda/Thrust: remove_if doesn't change device_vector.size()?

I have a somewhat rather simple cuda question that seems like it should be a straight forward operation: removing elements from 1 array based on the value of a 2nd bool array. The steps I take are:
Create a device_vector of bools with the same size as the processed input array.
Call kernel which will set some of the elements from (1) to true
Call remove_if on input array with predicate using processed array from (2).
For each value in the bool array that is set to true, remove the corresponding element from the input array.
What I am seeing is that the input array isn't changed and I am not sure why ?
struct EntryWasDeleted
{
__device__ __host__
bool operator()(const bool ifDeleted)
{ return true; }
};
//This array has about 200-300 elements
//thrust::device_vector<SomeStruct> & arrayToDelete
thrust::device_vector<bool>* deletedEntries =
new thrust::device_vector<bool>(arrayToDelete.size(), false);
cuDeleteTestEntries<<<grid, block>>>( thrust::raw_pointer_cast(arrayToDelete.data()), countToDelete, heapAccess, thrust::raw_pointer_cast(deletedEntries->data()));
cudaDeviceSynchronize();
thrust::remove_if(arrayToDelete.begin(), arrayToDelete.end(), deletedEntries->begin(), EntryWasDeleted());
//I am expecting testEntries to have 0 elements
thrust::host_vector<SomeStruct> testEntries = arrayToDelete;
for( int i = 0; i<testEntries.size(); i++)
{ printf( "%d", testEntries[i].someValue); }
In this sample, I am always returning true in the predicate for testing. However, when I do: testEntries = deletedEntries and output the members. I can validate that deletedEntries is properly filled in with trues and falses.
My expectation would be that testEntries would have 0 elements. But it doesn't and I get an output as if remove_if didn't do anything. ie: the output is showing ALL elements from the input array. I am not sure why? Is there a specific way to remove elements from a device_vector?
So you need to capture the iterator that is being returned from remove_if
thrust::device_vector<SomeStruct>::iterator endIterator =
thrust::remove_if(arrayToDelete.begin(), arrayToDelete.end(),
deletedEntries->begin(), EntryWasDeleted());
Then when you copy data back to the host instead of using thrusts default assignment operator between host and device do this:
thrust::host_vector<SomeStruct> testEntries(arrayToDelete.begin(),endIterator);
As a side note working with arrays of primitives can often be much more efficient. Like can you store the index of your structs in an array instead and operate on those indexes?