As a developer with both a bit of a background on other distributed ledger systems as well as working with Elixir/the BEAM VM which can be considered the ‘OG’ Actor Model implementation, I am wondering what approaches Motoko takes to prevent reentrancy-attacks.
Essentially, my current understanding of Motoko is:
- Invoking a function on a different actor/canister is always asynchronous. (a ‘cast’ in BEAM parlance).
- If we are interested in the result of this invocation (a ‘call’ in BEAM parlance), we use
await
. - Code is transformed into a continuation-passing-style under the hood.
Consider:
- a function
a
on actor A (containing both data and code) calls (and awaits) actor B’sb
. -
b
internally calls (and awaits) another functiona2
of actor A that mutates A’s state. -
a2
finishes;b
resumes. -
b
finishes;a
resumes. - unless the programmer was extremely careful, A’s state might be different from what the rest of
a
expected, resulting in e.g. A locking up or showing unintended behaviour.
Motoko takes a lot of care to prevent/reduce other kinds of programming errors with its type system, sane defaults etc.
Are there (currently or planned) any ways of preventing (or reducing the likelyhood of) reentrancy-attacks in Motoko’s design?
On e.g. the BEAM, this is resolved by all actors having an actual mailbox which serializes the order in which a single actor handles incoming invocations. This prevents reentrancy by a (synchronious) call-sequence like shown above ‘timing out’ (since A is waiting for B, B does not get a response when attempting to invoke a2
).
I’m not sure if that kind of solution (although it introduces its own peculiarities) would be possible to employ in the case of Motoko since ‘time’ is a bit of a vague and malleable concept when talking about a decentralized Internet Computer.
What are you thoughts on this?