mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			250 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			250 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| ###############
 | |
| 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`.
 | |
| 
 | |
| 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.
 | |
| 
 | |
| .. {% include open_link gist="fe4ef267cbdeac151b98" %}
 | |
| 
 | |
| ::
 | |
| 
 | |
|     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
 | |
|         // done!
 | |
|         modifier costs(uint _amount) {
 | |
|             if (msg.value < _amount)
 | |
|                 throw;
 | |
|             _
 | |
|             if (msg.value > _amount)
 | |
|                 msg.sender.send(_amount - msg.value);
 | |
|         }
 | |
| 
 | |
|         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
 | |
| only be called at a certain stage.
 | |
| 
 | |
| Automatic timed transitions
 | |
| are handled by the modifier `timeTransitions`, which
 | |
| 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
 | |
| 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
 | |
|     to call nextStage manually from those functions.
 | |
| 
 | |
| .. {% include open_link gist="0a221eaceb6d708bf271" %}
 | |
| 
 | |
| ::
 | |
| 
 | |
|     contract StateMachine {
 | |
|         enum Stages {
 | |
|             AcceptingBlindedBids,
 | |
|             RevealBids,
 | |
|             AnotherStage,
 | |
|             AreWeDoneYet,
 | |
|             Finished
 | |
|         }
 | |
|         // This is the current stage.
 | |
|         Stages public stage = Stages.AcceptingBlindedBids;
 | |
| 
 | |
|         uint public creationTime = now;
 | |
| 
 | |
|         modifier atStage(Stages _stage) {
 | |
|             if (stage != _stage) throw;
 | |
|             _
 | |
|         }
 | |
|         function nextStage() internal {
 | |
|             stage = Stages(uint(stage) + 1);
 | |
|         }
 | |
|         // 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
 | |
|         }
 | |
|         
 | |
|         // Order of the modifiers matters here!
 | |
|         function bid()
 | |
|             timedTransitions
 | |
|             atStage(Stages.AcceptingBlindedBids)
 | |
|         {
 | |
|             // We will not implement that here
 | |
|         }
 | |
|         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();
 | |
|         }
 | |
|         function g()
 | |
|             timedTransitions
 | |
|             atStage(Stages.AnotherStage)
 | |
|             transitionNext
 | |
|         {
 | |
|             // If you want to use `return` here,
 | |
|             // you have to call `nextStage()` manually.
 | |
|         }
 | |
|         function h()
 | |
|             timedTransitions
 | |
|             atStage(Stages.AreWeDoneYet)
 | |
|             transitionNext
 | |
|         {
 | |
|         }
 | |
|         function i()
 | |
|             timedTransitions
 | |
|             atStage(Stages.Finished)
 | |
|         {
 | |
|         }
 | |
|     }
 |