solidity/docs/common-patterns.rst

256 lines
7.2 KiB
ReStructuredText
Raw Normal View History

2015-12-07 20:16:25 +00:00
###############
Common Patterns
###############
.. index:: access;restricting
******************
Restricting Access
******************
Restricting access is a common pattern for contracts.
Note that you can never restrict any human or computer
from reading the content of your transactions or
your contract's state. You can make it a bit harder
by using encryption, but if your contract is supposed
to read the data, so will everyone else.
You can restrict read access to your contract's state
by **other contracts**. That is actually the default
unless you declare make your state variables ``public``.
2015-12-07 20:16:25 +00:00
Furthermore, you can restrict who can make modifications
to your contract's state or call your contract's
functions and this is what this page is about.
.. index:: function;modifier
The use of **function modifiers** makes these
restrictions highly readable.
::
contract AccessRestriction {
// These will be assigned at the construction
// phase, where `msg.sender` is the account
// creating this contract.
address public owner = msg.sender;
uint public creationTime = now;
// Modifiers can be used to change
// the body of a function.
// If this modifier is used, it will
// prepend a check that only passes
// if the function is called from
// a certain address.
modifier onlyBy(address _account)
{
if (msg.sender != _account)
throw;
// Do not forget the "_"! It will
// be replaced by the actual function
// body when the modifier is invoked.
_
}
/// Make `_newOwner` the new owner of this
/// contract.
function changeOwner(address _newOwner)
onlyBy(owner)
{
owner = _newOwner;
}
modifier onlyAfter(uint _time) {
if (now < _time) throw;
_
}
/// Erase ownership information.
/// May only be called 6 weeks after
/// the contract has been created.
function disown()
onlyBy(owner)
onlyAfter(creationTime + 6 weeks)
{
delete owner;
}
// This modifier requires a certain
// fee being associated with a function call.
// If the caller sent too much, he or she is
// refunded, but only after the function body.
// This is dangerous, because if the function
// uses `return` explicitly, this will not be
2016-08-16 14:26:57 +00:00
// done! This behavior will be fixed in Version 0.4.0.
2015-12-07 20:16:25 +00:00
modifier costs(uint _amount) {
if (msg.value < _amount)
throw;
_
if (msg.value > _amount)
2016-08-16 14:26:57 +00:00
msg.sender.send(msg.value - _amount);
2015-12-07 20:16:25 +00:00
}
function forceOwnerChange(address _newOwner)
costs(200 ether)
{
owner = _newOwner;
// just some example condition
if (uint(owner) & 0 == 1)
// in this case, overpaid fees will not
// be refunded
return;
// otherwise, refund overpaid fees
}
}
A more specialised way in which access to function
calls can be restricted will be discussed
in the next example.
.. index:: state machine
*************
State Machine
*************
Contracts often act as a state machine, which means
that they have certain **stages** in which they behave
differently or in which different functions can
be called. A function call often ends a stage
and transitions the contract into the next stage
(especially if the contract models **interaction**).
It is also common that some stages are automatically
reached at a certain point in **time**.
An example for this is a blind auction contract which
starts in the stage "accepting blinded bids", then
transitions to "revealing bids" which is ended by
"determine auction autcome".
.. index:: function;modifier
Function modifiers can be used in this situation
to model the states and guard against
incorrect usage of the contract.
Example
=======
In the following example,
the modifier ``atStage`` ensures that the function can
2015-12-07 20:16:25 +00:00
only be called at a certain stage.
Automatic timed transitions
are handled by the modifier ``timeTransitions``, which
2015-12-07 20:16:25 +00:00
should be used for all functions.
.. note::
**Modifier Order Matters**.
If atStage is combined
with timedTransitions, make sure that you mention
it after the latter, so that the new stage is
taken into account.
Finally, the modifier ``transitionNext`` can be used
2015-12-07 20:16:25 +00:00
to automatically go to the next stage when the
function finishes.
.. note::
**Modifier May be Skipped**.
Since modifiers are applied by simply replacing
code and not by using a function call,
the code in the transitionNext modifier
can be skipped if the function itself uses
return. If you want to do that, make sure
2016-08-16 14:26:57 +00:00
to call nextStage manually from those functions.
With version 0.4.0 (unreleased), modifier code
will run even if the function explicitly returns.
2015-12-07 20:16:25 +00:00
::
contract StateMachine {
enum Stages {
AcceptingBlindedBids,
RevealBids,
AnotherStage,
AreWeDoneYet,
Finished
}
2016-05-18 15:05:28 +00:00
2015-12-07 20:16:25 +00:00
// This is the current stage.
Stages public stage = Stages.AcceptingBlindedBids;
uint public creationTime = now;
modifier atStage(Stages _stage) {
if (stage != _stage) throw;
_
}
2016-05-18 15:05:28 +00:00
2015-12-07 20:16:25 +00:00
function nextStage() internal {
stage = Stages(uint(stage) + 1);
}
2016-05-18 15:05:28 +00:00
2015-12-07 20:16:25 +00:00
// Perform timed transitions. Be sure to mention
// this modifier first, otherwise the guards
// will not take the new stage into account.
modifier timedTransitions() {
if (stage == Stages.AcceptingBlindedBids &&
now >= creationTime + 10 days)
nextStage();
if (stage == Stages.RevealBids &&
now >= creationTime + 12 days)
nextStage();
// The other stages transition by transaction
_
2015-12-07 20:16:25 +00:00
}
2016-05-13 14:32:35 +00:00
2015-12-07 20:16:25 +00:00
// Order of the modifiers matters here!
function bid()
timedTransitions
atStage(Stages.AcceptingBlindedBids)
{
// We will not implement that here
}
2016-05-18 15:05:28 +00:00
2015-12-07 20:16:25 +00:00
function reveal()
timedTransitions
atStage(Stages.RevealBids)
{
}
// This modifier goes to the next stage
// after the function is done.
// If you use `return` in the function,
// `nextStage` will not be called
// automatically.
modifier transitionNext()
{
_
nextStage();
}
2016-05-18 15:05:28 +00:00
2015-12-07 20:16:25 +00:00
function g()
timedTransitions
atStage(Stages.AnotherStage)
transitionNext
{
// If you want to use `return` here,
// you have to call `nextStage()` manually.
}
2016-05-18 15:05:28 +00:00
2015-12-07 20:16:25 +00:00
function h()
timedTransitions
atStage(Stages.AreWeDoneYet)
transitionNext
{
}
2016-05-18 15:05:28 +00:00
2015-12-07 20:16:25 +00:00
function i()
timedTransitions
atStage(Stages.Finished)
{
}
}