How to get 10000 random numbers from chainlink VRF V2? - random

I want to generate 10000 random numbers by using chainlink VRF V2,but the max NUM_WORDS is 500 in one request,and it fails every time!
What should I do?
My account has enough LINK,but what I have done below does't work!
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "#chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "#chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
contract VRFCoordinatorTest is VRFConsumerBaseV2 {
// Chainlink VRF Variables
address vrfCoordinatorV2 = 0x6168499c0cFfCaCD319c818142124B7A15E857ab;
uint64 subscriptionId = 14422;
bytes32 gasLane = 0xd89b2bf150e3b9e13446986e571fb9cab24b13cea0a43ea20a6049a85cc807cc;
uint32 public callbackGasLimit = 2500000; //already max gaslimit
//network coordinator
VRFCoordinatorV2Interface private immutable _vrfCoordinator;
// The default is 3, but you can set this higher.
uint16 public REQUEST_CONFIRMATIONS = 10;
// retrieve NUM_WORDS random values in one request.
uint32 public NUM_WORDS = 500;
//keep the randomWords from fulfillRandomWords() function.
uint256[][] public _randomWords = new uint256[][](0);
//uint256[] public _randomWords;
event RequestedRandomWords(uint256 requestId ,address requester);
constructor() VRFConsumerBaseV2(vrfCoordinatorV2) {
_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
}
function fulfillRandomWords(uint256, uint256[] memory randomWords) internal override{
_randomWords.push(randomWords);
// _randomWords = randomWords;
}
function requestRandomWords()public{
uint256 requestId = _vrfCoordinator.requestRandomWords(
gasLane,
subscriptionId,
REQUEST_CONFIRMATIONS,
callbackGasLimit,
NUM_WORDS
);
emit RequestedRandomWords(requestId, msg.sender);
}
function set_REQUEST_CONFIRMATIONS(uint16 comf)public{
REQUEST_CONFIRMATIONS = comf;
}
function set_NUM_WORDS(uint32 num)public{
NUM_WORDS = num;
}
function set_gasLimit(uint32 gasl) public{
callbackGasLimit = gasl;
}
function getMaxLengthAndNum()public view returns(uint256,uint256){
uint256 lth = _randomWords.length;
uint256[] memory tmpArray = _randomWords[lth-1];
uint256 lastNum = tmpArray[tmpArray.length-1];
//uint256 maxNum = _randomWords[_randomWords.length-1];
return (lth,lastNum);
}
}

I see that you are testing on Rinkeby. I advise you to test on Goerli. Rinkeby is marked as deprecated (cf. https://docs.chain.link/docs/vrf/v2/supported-networks/).
I've tested your contract and remarked that fulfillRandomWords runs out of gas. In fact, The maximum number of random words that can be received per request is 500, and the maximum gas limit supported for your callback function is 2,500,000. Therefore, you must ensure that the callback function will not run out of gas (meaning: your callback function cannot consume more than 2,500,000 of gas).
In your example, you are trying to store 500 random words. Setting a value from non-zero to zero takes about 20,000 of gas (cf. Ethereum yellow paper Appendix H). With 2,500,000 of gas, you can store up to 125 words. And that’s why the attached contract will not work with NUM_WORDS=500 (fulfillRandomWords runs out of gas).
If you want to store 10001 random values, I’d suggest making multiple requests. For instance: inside requestRandomWords you can loop over
vrfCoordinator.requestRandomWords multiple times (e.g.: with NUM_WORDS=100 for 100 runs. and 1 for the last run).
Your callback function will be called multiple times (=number of requestIds) so make sure to implement a logic that waits for all the requests to be fulfilled (e.g., you can update a state variable s_requestsFulfilled
each time a request is fulfilled).
Here is an example that you can test on Goerli, I modified your example (Important remark: this is a non-audited code with hardcoded values. Do not use it as a reference for production code. Please test it/change it according to your needs)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;
import "#chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "#chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
contract VRFCoordinatorTest_2 is VRFConsumerBaseV2 {
// Chainlink VRF Variables
address vrfCoordinatorV2 = 0x2Ca8E0C643bDe4C2E08ab1fA0da3401AdAD7734D;
uint64 subscriptionId = 443;
bytes32 gasLane = 0x79d3d8832d904592c0bf9818b621522c988bb8b0c05cdc3b15aea1b6e8db0c15;
uint32 public callbackGasLimit = 2500000; //already max gaslimit
uint256 public requestsFulfilled = 0; // count number of request fulfilled
//network coordinator
VRFCoordinatorV2Interface private immutable _vrfCoordinator;
// The default is 3, but you can set this higher.
uint16 public REQUEST_CONFIRMATIONS = 3;
//keep the randomWords from fulfillRandomWords() function.
struct RequestStatus {
bool fulfilled; // whether the request has been successfully fulfilled
bool exists; // whether a requestId exists
uint256[] randomWords;
}
mapping(uint256 => RequestStatus) public s_requests;
uint256[] public requestIds;
//uint256[] public _randomWords;
event ReceivedRandomWords(uint256 requestId ,uint256[] randomWords);
event RequestedRandomWords(uint256 requestId ,address requester);
event AllRequestsFulfilled();
constructor() VRFConsumerBaseV2(vrfCoordinatorV2) {
_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
}
function fulfillRandomWords(uint256 _requestId, uint256[] memory _randomWords) internal override{
require(s_requests[_requestId].exists, 'request not found');
require(!s_requests[_requestId].fulfilled, 'request already fulfilled');
requestsFulfilled++;
s_requests[_requestId].fulfilled = true;
s_requests[_requestId].randomWords = _randomWords;
emit ReceivedRandomWords(_requestId,_randomWords);
if(requestsFulfilled==101){
emit AllRequestsFulfilled();
}
}
function requestRandomWords()public{
uint32 numWords = 100;
for(uint256 i=0;i<101;i++){
if(i==100){
numWords=1;
}
uint256 requestId = _vrfCoordinator.requestRandomWords(
gasLane,
subscriptionId,
REQUEST_CONFIRMATIONS,
callbackGasLimit,
numWords
);
s_requests[requestId]=RequestStatus({randomWords: new uint256[](0), exists: true, fulfilled: false});
requestIds.push(requestId);
emit RequestedRandomWords(requestId, msg.sender);
}
}
function getRequestStatus(uint256 requestId) external view returns (bool fulfilled, uint256[] memory randomWords) {
require(s_requests[requestId].exists, 'request not found');
RequestStatus memory request = s_requests[requestId];
return (request.fulfilled, request.randomWords);
}
function getRandomWordsAt(uint256 requestId,uint32 index) external view returns (uint256) {
require(s_requests[requestId].exists, 'request not found');
RequestStatus memory request = s_requests[requestId];
return request.randomWords[index];
}
function getRequestIdsLength() external view returns (uint256){
return requestIds.length;
}
function getRandomWords(uint256 requestId) external view returns (uint256[] memory){
require(s_requests[requestId].exists, 'request not found');
return s_requests[requestId].randomWords;
}
}

Related

Why is the public key in `.near-credentials` keypairs seemingly irrelevant?

Let's say I create an account foo.near on NEAR with a single full access public key ed25519:publiccc which is matched to a private key ed25519:secrettt and stored locally in the .near-credentials/mainnet/foo.near.json file.
I can now send myself 1 NEAR using near send foo.near foo.near 1 to verify the keys work. If I modify the private key in that keypair at all, the send fails. Good.
Then I replace the public key in that keypair with a nonsense string like ed25519:giggle.
I can still send myself 1 NEAR using the same command. Why?
It appears the public key is totally irrelevant in this keypair. You can use a nonsense string or a public key that's supposed to have limited access only and the private key gives full access to transfer funds.
Good question! I had to do some digging. So let's start with what the CLI is doing behind the scenes. The code is found here:
exports.sendMoney = async function (options) {
await checkCredentials(options.sender, options.networkId, options.keyStore);
console.log(`Sending ${options.amount} NEAR to ${options.receiver} from ${options.sender}`);
const near = await connect(options);
const account = await near.account(options.sender);
const result = await account.sendMoney(options.receiver, utils.format.parseNearAmount(options.amount));
inspectResponse.prettyPrintResponse(result, options);
};
Essentially it connects to the network, creates an account object, and then calls the sendMoney method for that account object which is through near-api-js. The way it's instantiated is through near.account() which behind the scenes does the following (found here):
async account(accountId: string): Promise<Account> {
const account = new Account(this.connection, accountId);
return account;
}
This is just calling the class' constructor passing in the connection and account ID. Up until this point, we have no reference to the private or public key other than the connection which has a keystore indicating WHERE the keypair will be found. Ok moving on.
After the account class is initialized, the sendMoney method is called and the only action is a simple transfer with the amount. The code for this method can be seen here.
async sendMoney(receiverId: string, amount: BN): Promise<FinalExecutionOutcome> {
return this.signAndSendTransaction({
receiverId,
actions: [transfer(amount)]
});
}
You'll notice that it calls the signAndSendTransaction method that is part of the Account Class (found here).
async signAndSendTransaction({ receiverId, actions, returnError }: SignAndSendTransactionOptions): Promise<FinalExecutionOutcome> {
let txHash, signedTx;
// TODO: TX_NONCE (different constants for different uses of exponentialBackoff?)
const result = await exponentialBackoff(TX_NONCE_RETRY_WAIT, TX_NONCE_RETRY_NUMBER, TX_NONCE_RETRY_WAIT_BACKOFF, async () => {
[txHash, signedTx] = await this.signTransaction(receiverId, actions);
const publicKey = signedTx.transaction.publicKey;
try {
return await this.connection.provider.sendTransaction(signedTx);
}
...
...
Out of this method, the important thing is this.signTransaction whose method is found here:
protected async signTransaction(receiverId: string, actions: Action[]): Promise<[Uint8Array, SignedTransaction]> {
const accessKeyInfo = await this.findAccessKey(receiverId, actions);
if (!accessKeyInfo) {
throw new TypedError(`Can not sign transactions for account ${this.accountId} on network ${this.connection.networkId}, no matching key pair exists for this account`, 'KeyNotFound');
}
const { accessKey } = accessKeyInfo;
const block = await this.connection.provider.block({ finality: 'final' });
const blockHash = block.header.hash;
const nonce = accessKey.nonce.add(new BN(1));
return await signTransaction(
receiverId, nonce, actions, baseDecode(blockHash), this.connection.signer, this.accountId, this.connection.networkId
);
}
In this method, there's a sneaky this.findAccessKey being done whose code can be found here.
async findAccessKey(receiverId: string, actions: Action[]): Promise<{ publicKey: PublicKey; accessKey: AccessKeyView }> {
// TODO: Find matching access key based on transaction (i.e. receiverId and actions)
const publicKey = await this.connection.signer.getPublicKey(this.accountId, this.connection.networkId);
if (!publicKey) {
throw new TypedError(`no matching key pair found in ${this.connection.signer}`, 'PublicKeyNotFound');
}
const cachedAccessKey = this.accessKeyByPublicKeyCache[publicKey.toString()];
if (cachedAccessKey !== undefined) {
return { publicKey, accessKey: cachedAccessKey };
}
try {
const rawAccessKey = await this.connection.provider.query<AccessKeyViewRaw>({
request_type: 'view_access_key',
account_id: this.accountId,
public_key: publicKey.toString(),
finality: 'optimistic'
});
// store nonce as BN to preserve precision on big number
const accessKey = {
...rawAccessKey,
nonce: new BN(rawAccessKey.nonce),
};
// this function can be called multiple times and retrieve the same access key
// this checks to see if the access key was already retrieved and cached while
// the above network call was in flight. To keep nonce values in line, we return
// the cached access key.
if (this.accessKeyByPublicKeyCache[publicKey.toString()]) {
return { publicKey, accessKey: this.accessKeyByPublicKeyCache[publicKey.toString()] };
}
this.accessKeyByPublicKeyCache[publicKey.toString()] = accessKey;
return { publicKey, accessKey };
} catch (e) {
if (e.type == 'AccessKeyDoesNotExist') {
return null;
}
throw e;
}
}
If you take a look at this code, you'll notice a really important line:
const publicKey = await this.connection.signer.getPublicKey(this.accountId, this.connection.networkId);
What is this returning? The short answer is that if you're using the NEAR-CLI, the connection.signer comes from UnencryptedFileSystemKeyStore since your keys are stored in .near-credentials. Now let's look at this getPublicKey method found here:
async getPublicKey(accountId?: string, networkId?: string): Promise<PublicKey> {
const keyPair = await this.keyStore.getKey(networkId, accountId);
if (keyPair === null) {
return null;
}
return keyPair.getPublicKey();
}
It does this.keyStore.getKey where the keyStore is the UnencryptedFileSystemKeyStore I mentioned earlier. The getKey method is then found here:
async getKey(networkId: string, accountId: string): Promise<KeyPair> {
// Find key / account id.
if (!await exists(this.getKeyFilePath(networkId, accountId))) {
return null;
}
const accountKeyPair = await readKeyFile(this.getKeyFilePath(networkId, accountId));
return accountKeyPair[1];
}
In this function, the final culprit is the readKeyFile where it returns the key pair (both public and private). This can be found here:
export async function readKeyFile(filename: string): Promise<[string, KeyPair]> {
const accountInfo = await loadJsonFile(filename);
// The private key might be in private_key or secret_key field.
let privateKey = accountInfo.private_key;
if (!privateKey && accountInfo.secret_key) {
privateKey = accountInfo.secret_key;
}
return [accountInfo.account_id, KeyPair.fromString(privateKey)];
}
Notice that it returns a KeyPair that is generated from the private key. This means that no matter what the public key is, it will overwrite it. I did a quick test where I console logged the accountInfo and it indeed returned the incorrect public key that I had set manually but when it generates the KeyPair from the private key, that information is overwritten.
TLDR:
When the your account signs a transaction, the public key is re-generated based on the private key. It doesn't matter what public key you have in your .near-credentials since it is overwritten when the KeyPair is created based on the private key.

Updating an entry in PersistentVector not working NEAR Protocol

I'm trying to update the status of a job object. I get the "success" message return but the value is not updating. Do I miss something?
#nearBindgen
export class Contract {
private jobs: PersistentVector<Job> = new PersistentVector<Job>('jobs');
......
#mutateState()
cancelJob(jobTitle: string): string {
for (let i = 0; i < this.jobs.length; i++) {
if (this.jobs[i].title == jobTitle) {
this.jobs[i].status = "Cancelled";
return "success"
}
}
return "not found";
}
And I'm calling it like that:
near call apptwo.msaudi.testnet cancelJob '{\"jobTitle\":\"title2\"}' --account-id=msaudi.testnet
It’s not enough to update entry when you fetch it. You need to update the storage on the contract as well. Write it back in so to speak.
This isn’t enough
this.jobs[i].status = "Cancelled";
You need to add it back in:
if (this.jobs[i].title == jobTitle) {
const job: Job = this.jobs[i]; // Need an intermediate object in memory
job.status = "Cancelled";
this.jobs.replace(i, job); // Update storage with the new job.
return "success"
}

Chainlink Fullfill_alarm() not calling back after successfull request send to oracle on testing with mocha

I'm trying to test the Chainlink alarm oracle following the documentation steps with hardhat on a kovan fork network.
https://docs.chain.link/docs/chainlink-alarm-clock/#:~:text=ala
When I test the contract with npx hardhat test it looks like it hits the request function to the oracle but never calling back the fulfill_alarm().
I wonder if that might be an issue with the fork network or with my testing code.
I'm using Alchemy api as url for the fork network on kovan.
Also, I'm using a timeout with mocha so it can wait for the callback to execute, but never hits.
The first it statement executes correctly, but then on the second one which is suposed to increment lotteryId + 1 once the lottery is done (fulfill_alarm() called) doesn't pass. It looks like the function never gets executed by the chainlink oracle.
oracle: 0xAA1DC356dc4B18f30C347798FD5379F3D77ABC5b
jobId: '982105d690504c5d9ce374d040c08654'
Solidity code:
//Starting Oracle Alarm
function start_new_lottery(uint256 duration) public {
require(lottery_state == LOTTERY_STATE.CLOSED, "can't start a n
lottery_state = LOTTERY_STATE.OPEN;
Chainlink.Request memory req = buildChainlinkRequest(jobId, add
req.addUint("until", block.timestamp + duration);
sendChainlinkRequestTo(oracle, req, oraclePayment);
console.log('LINK REQ SEND');
}
//Callback Function after Oracle Alarm is Fulfilled
function fulfill_alarm(bytes32 _requestId) public recordChainlinkFu
require(lottery_state == LOTTERY_STATE.OPEN, "The lottery hasn'
lotteryId = lotteryId + 1;
console.log('ALARM_FULFILLED');
pickWinner();
}
test code:
const {expect} = require("chai");
const { time } = require("#openzeppelin/test-helpers");
describe("Lottery Contract", function() {
//Defining global variables for testing.
const price_lottery = ethers.BigNumber.from(1);
let owner;
let Lottery;
let lottery;
before(async function () {
Lottery = await ethers.getContractFactory("Lottery");
[owner] = await ethers.getSigners();
lottery = await Lottery.deploy(
price_lottery,
);
//Starting new Lottery
await lottery.start_new_lottery(60);
});
it("should start a chainlink alarm to init new lottery", async func
let lottery_state = await lottery.lottery_state();
expect(lottery_state).to.equal(0);
//Initial value
let lotteryId = await lottery.lotteryId();
expect(lotteryId).to.equal(1);
})
it("increments lotteryID + 1, when chainlink alarm is fulfilled aft
//Wait until alarm is fulfilled
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
await timeout(120000);
//Expected value
let lotteryId = await lottery.lotteryId();
expect(lotteryId).to.equal(2);
})
})

Solidity event n emit syntex

Hope doing well
I want to do the following activities in solidity code below but dont know how, please advise.
Thanks with regards
Syed
QUERY:_
Emit an event ScoreChanged with two arguments: int amount (equal to the value that was sent) and bool direction (true for up function and false for down)
Emit an event GameEnded after gameOver is switched to true
CODE:
pragma solidity ^0.4.17;
contract TugOfWar {
int public score = 0;
int constant endAt = 1 ether;
bool public gameOver = false;
function up() external payable {
require(msg.value > 0);
require(!gameOver);
int value = int(msg.value);
score += value;
checkIfGameOver();
}
function down() external payable {
require(msg.value > 0);
require(!gameOver);
int value = int(msg.value);
score -= value;
checkIfGameOver();
}
function checkIfGameOver() internal {
if(score >= endAt || score <= endAt * -1) {
gameOver = true;
}
}
}
You can define events in the contract by:
event ScoreChanged(uint amount, bool up);
event GameEnded(bool up); // where 'up' indicates which side won.
to emit the events you write:
emit ScoreChanged(msg.value, true); // where the second argument indicates the direction
you could to this right after you increment or decrement the score.
and when the game has ended:
emit GameEnded(true); // where the argument indicates which side has won.

Exception: argument too large: value Google Script

I'm trying to scrape a website & put the value in cache so I don't hit the daily limit of UrlFetchApp
Here is the script I did:
/**
* Scrape URL, return whatever you choose with jquery-style selectors.
Dependency: cheeriogs, see https://github.com/fgborges/cheeriogs
*
* #param {url} valid start-url
* #return result (array values)
*
* #customfunction
*/
function scrapercache(url) {
var result = [];
var description;
var options = {
'muteHttpExceptions': true,
'followRedirects': false,
};
var cache = CacheService.getScriptCache();
var properties = PropertiesService.getScriptProperties();
try {
let res = cache.get(url);
if (!res) {
// trim url to prevent (rare) errors
url.toString().trim();
var r = UrlFetchApp.fetch(url, options);
var c = r.getResponseCode();
// check for meta refresh if 200 ok
if (c == 200) {
var html = r.getContentText();
cache.put(url, "cached", 21600);
properties.setProperty(url, html);
var $ = Cheerio.load(html); // make sure this lib is added to your project!
// meta description
if ($('meta[name=description]').attr("content")) {
description = $('meta[name=description]').attr("content").trim();
}
}
result.push([description]);
}
}
catch (error) {
result.push(error.toString());
}
finally {
return result;
}
}
but when I call the function like that:
=scrapercache("https://www.gurufocus.com/term/total_freecashflow/nyse:ABBV/Free-Cash-Flow")
I get the error message:
Exception: argument too large: value
Anyone can help me please?
Thank you :)
Gabriel
As written in the official documentation,
The maximum length of a key is 250 characters. The maximum amount of data that can be stored per key is 100KB.
If the size of the data put in cache exceeds any of the above limitations, the error
Exception: argument too large
is shown. In your case, value exceeds 100KB. Solution would be to cache only necessary data or don't cache at all depending on your specific needs.

Resources