Reentrancy attacks in smart contracts, explained