solidity assert for several not nulls - solidity

I have the following function in solidity:
function registerStudentEvaluation(string memory _firstName, string memory _lastName, string memory _subject, uint _note) public returns(Note memory) {
bytes32 studentHash = keccak256(abi.encodePacked(_firstName, _lastName));
bytes32 subjectHash = keccak256(abi.encode(_subject));
address teacherAddress = msg.sender;
assert (students[studentHash] && subjects[subjectHash] && teachers[msg.sender]);
Note memory newNote = Note(studentHash, subjectHash, msg.sender, _note);
bytes32 noteHash = keccak256(abi.encodePacked(studentHash, subjectHash, msg.sender, _note));
notes[noteHash] = newNote;
emit student_evaluated(noteHash);
return newNote;
}
And I am getting these errors:
TypeError: Operator & not compatible with types struct Notes.Student storage ref and struct Notes.Subject storage ref
--> notes.sol:71:17:
|
71 | assert (students[studentHash] & subjects[subjectHash] & teachers[msg.sender])
TypeError: Operator && not compatible with types struct Notes.Student storage ref and struct Notes.Teacher storage ref
TypeError: Invalid type for argument in function call. Invalid implicit conversion from struct Notes.Student storage ref to bool requested.
How can I assert for several not nulls in Solidity?

Solidity uses two ampersands && as the logical AND operator.
assert(true && true);
One ampersand & is the bitwise AND operator.
uint8 three = 3; // 00000011
uint8 five = 5; // 00000101
uint8 result = three & five; // 00000001
Docs: https://docs.soliditylang.org/en/v0.8.12/cheatsheet.html
Solution: Use logical operators in the assert() condition.
assert (students[studentHash] && subjects[subjectHash] && teachers[msg.sender]);
Note: It's not defined in your question but I'm assuming that the students property is defined as mapping(bytes32 => bool) students;

Related

Solidity unit type holding an array?

I am working through the solidity course cryptozombies and heres something im not understanding
struct Zombie {
string name;
uint dna;
uint32 level;
uint32 readyTime;
uint16 winCount;
uint16 lossCount;
}
Zombie[] public zombies;
mapping (uint => address) public zombieToOwner;
mapping (address => uint) ownerZombieCount;
function _createZombie(string memory _name, uint _dna) internal {
uint id = zombies.push(Zombie(_name, _dna, 1, uint32(now + cooldownTime), 0, 0)) - 1;
zombieToOwner[id] = msg.sender;
ownerZombieCount[msg.sender]++;
emit NewZombie(id, _name, _dna);
}
Based on my understanding "zombies" is an array containing a string and different type of integers. If you look in the _createzombie function "id" is set as an uint. How can something of type uint store all these values?
Based on my understanding "zombies" is an array containing a string and different type of integers
This is only partially correct. zombies is in fact an array. But each of its items is of type Zombie.
A struct (docs) is kind of a "wrapper" type - it can contain multiple other datatypes on the inside, but from the outside it's seen as just one datatype (in this case you created a new datatype called Zombie).
You can see it for example in the push() function that accepts one new item of the array - type Zombie (wrapping the other variables).
zombies.push(
Zombie(...) // pushes 1 item of type `Zombie` to the array
)

Looping over array with array length as condition

Say we have a contract with a dynamic state array fooArray of uint256. At the same contract there is a function with the following loop:
for (uint256 i = 0; i < fooArray.length; ++i) {
// Some code here, that does not modify array in any way.
}
Does checking the loop condition require reading the length of the fooArray from the storage on each iteration or it is not considered as reading a value from the storage?
Should I preliminarily save length of the array to a local variable and use this variable as a loop condition to save gas?
I created new contract and then executed it 3 times without temp variable and then again created new contract and executed with temp variable. Results from Remix Javscript VM and Rinkeby networks were precisely the same and function with temp variable required less gas. It is possible to read bytecode and Gas Costs from Yellow Paper however I think it is quite clear that it is better to use temp variable.
Solidity code:
pragma solidity ^0.8.0;
contract EstimateGas {
uint[] public fooArray;
constructor (){
for (uint256 i = 0; i < 500; ++i) {
fooArray.push(i);
}
}
function changeNoTemp() public{
for (uint256 i = 0; i < fooArray.length; ++i) {
fooArray[i] = fooArray[i] +1;
}
}
function changeWithTemp() public{
uint arrayLen = fooArray.length;
for (uint256 i = 0; i < arrayLen; ++i) {
fooArray[i] = fooArray[i] +1;
}
}
}
Gas execution costs
changeNoTemp()
1) 2965426 gas
2) 2948326 gas
3) 2948326 gas
changeWithTemp()
1) 2911461 gas
2) 2894361 gas
3) 2894361 gas

how to use the ECDSA.sol module correctly?

I have this contract using the ECDSA library.
import "./ECDSA.sol";
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
function make(Signature memory sign) public returns(bool)
I try to understand the parameters I have to use in this case. What I can see it's a tuple type value, but I can't figure out what it looks like for v, r, s. Where can I get these values from my address?
The v, r, and s parameters are a result of signing a message with a private key. The signature has 65 bytes, which are split into 3 parts:
65 byte array (of type bytes in Solidity) arranged the following way: [[v (1)], [r (32)], [s (32)]].
Source: OpenZeppelin
Sign off-chain (because you're using a private key).
Note the address in the comment, we'll verify it on-chain later.
const signature = await web3.eth.accounts.sign(
'Hello world',
// below is private key to the address `0x0647EcF0D64F65AdA7991A44cF5E7361fd131643`
'02ed07b6d5f2e29907962d2bfde8f46f03c46e79d5f2ded0b1e0c27fa82f1384'
);
console.log(signature);
Output
{
message: 'Hello world',
messageHash: '0x8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede',
v: '0x1c',
r: '0x285e6fbb504b57dca3ceacc851a7bfa37743c79b5c53fb184f4cc0b10ebff6ad',
s: '0x245f558fa13540029f0ee2dc0bd73264cf04f28ba9c2520ad63ddb1f2e7e9b24',
signature: '0x285e6fbb504b57dca3ceacc851a7bfa37743c79b5c53fb184f4cc0b10ebff6ad245f558fa13540029f0ee2dc0bd73264cf04f28ba9c2520ad63ddb1f2e7e9b241c'
}
Note that v is the last byte of signature, r is the first half, and s is the second half (excluding the last byte).
Verify on-chain
pragma solidity ^0.8;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol";
contract MyContract {
function foo() external pure returns (bool) {
address recovered = ECDSA.recover(
0x8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede, // messageHash
0x1c, // v
0x285e6fbb504b57dca3ceacc851a7bfa37743c79b5c53fb184f4cc0b10ebff6ad, // r
0x245f558fa13540029f0ee2dc0bd73264cf04f28ba9c2520ad63ddb1f2e7e9b24 // s
);
return recovered == address(0x0647EcF0D64F65AdA7991A44cF5E7361fd131643);
}
}

Why does declaring returns type bytes memory give a compiler error?

If we have the following code:
pragma solidity >= 0.5;
contract stringsContract {
function takesTwo(string memory str, uint idx) public pure returns (bytes memory) {
bytes memory bytesStr = bytes(str);
return bytesStr[idx];
}
}
Why do we get TypeError return argument bytes1 is not explicitly convertible to expected type (type of first return variable bytes memory).
The fix was to change bytes memory to bytes:
contract stringsContract {
function takesTwo(string memory str, uint idx) public pure returns (byte) {
bytes memory bytesStr = bytes(str);
return bytesStr[idx];
}
}
Nevertheless, I'm still curious about the reason of the compilation error. Any thoughts?
Variable of type 'bytes' and 'string' are special arrays. In first code snippet you are returning an array which is not yet possible in Solidity yet.
You can find docs: https://solidity.readthedocs.io/en/develop/types.html#bytes-and-strings-as-arrays
When you return in solidity the syntax is like (value_type value_name: optional), what you tried returning bytes which is an array and with name memory, memory itself is spacial keyword in solidity

Using ecrecover function - Solidity

I'm trying to verify a message, I searched on StackOverflow and I found the ecrecover function. but when I use it, it returns a different address from what I expect.
function verify(bytes32 hash, uint8 v, bytes32 r, bytes32 s) constant returns(address) {
bytes memory prefix = "\x19Ethereum Signed Message:\n32";
bytes32 prefixedHash = keccak256(prefix, hash);
return ecrecover(prefixedHash, v, r, s);
}
signature object:
{
message: 'a',
messageHash: '0x34f291c0b5f0c13c8f43e9d37c04094c22234da43f4040adb36654c98235b4b3',
v: '0x1b',
r: '0x944f8187c19a711259e32dd9ab0f005c97c9e2013c735f823d3ad34c7cd5030f',
s: '0x254607e8d32e8a0436c8d678fe7d3478c8858fd903e164c51f8a8595e723b7a7',
signature: '0x944f8187c19a711259e32dd9ab0f005c97c9e2013c735f823d3ad34c7cd5030f254607e8d32e8a0436c8d678fe7d3478c8858fd903e164c51f8a8595e723b7a71b' }
input: (i pass it to remix-ide)
"0x34f291c0b5f0c13c8f43e9d37c04094c22234da43f4040adb36654c98235b4b3", 0x1b, "0x944f8187c19a711259e32dd9ab0f005c97c9e2013c735f823d3ad34c7cd5030f", "0x254607e8d32e8a0436c8d678fe7d3478c8858fd903e164c51f8a8595e723b7a7"
output: (wrong)
0x5dd277a46b3ab8ce30735d82df5e6e8312bce7ef
Please help me figure out the problems. many thanks.