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);
}
}
Related
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;
I am having issues when trying to use the Chainlink random number generator and deploying to Rinkeby. Relevant code pieces are the following:
Constructor from the importing contract (should be working fine).
// RandomNumberConsumer parameters for RINKEBY testnet
address _vrfCoordinator = 0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B;
address _link = 0x01BE23585060835E02B77ef475b0Cc51aA1e0709;
bytes32 _keyHash = 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311;
uint256 _fee = 0.1 * 10 ** 18; // 0.1 LINK
constructor() RandomNumberConsumer(_vrfCoordinator, _link, _keyHash, _fee) {}
RandomNumberConsumer.sol. As specified in the chainlink docs, with a few tweaks needed for my approach.
pragma solidity ^0.8.7;
import "#chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
import "#openzeppelin/contracts/access/Ownable.sol";
import "hardhat/console.sol";
contract RandomNumberConsumer is VRFConsumerBase, Ownable{
// Variables
bytes32 internal s_keyHash;
uint256 internal s_fee;
uint256 private constant ROLL_IN_PROGRESS = 150;
mapping(bytes32 => address) private s_rollers;
mapping(address => uint256) private s_results;
//address vrfCoordinator = 0x3d2341ADb2D31f1c5530cDC622016af293177AE0;
//address link = 0xb0897686c545045aFc77CF20eC7A532E3120E0F1;
// Events
event DiceRolled(bytes32 indexed requestId, address indexed roller);
event DiceLanded(bytes32 indexed requestId, uint256 indexed result);
/**
* Constructor inherits VRFConsumerBase
*
* Network: Rinkeby
* Chainlink VRF Coordinator address: 0xb3dCcb4Cf7a26f6cf6B120Cf5A73875B7BBc655B
* LINK token address: 0x01BE23585060835E02B77ef475b0Cc51aA1e0709
* Key Hash: 0x2ed0feb3e7fd2022120aa84fab1945545a9f2ffc9076fd6156fa96eaff4c1311
*/
constructor(address vrfCoordinator, address link, bytes32 keyHash, uint256 fee)
VRFConsumerBase(vrfCoordinator, link){
s_keyHash = keyHash;
s_fee = fee;
}
// Functions
function rollDice(address roller) public onlyOwner returns (bytes32 requestId){
console.log("RNG Contract address",address(this));
// Checking LINK balance
require(LINK.balanceOf(address(this)) >= s_fee, "Not enough LINK in contract.");
// Checking if roller has already rolled dice since each roller can only ever be assigned to a single house. TODO: this can be changed
require(s_results[roller] == 0, "Already rolled");
// Requesting randomness
requestId = requestRandomness(s_keyHash, s_fee); // Error is happening here!
// Storing requestId and roller address
s_rollers[requestId] = roller;
// Emitting event to signal rolling of dice
s_results[roller] = ROLL_IN_PROGRESS;
emit DiceRolled(requestId, roller);
}
// fulfillRandomness is a special function defined within the VRFConsumerBase contract that our contract extends from.
// The coordinator sends the result of our generated randomness back to fulfillRandomness.
function fulfillRandomness(bytes32 requestId, uint256 randomness) override internal {
// Transform the result to a number between 0 and 100, both included. Using % as modulo
require(randomness!=0, "Modulo zero!");
uint256 d100Value = (randomness % 100) + 1; // +1 so s_results[player] can be 0 if no dice has been rolled
// Assign the transformed value to the address in the s_results mapping variable.
s_results[s_rollers[requestId]] = d100Value;
// Emit a DiceLanded event.
emit DiceLanded(requestId, d100Value);
}
// playerWins determines whether the player wins or lose the battle, based on a fixed chance (0-100)
function playerWins (address player, uint8 chance) internal view returns (bool wins){
require(s_results[player] != 0, "Player has not engaged in battle!");
require(s_results[player] != ROLL_IN_PROGRESS, "Battle in progress!");
return s_results[player] <= (chance + 1); //+1 because dice is 1-101
}
}
RNG call from the importing contract(simplified to relevant part only. _player address is working correctly).
address _player = ownerOf(_monId);
rollDice(_player);
I have the certainty that the error occurs inside the rollDice function, more specifically in the call to requestRandomness. Apart from that, I cannot seem to find why the error is hapenning, nor any references to the error message (Below agreed payment) inside any of the dependency contracts. Cannot find any references online either.
Any help is appreciated, thanks.
Below agreed payment error message comes from the sufficientLINK modifier of the VRFCoordinator.sol contract. You can see it here and here.
Double-check the constructor parameters, especially the fee value.
Also, make sure to fund your smart contract with Rinkeby LINK tokens which you can claim from the faucet.
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
I got a smartcontract function returning several values on of them as string
I've trying to transform this number to UTF with web3 and is not working.
If I try it on remix calling the smart contract it's works fine... but not from JS
my current code in the smart contract are
function getToken(uint256 _tokenId) public view returns (
string _tokenName,
string _cryptoCardID,
uint256 _price,
uint256 _nextPrice,
address _owner
) {
_tokenName = doggies[_tokenId].name;
_cryptoCardID = doggies[_tokenId].cryptoCardID;
_price = tokenIdToPrice[_tokenId];
_nextPrice = nextPriceOf(_tokenId);
_owner = tokenIdToOwner[_tokenId];
}
from JS
for ( var i = 0; i < result; i++){
myfunction.getToken.call( i , function (error, resultGetAll) {
console.log("Token ID:"+i+ "Data:"+JSON.stringify(resultGetAll)); //here i get _cryptoCardID as hex 0x000000...
}) // each one
}// for
what I expected is a string in readable form (utf8)
Contracts will store the binary data, so returning the data in hexadecimal form is a more efficient way of viewing this data.
You can always decode the hex to utf-8 with a utility function from the web3 library.
Note: this method may differ depending on your web3 version, see here for web3.utils.hexToUtf8 alternative.
Example:
$ web3.toUtf8("0x68656c6c6f20776f726c640000000000000000000000000000000000000000")
$ "hello world"
tested on web3 version: 0.20.3
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.