Skip to content

Commit

Permalink
tests: vyper: fix compilation failure
Browse files Browse the repository at this point in the history
  Testing vyper integration of /opt/hostedtoolcache/Python/3.12.7/x64/bin/crytic-compile
  ERROR:CryticCompile:missing type annotation

    (hint: did you mean something like `for i: uint256 in ...`?)

    line 111:4
         110     refund: uint256 = 0
    ---> 111     for i in range(MAX_BIDS):
    -------------^
         112         # Note that loop may break sooner than 128 iterations if i >= _numBids

  ERROR:CryticCompile:Vyper compilation errored
  vyper test failed
  • Loading branch information
elopez committed Oct 26, 2024
1 parent de7ae2c commit aba3c12
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 2 deletions.
10 changes: 8 additions & 2 deletions scripts/ci_test_vyper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@

pip install vyper

if vyper --version | grep -q 0.3; then
TEST_FILE=auction-0.3.vy
else
TEST_FILE=auction-0.4.vy
fi

echo "Testing vyper integration of $(realpath "$(which crytic-compile)")"

cd tests/vyper || exit 255

if ! crytic-compile auction.vy --export-formats standard
if ! crytic-compile "$TEST_FILE" --export-formats standard
then echo "vyper test failed" && exit 255
else echo "vyper test passed" && exit 0
fi
fi
File renamed without changes.
178 changes: 178 additions & 0 deletions tests/vyper/auction-0.4.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# Taken from https://github.com/vyperlang/vyper/blob/9136169468f317a53b4e7448389aa315f90b95ba/examples/auctions/blind_auction.vy
# Blind Auction. Adapted to Vyper from [Solidity by Example](https://github.com/ethereum/solidity/blob/develop/docs/solidity-by-example.rst#blind-auction-1)

struct Bid:
blindedBid: bytes32
deposit: uint256

# Note: because Vyper does not allow for dynamic arrays, we have limited the
# number of bids that can be placed by one address to 128 in this example
MAX_BIDS: constant(int128) = 128

# Event for logging that auction has ended
event AuctionEnded:
highestBidder: address
highestBid: uint256

# Auction parameters
beneficiary: public(address)
biddingEnd: public(uint256)
revealEnd: public(uint256)

# Set to true at the end of auction, disallowing any new bids
ended: public(bool)

# Final auction state
highestBid: public(uint256)
highestBidder: public(address)

# State of the bids
bids: HashMap[address, Bid[128]]
bidCounts: HashMap[address, int128]

# Allowed withdrawals of previous bids
pendingReturns: HashMap[address, uint256]


# Create a blinded auction with `_biddingTime` seconds bidding time and
# `_revealTime` seconds reveal time on behalf of the beneficiary address
# `_beneficiary`.
@external
def __init__(_beneficiary: address, _biddingTime: uint256, _revealTime: uint256):
self.beneficiary = _beneficiary
self.biddingEnd = block.timestamp + _biddingTime
self.revealEnd = self.biddingEnd + _revealTime


# Place a blinded bid with:
#
# _blindedBid = keccak256(concat(
# convert(value, bytes32),
# convert(fake, bytes32),
# secret)
# )
#
# The sent ether is only refunded if the bid is correctly revealed in the
# revealing phase. The bid is valid if the ether sent together with the bid is
# at least "value" and "fake" is not true. Setting "fake" to true and sending
# not the exact amount are ways to hide the real bid but still make the
# required deposit. The same address can place multiple bids.
@external
@payable
def bid(_blindedBid: bytes32):
# Check if bidding period is still open
assert block.timestamp < self.biddingEnd

# Check that payer hasn't already placed maximum number of bids
numBids: int128 = self.bidCounts[msg.sender]
assert numBids < MAX_BIDS

# Add bid to mapping of all bids
self.bids[msg.sender][numBids] = Bid({
blindedBid: _blindedBid,
deposit: msg.value
})
self.bidCounts[msg.sender] += 1


# Returns a boolean value, `True` if bid placed successfully, `False` otherwise.
@internal
def placeBid(bidder: address, _value: uint256) -> bool:
# If bid is less than highest bid, bid fails
if (_value <= self.highestBid):
return False

# Refund the previously highest bidder
if (self.highestBidder != empty(address)):
self.pendingReturns[self.highestBidder] += self.highestBid

# Place bid successfully and update auction state
self.highestBid = _value
self.highestBidder = bidder

return True


# Reveal your blinded bids. You will get a refund for all correctly blinded
# invalid bids and for all bids except for the totally highest.
@external
def reveal(_numBids: int128, _values: uint256[128], _fakes: bool[128], _secrets: bytes32[128]):
# Check that bidding period is over
assert block.timestamp > self.biddingEnd

# Check that reveal end has not passed
assert block.timestamp < self.revealEnd

# Check that number of bids being revealed matches log for sender
assert _numBids == self.bidCounts[msg.sender]

# Calculate refund for sender
refund: uint256 = 0
for i: uint256 in range(MAX_BIDS):
# Note that loop may break sooner than 128 iterations if i >= _numBids
if (i >= _numBids):
break

# Get bid to check
bidToCheck: Bid = (self.bids[msg.sender])[i]

# Check against encoded packet
value: uint256 = _values[i]
fake: bool = _fakes[i]
secret: bytes32 = _secrets[i]
blindedBid: bytes32 = keccak256(concat(
convert(value, bytes32),
convert(fake, bytes32),
secret
))

# Bid was not actually revealed
# Do not refund deposit
assert blindedBid == bidToCheck.blindedBid

# Add deposit to refund if bid was indeed revealed
refund += bidToCheck.deposit
if (not fake and bidToCheck.deposit >= value):
if (self.placeBid(msg.sender, value)):
refund -= value

# Make it impossible for the sender to re-claim the same deposit
zeroBytes32: bytes32 = empty(bytes32)
bidToCheck.blindedBid = zeroBytes32

# Send refund if non-zero
if (refund != 0):
send(msg.sender, refund)


# Withdraw a bid that was overbid.
@external
def withdraw():
# Check that there is an allowed pending return.
pendingAmount: uint256 = self.pendingReturns[msg.sender]
if (pendingAmount > 0):
# If so, set pending returns to zero to prevent recipient from calling
# this function again as part of the receiving call before `transfer`
# returns (see the remark above about conditions -> effects ->
# interaction).
self.pendingReturns[msg.sender] = 0

# Then send return
send(msg.sender, pendingAmount)


# End the auction and send the highest bid to the beneficiary.
@external
def auctionEnd():
# Check that reveal end has passed
assert block.timestamp > self.revealEnd

# Check that auction has not already been marked as ended
assert not self.ended

# Log auction ending and set flag
log AuctionEnded(self.highestBidder, self.highestBid)
self.ended = True

# Transfer funds to beneficiary
send(self.beneficiary, self.highestBid)

0 comments on commit aba3c12

Please sign in to comment.