Require a minimum amount of 1 Mana per bet. (#273)

This is a hacky patch for a problem that runs more deeply: the use of
floating-point for Mana calculations, along with the associated rounding
errors.

Consider the following example:

```typescript
const balance = 1000
const bet = 5.6e-14
const newBalance = balance - bet
if (newBalance == balance) {
  alert(`I placed a bet of ${bet} without changing my balance.`)
}
```

This will actually print a message, because floating-point numbers can
only represent so many digits, and thus we get 1000.0 rather than
999.99999999999994.

This is a purely theoretical attack at this point; nobody could create
enough pico-bets to actually affect their balance using this technique.
However, I believe is worth ensuring a minimum bet amount, and might
prevent other problems such as the UI showing messages like "User Foo
bought M0 of YES", which could confuse users.

For a more definite solution, we would probably need to change some
computation to integers, where addition is always commutative and
value-preserving. This is similar to what most financial software does
(e.g., Bitcoin uses 1 Satoshi = 0.00000001 BTC as their unit).
This commit is contained in:
Jonas Wagner 2022-05-22 23:34:18 +02:00 committed by GitHub
parent 73d538c7cf
commit 2a5b68977b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -18,7 +18,7 @@ export const placeBet = newEndpoint(['POST'], async (req, _res) => {
const [bettor, _privateUser] = await lookupUser(await parseCredentials(req)) const [bettor, _privateUser] = await lookupUser(await parseCredentials(req))
const { amount, outcome, contractId, value } = req.body || {} const { amount, outcome, contractId, value } = req.body || {}
if (amount <= 0 || isNaN(amount) || !isFinite(amount)) if (amount < 1 || isNaN(amount) || !isFinite(amount))
throw new APIError(400, 'Invalid amount') throw new APIError(400, 'Invalid amount')
if (outcome !== 'YES' && outcome !== 'NO' && isNaN(+outcome)) if (outcome !== 'YES' && outcome !== 'NO' && isNaN(+outcome))