PatrickAlphaC / smartcontract-lottery

MIT License
79 stars 113 forks source link

test_can_pick_winner failed: Gas estimation failed: 'execution reverted - minute 8:16:00 #68

Closed tolivern closed 2 years ago

tolivern commented 2 years ago

Hi! Trying to run test_can_pick_winner I get this error:

============================================================ FAILURES ============================================================ __ test_can_pick_winner __

def test_can_pick_winner():
    if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
        pytest.skip()
    lottery = deploy_lottery()
    account = get_account()
    lottery.startLottery({"from": account})
    lottery.enter({"from": account, "value": lottery.getEntranceFee() + 100000000})
    lottery.enter({"from": account, "value": lottery.getEntranceFee() + 100000000})
    # fund_with_link(lottery.address) - NOT NEEDED, I THINK, BECAUSE IT IS CALLED ALREADY IN DEPLOY_LOTTERY
  lottery.endLottery({"from": account})

tests\test_lottery_integration.py:21:


....\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py:1693: in call return self.transact(*args) ....\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py:1566: in transact return tx["from"].transfer( ....\AppData\Roaming\Python\Python310\site-packages\brownie\network\account.py:642: in transfer receipt, exc = self._make_transaction( ....\AppData\Roaming\Python\Python310\site-packages\brownie\network\account.py:725: in _make_transaction raise VirtualMachineError(e) from None


self = VirtualMachineError(ValueError("Gas estimation failed: 'execution reverted'. This transaction will likely revert. If you wish to broadcast, you must set the gas limit manually.")) exc = "Gas estimation failed: 'execution reverted'. This transaction will likely revert. If you wish to broadcast, you must set the gas limit manually."

def __init__(self, exc: ValueError) -> None:
    try:
        exc = exc.args[0]
    except Exception:
        pass

    if isinstance(exc, dict) and "message" in exc:
        if "data" not in exc:
            raise ValueError(exc["message"]) from None

        self.message: str = exc["message"].rstrip(".")

        if isinstance(exc["data"], str):
            # handle parity exceptions - this logic probably is not perfect
            if "0x08c379a0" in exc["data"]:
                revert_type, err_msg = [i.strip() for i in exc["data"].split("0x08c379a0", 1)]
                err_msg = eth_abi.decode_abi(["string"], HexBytes(err_msg))
                err_msg = f"{revert_type} '{err_msg}'"
            elif exc["data"].endswith("0x"):
                err_msg = exc["data"][:-2].strip()
            else:
                err_msg = exc["data"]
            raise ValueError(f"{self.message}: {err_msg}") from None

        try:
            txid, data = next((k, v) for k, v in exc["data"].items() if k.startswith("0x"))
        except StopIteration:
            raise ValueError(exc["message"]) from None

        self.txid: str = txid
        self.source: str = ""
        self.revert_type: str = data["error"]
        self.pc: Optional[str] = data.get("program_counter")
        if self.pc and self.revert_type == "revert":
            self.pc -= 1

        self.revert_msg: Optional[str] = data.get("reason")
        self.dev_revert_msg = brownie.project.build._get_dev_revert(self.pc)
        if self.revert_msg is None and self.revert_type in ("revert", "invalid opcode"):
            self.revert_msg = self.dev_revert_msg
        elif self.revert_msg == "Failed assertion":
            self.revert_msg = self.dev_revert_msg or self.revert_msg

    else:
      raise ValueError(str(exc)) from None

E ValueError: Gas estimation failed: 'execution reverted'. This transaction will likely revert. If you wish to broadcast, you must set the gas limit manually.

There is a problem with endLottery. I have tried a million things and I only get a different error, always there. I have enough eth and link.

This is the code of the test:

test_can_pick_winner

from brownie import network from scripts.helpful_scripts import ( LOCAL_BLOCKCHAIN_ENVIRONMENTS, get_account,

fund_with_link,

) from scripts.deploy_lottery import deploy_lottery import pytest import time

def test_can_pick_winner(): if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS: pytest.skip() lottery = deploy_lottery() account = get_account() lottery.startLottery({"from": account}) lottery.enter({"from": account, "value": lottery.getEntranceFee() + 100000000}) lottery.enter({"from": account, "value": lottery.getEntranceFee() + 100000000})

fund_with_link(lottery.address) - NOT NEEDED, I THINK, BECAUSE IT IS CALLED ALREADY IN DEPLOY_LOTTERY

lottery.endLottery({"from": account})
time.sleep(60)

# como solo ha participado account...
assert lottery.recentWinner() == account
assert lottery.balance() == 0

deploy_lottery.py

from scripts.helpful_scripts import get_account, get_contract, fund_with_link from brownie import Lottery, config, network import time

def deploy_lottery(): account = get_account() lottery = Lottery.deploy( get_contract("eth_usd_price_feed").address, get_contract("vrf_coordinator").address, get_contract("link_token").address, config["networks"][network.show_active()]["fee"], config["networks"][network.show_active()]["keyhash"], {"from": account}, publish_source=config["networks"][network.show_active()].get("verify", False), ) print("Deployed lottery!!") return lottery

def start_lottery(): account = get_account() lottery = Lottery[-1] starting_tx = lottery.startLottery({"from": account}) starting_tx.wait(1) print("The lottery started!")

def enter_lottery(): account = get_account() lottery = Lottery[-1] value = lottery.getEntranceFee() + 100000000 starting_tx = lottery.enter({"from": account, "value": value}) starting_tx.wait(1) print("You entered the lottery!")

def end_lottery(): account = get_account() lottery = Lottery[-1]

fund the lottery with link so that it can call randomness

tx = fund_with_link(lottery.address)
tx.wait(1)
tx2 = lottery.endLottery({"from": account})
tx2.wait(1)
time.sleep(60)
# esperamos a que conteste el fulfillRandomness
print(f"We have a winner!: {lottery.recentWinner()}")

def main(): deploy_lottery() start_lottery() enter_lottery() end_lottery() # DA ERROR EN REQUESTRANDOMNESS, HAY QUE COMENTARLO EN DEVELOPMENT

Lottery.sol

// SPDX-License-Identifier: MIT pragma solidity ^0.6.6;

import "@chainlink/contracts/src/v0.6/interfaces/AggregatorV3Interface.sol"; import "@openzeppelin/contracts/access/Ownable.sol";

//import "@chainlink/contracts/src/v0.6/VRFConsumerBase.sol"; import "C:/Users/teres/demos/smartcontract-lottery/contracts/test/VRFConsumerBase.sol";

contract Lottery is VRFConsumerBase, Ownable { address payable[] public players; address payable public recentWinner; uint256 public randomness; uint256 public usdEntryFee; AggregatorV3Interface internal ethUsdPriceFeed; enum LOTTERY_STATE { OPEN, CLOSED, CALCULATING_WINNER } LOTTERY_STATE public lottery_state; uint256 public fee; bytes32 public keyhash; event RequestedRandomness(bytes32 requestId);

// 0
// 1
// 2

constructor(
    address _priceFeedAddress,
    address _vrfCoordinator,
    address _link,
    uint256 _fee,
    bytes32 _keyhash
) public VRFConsumerBase(_vrfCoordinator, _link) {
    usdEntryFee = 50 * (10**18);
    ethUsdPriceFeed = AggregatorV3Interface(_priceFeedAddress);
    lottery_state = LOTTERY_STATE.CLOSED;
    fee = _fee;
    keyhash = _keyhash;
}

function enter() public payable {
    // $50 minimum
    require(lottery_state == LOTTERY_STATE.OPEN);
    require(msg.value >= getEntranceFee(), "Not enough ETH!");
    players.push(msg.sender);
}

function getEntranceFee() public view returns (uint256) {
    (, int256 price, , , ) = ethUsdPriceFeed.latestRoundData();
    uint256 adjustedPrice = uint256(price) * 10**10; // 18 decimals
    // $50, $2,000 / ETH
    // 50/2,000
    // 50 * 100000 / 2000
    uint256 costToEnter = (usdEntryFee * 10**18) / adjustedPrice;
    return costToEnter;
}

function startLottery() public onlyOwner {
    require(
        lottery_state == LOTTERY_STATE.CLOSED,
        "Can't start a new lottery yet!"
    );
    lottery_state = LOTTERY_STATE.OPEN;
}

function endLottery() public onlyOwner {
    lottery_state = LOTTERY_STATE.CALCULATING_WINNER;
    bytes32 requestId = requestRandomness(keyhash, fee);
    emit RequestedRandomness(requestId);
}

function fulfillRandomness(bytes32 _requestId, uint256 _randomness)
    internal
    override
{
    require(
        lottery_state == LOTTERY_STATE.CALCULATING_WINNER,
        "You aren't there yet!"
    );
    require(_randomness > 0, "random-not-found");
    uint256 indexOfWinner = _randomness % players.length;
    recentWinner = players[indexOfWinner];
    recentWinner.transfer(address(this).balance);
    // Reset
    players = new address payable[](0);
    lottery_state = LOTTERY_STATE.CLOSED;
    randomness = _randomness;
}

}

PLEEEAAAASE, what is going on...?

Conix96 commented 2 years ago

Got the same error...

DavBE commented 2 years ago

This error is happening because your contract is not funded with chainlink tokens.

# fund_with_link(lottery.address) - NOT NEEDED, I THINK, BECAUSE IT IS CALLED ALREADY IN DEPLOY_LOTTERY

You should uncomment this.

tolivern commented 2 years ago

Hi! Ok, I tried that before and it didn´t work either, here it is the error I get. Can you help, please?

def test_can_pick_winner():
    if network.show_active() in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
        pytest.skip()
    lottery = deploy_lottery()
    account = get_account()
    lottery.startLottery({"from": account})
    lottery.enter({"from": account, "value": lottery.getEntranceFee() + 100000000})
    lottery.enter({"from": account, "value": lottery.getEntranceFee() + 100000000})
    fund_with_link(lottery.address)
    # - NOT NEEDED, I THINK, BECAUSE IT IS CALLED ALREADY IN DEPLOY_LOTTERY
  lottery.endLottery({"from": account, "gas_limit": 12000000})

tests\test_lottery_integration.py:22:


....\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py:1693: in call return self.transact(*args) ....\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py:1566: in transact return tx["from"].transfer( ....\AppData\Roaming\Python\Python310\site-packages\brownie\network\account.py:642: in transfer receipt, exc = self._make_transaction( ....\AppData\Roaming\Python\Python310\site-packages\brownie\network\account.py:743: in _make_transaction exc = VirtualMachineError(e)


self = VirtualMachineError(ValueError("Execution reverted during call: 'execution reverted'. This transaction will likely revert. If you wish to broadcast, include allow_revert:True as a transaction parameter.")) exc = "Execution reverted during call: 'execution reverted'. This transaction will likely revert. If you wish to broadcast, include allow_revert:True as a transaction parameter."

def __init__(self, exc: ValueError) -> None:
    try:
        exc = exc.args[0]
    except Exception:
        pass

    if isinstance(exc, dict) and "message" in exc:
        if "data" not in exc:
            raise ValueError(exc["message"]) from None

        self.message: str = exc["message"].rstrip(".")

        if isinstance(exc["data"], str):
            # handle parity exceptions - this logic probably is not perfect
            if "0x08c379a0" in exc["data"]:
                revert_type, err_msg = [i.strip() for i in exc["data"].split("0x08c379a0", 1)]
                err_msg = eth_abi.decode_abi(["string"], HexBytes(err_msg))
                err_msg = f"{revert_type} '{err_msg}'"
            elif exc["data"].endswith("0x"):
                err_msg = exc["data"][:-2].strip()
            else:
                err_msg = exc["data"]
            raise ValueError(f"{self.message}: {err_msg}") from None

        try:
            txid, data = next((k, v) for k, v in exc["data"].items() if k.startswith("0x"))
        except StopIteration:
            raise ValueError(exc["message"]) from None

        self.txid: str = txid
        self.source: str = ""
        self.revert_type: str = data["error"]
        self.pc: Optional[str] = data.get("program_counter")
        if self.pc and self.revert_type == "revert":
            self.pc -= 1

        self.revert_msg: Optional[str] = data.get("reason")
        self.dev_revert_msg = brownie.project.build._get_dev_revert(self.pc)
        if self.revert_msg is None and self.revert_type in ("revert", "invalid opcode"):
            self.revert_msg = self.dev_revert_msg
        elif self.revert_msg == "Failed assertion":
            self.revert_msg = self.dev_revert_msg or self.revert_msg

    else:
      raise ValueError(str(exc)) from None

E ValueError: Execution reverted during call: 'execution reverted'. This transaction will likely revert. If you wish to broadcast, include allow_revert:True as a transaction parameter.

tolivern commented 2 years ago

By the way, in my unit tests when I try to run in development it does not like my endLottery function either, this time for a different reason. There is something wrong there:

def test_can_pick_winner_correctly():

arrange

    if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS:
        pytest.skip()
    lottery = deploy_lottery()
    account = get_account()
    lottery.startLottery({"from": account})
    lottery.enter({"from": account, "value": lottery.getEntranceFee()})
    lottery.enter({"from": get_account(index=1), "value": lottery.getEntranceFee()})
    lottery.enter({"from": get_account(index=2), "value": lottery.getEntranceFee()})
    fund_with_link(lottery.address)
  transaction = lottery.endLottery({"from": account})

tests\test_lottery_unit.py:95:


....\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py:1693: in call return self.transact(*args) ....\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py:1566: in transact return tx["from"].transfer( ....\AppData\Roaming\Python\Python310\site-packages\brownie\network\account.py:680: in transfer receipt._raise_if_reverted(exc)


self = <Transaction '0x9cc53d5c22fcee6413c2bdf3de01b9b55ee8791e3cce510cc20ded262ea96ac7'> exc = VirtualMachineError(ValueError({'message': 'VM Exception while processing transaction: revert', 'code': -32000, 'data'...unMicrotasks ()\n at processTicksAndRejections (node:internal/process/task_queues:96:5)', 'name': 'c'}}))

def _raise_if_reverted(self, exc: Any) -> None:
    if self.status or CONFIG.mode == "console":
        return
    if not web3.supports_traces:
        # if traces are not available, do not attempt to determine the revert reason
        raise exc or ValueError("Execution reverted")

    if self._dev_revert_msg is None:
        # no revert message and unable to check dev string - have to get trace
        self._expand_trace()
    if self.contract_address:
        source = ""
    elif CONFIG.argv["revert"]:
        source = self._traceback_string()
    else:
        source = self._error_string(1)
        contract = state._find_contract(self.receiver)
        if contract:
            marker = "//" if contract._build["language"] == "Solidity" else "#"
            line = self._traceback_string().split("\n")[-1]
            if marker + " dev: " in line:
                self._dev_revert_msg = line[line.index(marker) + len(marker) : -5].strip()
  raise exc._with_attr(

source=source, revert_msg=self._revert_msg, dev_revert_msg=self._dev_revert_msg ) E brownie.exceptions.VirtualMachineError: revert E Trace step -1, program counter 2469: E File "C:/Users/teres/demos/smartcontract-lottery/contracts/test/VRFConsumerBase.sol", lines 167-168, in VRFConsumerBase.requestRandomness: E _fee, E abi.encode(_keyHash, USER_SEED_PLACEHOLDER) E ); E // This is the seed passed to VRFCoordinator. The oracle will mix this with E // the hash of the block containing this request to obtain the seed/input E // which is finally passed to the VRF cryptographic machinery. E uint256 vRFSeed = makeVRFInputSeed( E _keyHash,

....\AppData\Roaming\Python\Python310\site-packages\brownie\network\transaction.py:420: VirtualMachineError

DavBE commented 2 years ago

Does the account have link tokens ? You can get some here : https://faucets.chain.link/

tolivern commented 2 years ago

Yes, yes, sure, I have 30 LINK. I am going crazy with this, I think I am going to skip this part and go on with the course. My endLottery is not working in development or rinkeby, and the code is the same as Patrick´s. There is something wrong somewhere else (VRF, etc...). Thank you!

DavBE commented 2 years ago

Does the account have enough eth ?

tolivern commented 2 years ago

Yes, also full of eth...

tolivern commented 2 years ago

My last attempt, this time to deploy the contract in rinkeby, just to see what happens. It does not like it either, a different error XDDD

PS C:\Users\teres\demos\smartcontract-lottery> brownie run scripts/deploy_lottery.py --network rinkeby INFORMACIÓN: no se pudo encontrar ningún archivo para los patrones dados. Brownie v1.16.4 - Python development framework for Ethereum

SmartcontractLotteryProject is the active project.

Running 'scripts\deploy_lottery.py::main'... Transaction sent: 0x9cc54bf4406fbc8770bbc19cf3ff8ca3b159b05f126abb14be03fd369db473fe Gas price: 2.05018293 gwei Gas limit: 995904 Nonce: 113 Lottery.constructor confirmed Block: 10661937 Gas used: 905368 (90.91%) Lottery deployed at: 0xfEdBe638389337975A40a4D289109Dc154cB3D02

Waiting for https://api-rinkeby.etherscan.io/api to process contract... Verification submitted successfully. Waiting for result... Verification pending... Verification pending... Verification pending... Verification pending... File "C:\Users\teres\AppData\Roaming\Python\Python310\site-packages\brownie_cli\run.py", line 49, in main return_value, frame = run( File "C:\Users\teres\AppData\Roaming\Python\Python310\site-packages\brownie\project\scripts.py", line 103, in run return_value = f_locals[method_name](*args, kwargs) File ".\scripts\deploy_lottery.py", line 52, in main deploy_lottery() File ".\scripts\deploy_lottery.py", line 8, in deploy_lottery lottery = Lottery.deploy( File "C:\Users\teres\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py", line 600, in call return tx["from"].deploy( File "C:\Users\teres\AppData\Roaming\Python\Python310\site-packages\brownie\network\account.py", line 555, in deploy contract.publish_source(deployed_contract, silent=silent) File "C:\Users\teres\AppData\Roaming\Python\Python310\site-packages\brownie\network\contract.py", line 515, in publish_source response = requests.get(url, params=params_status, headers=REQUEST_HEADERS) File "C:\Users\teres\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\api.py", line 75, in get return request('get', url, params=params, kwargs) File "C:\Users\teres\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\api.py", line 61, in request return session.request(method=method, url=url, kwargs) File "C:\Users\teres\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\sessions.py", line 542, in request resp = self.send(prep, send_kwargs) File "C:\Users\teres\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\sessions.py", line 655, in send r = adapter.send(request, **kwargs) File "C:\Users\teres\AppData\Local\Programs\Python\Python310\lib\site-packages\requests\adapters.py", line 498, in send raise ConnectionError(err, request=request)

DavBE commented 2 years ago

Try disabling verification and see if the rest is ok ?

tolivern commented 2 years ago

I give up, I have decided to move on to the next part of the tutorial, I have wasted 4 days trying all sort of things :)

Thanks for your help, see you!

PatrickAlphaC commented 2 years ago

@tolivern sorry it didn't work :( If you make this issue on the full repo you might get more help too!

Great work helping @DavBE !

damdafayton commented 2 years ago

EDITED: (removed external link)

PatrickAlphaC commented 2 years ago

Looks like this has been solved here: https://github.com/smartcontractkit/full-blockchain-solidity-course-py/issues/1493