Cyfrin / foundry-smart-contract-lottery-cu

47 stars 42 forks source link

Why assert(recentWinner == expectedWinner); #55

Closed Takeroro closed 4 months ago

Takeroro commented 4 months ago

hi, i dont understand , in RaffleTest.t.sol, tfuntion: estFulfillRandomWordsPicksAWinnerResetsAndSendsMoney, line: 275, WHY recentWinner shoud be equal to expectedWinner ? i think the recentWinner should random num in [1,3]

address expectedWinner = address(1);
...
...
assert(recentWinner == expectedWinner);
..

the assert expects that recentWinner will always return address(1); Can bros help me ?

dustinstacy commented 4 months ago

Following Patrick's setup in the Act portion of this test:

    // Act
    vm.recordLogs();
    raffle.performUpkeep("");
    Vm.Log[] memory entries = vm.getRecordedLogs();
    bytes32 requestId = entries[1].topics[1];

    VRFCoordinatorV2_5Mock(vrfCoordinator).fulfillRandomWords(
        uint256(requestId),
        address(lottery)
    );

The requestId we get back from this line will always equal 1:

        bytes32 requestId = entries[1].topics[1];

This is because we start fresh on our local network every time we run forge test so we are always making the first request.

Then we pass it on over to the VRFCoordinatorV2_5Mock as a parameter. Take a dive into the VRFCoordinatorV2_5Mock contract and we see the the fulfillRandomWords() function we call in our test then calls the fulfillRandomWordsWithOverride() function:

    contract VRFCoordinatorV2_5Mock is SubscriptionAPI, IVRFCoordinatorV2Plus {
    ....

    function fulfillRandomWords(uint256 _requestId, address _consumer) external nonReentrant {
        fulfillRandomWordsWithOverride(_requestId, _consumer, new uint256[](0));
    }

    function fulfillRandomWordsWithOverride(uint256 _requestId, address _consumer, uint256[] memory _words) public {
        uint256 startGas = gasleft();
        if (s_requests[_requestId].subId == 0) {
            revert InvalidRequest();
        }
        Request memory req = s_requests[_requestId];

        if (_words.length == 0) {
            _words = new uint256[](req.numWords);
            for (uint256 i = 0; i < req.numWords; i++) {
                _words[i] = uint256(keccak256(abi.encode(_requestId, i)));
                console.log("words: ", _words[i]);
            }
        } else if (_words.length != req.numWords) {
            revert InvalidRandomWords();
        }

    ....

There's a lot happening here and even more so after in the function, but the main take away is that inside the for loop it is generating our "random" words.

When this line inside fulfillRandomWordsOverride() executes during out test:

    _words[i] = uint256(keccak256(abi.encode(_requestId, i)));
   // _words[i] = uint256(keccak256(abi.encode(1, 0))); replaced with actual values

_words[i] always equals 78541660797044910968829902406342334108369226379826116161446442989268089806461 since _requestId is always 1 and i is always 0.

Again some hashing stuff that hasn't been completely fleshed out at this point in the course, but we can return to the fulfillRandomWords() function in our Raffle contract and looking at this line:

    uint256 indexOfWinner = randomWords[0] % s_entrants.length;

And since we know from setting up the test that s_entrants.length = 4 and we see now that randomWords[0] = 78541660797044910968829902406342334108369226379826116161446442989268089806461 we can run the math on that line of code:

78541660797044910968829902406342334108369226379826116161446442989268089806461 % 4 = 1

And we see that we will always get an indexOfWinner equaling 1.

Since we manually entered all our player addresses during the for loop inside our test, we know the the s_players[1] would have address(1). Patrick said he did a little math on this to get to that expectedWinner address so hopefully he can confirm!