I want to answer my own question with what I've learned since.
Using Blockchain Data
Contracts using the blockchain's record of ether sent to its address via e.g. this.balance
open themselves up to attack.
My comment above and the answer by Foxxxey allude to this in that code that is contingent on the contract's balance matching any internal record of the ether sent to it can be made to misbehave by having ether "forcibly" sent to the contract address via unusual means.
However, it's not just checks, but more subtle bugs may exist when this.balance
conspires with other code, or where code depends on exact values of a contract's balance, or even where unchecked math operations may over or underflow a value.
It seems to me that because ether can be sent to a contract without invoking contract code, that it is actually safer NOT to rely upon or indeed use the blockchain's balance data at all, but only your own thoroughly checked code.
Pots
mapping(address => uint256) balances;
This is needed for internal accounting: it's a form of sub-account. I think it's better to call the field pots
as it sets up a more concrete mental model.
Without a concept of pots it's impossible to record who's money you're spending, if your contract has such a need.
The blockchain records facts about the ether that has been transferred between addresses, so it's reasonable to conclude that you can use the official record to query the "balance" a sender has with your contract.
However, if your contract spends ether, then who's ether is it spending?
It's possible that your design is simply that all ether is pooled and spending is from the pool. But if not, then you must add to and subtract from the balance of everyone's pot.
I'm not sure whether this is obvious to beginners, i.e. that the blockchain has no built-in concept of sub-accounts or "pots", and why should it. Example contracts often don't include spending code and so tracking who sent what appears strange given that this is the fundamental job of the blockchain itself.
If your contract only ever receives ether from an address and sends it back to the same address, and never anywhere else, then I would argue that the mapping
field is superfluous, since the blockchain should know the total ether sent from x
to y
and from y
to x
with the balance being the difference. I'm not sure what kind of contract would do such a thing.