├── .gitattributes ├── Maintenance ├── Satellite │ ├── Satellite.sol │ └── Base.sol ├── ProxyDelegate │ ├── LibInterface.sol │ ├── TheContract.sol │ ├── DispatcherStorage.sol │ ├── Migrations.sol │ └── Dispatcher.sol ├── Segregation │ ├── Logic.sol │ ├── DataSegregation.sol │ └── DataStorage.sol ├── Relay.sol └── Register.sol ├── Lifecycle ├── Mortal.sol ├── AutomaticDeprecation2.sol └── AutomaticDeprecation.sol ├── Security ├── RateLimit.sol ├── Mutex.sol ├── LimitBalance.sol ├── EmergencyStop.sol ├── ChecksEffectsInteraction.sol └── SpeedBump.sol ├── ActionAndControl ├── SendingFundsAntipattern.sol ├── Oracle │ ├── OracleConsumer.sol │ └── Oracle.sol ├── SendingFunds.sol ├── Randomness.sol ├── StateMachine.sol └── CommitReveal.sol ├── README.md └── Authorization ├── Ownership.sol └── AccessRestriction.sol /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /Maintenance/Satellite/Satellite.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Satellite { 4 | function calculateVariable() public pure returns (uint) { 5 | // calculate var 6 | return 2 * 3; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Maintenance/ProxyDelegate/LibInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | library LibInterface { 4 | struct S { uint i; } 5 | 6 | function getUint(S storage s) public view returns (uint); 7 | function setUint(S storage s, uint i) public; 8 | } -------------------------------------------------------------------------------- /Lifecycle/Mortal.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "../authorization/Ownership.sol"; 4 | 5 | contract Mortal is Owned { 6 | function destroy() public onlyOwner { 7 | selfdestruct(owner); 8 | } 9 | 10 | function destroyAndSend(address recipient) public onlyOwner { 11 | selfdestruct(recipient); 12 | } 13 | } -------------------------------------------------------------------------------- /Security/RateLimit.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract RateLimit { 4 | uint enabledAt = now; 5 | 6 | modifier enabledEvery(uint _t) { 7 | if (now >= enabledAt) { 8 | enabledAt = now + _t; 9 | _; 10 | } 11 | } 12 | 13 | function f() public enabledEvery(1 minutes) { 14 | // some code 15 | } 16 | } -------------------------------------------------------------------------------- /Security/Mutex.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Mutex { 4 | bool locked; 5 | 6 | modifier noReentrancy() { 7 | require(!locked); 8 | locked = true; 9 | _; 10 | locked = false; 11 | } 12 | 13 | function f() noReentrancy public returns (uint) { 14 | require(msg.sender.call()); 15 | return 1; 16 | } 17 | } -------------------------------------------------------------------------------- /Maintenance/ProxyDelegate/TheContract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./LibInterface.sol"; 4 | 5 | contract TheContract { 6 | LibInterface.S s; 7 | 8 | using LibInterface for LibInterface.S; 9 | 10 | function get() public view returns (uint) { 11 | return s.getUint(); 12 | } 13 | 14 | function set(uint i) public { 15 | return s.setUint(i); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Security/LimitBalance.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract LimitBalance { 4 | uint256 public limit; 5 | 6 | constructor(uint256 _value) public { 7 | limit = _value; 8 | } 9 | 10 | modifier limitedPayable() { 11 | require(this.balance <= limit); 12 | _; 13 | } 14 | 15 | function deposit() public payable limitedPayable { 16 | // some code 17 | } 18 | } -------------------------------------------------------------------------------- /Lifecycle/AutomaticDeprecation2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract AutoDeprecate { 4 | uint public constant BLOCK_NUMBER = 4400000; 5 | 6 | modifier isActive() { 7 | require(block.number <= BLOCK_NUMBER); 8 | _; 9 | } 10 | 11 | function deposit() public isActive() { 12 | // some code 13 | } 14 | 15 | function withdraw() public { 16 | // some code 17 | } 18 | } -------------------------------------------------------------------------------- /Maintenance/ProxyDelegate/DispatcherStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "zeppelin-solidity/contracts/ownership/Ownable.sol"; 4 | 5 | contract DispatcherStorage is Ownable { 6 | address public lib; 7 | 8 | function DispatcherStorage(address newLib) public { 9 | replace(newLib); 10 | } 11 | 12 | function replace(address newLib) public onlyOwner /* onlyDAO */ { 13 | lib = newLib; 14 | } 15 | } -------------------------------------------------------------------------------- /ActionAndControl/SendingFundsAntipattern.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract Auction { 4 | address public highestBidder; 5 | uint highestBid; 6 | 7 | function bid() public payable { 8 | require(msg.value >= highestBid); 9 | if (highestBidder != 0) { 10 | highestBidder.transfer(highestBid); 11 | } 12 | highestBidder = msg.sender; 13 | highestBid = msg.value; 14 | } 15 | } -------------------------------------------------------------------------------- /Maintenance/Segregation/Logic.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./DataStorage.sol"; 4 | 5 | contract Logic { 6 | DataStorage dataStorage; 7 | 8 | constructor(address _address) public { 9 | dataStorage = DataStorage(_address); 10 | } 11 | 12 | function f() public { 13 | bytes32 key = keccak256("emergency"); 14 | dataStorage.setUintValue(key, 911); 15 | dataStorage.getUintValue(key); 16 | } 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Solidity Design Patterns [WIP] 2 | 3 | Design Pattern Groups 4 | 5 | - Action and Control 6 | - Authorization 7 | - Lifecycle 8 | - Maintenance 9 | - Security 10 | 11 | # Reference 12 | - [Design Patterns for Smart Contracts in the Ethereum Ecosystem](https://eprints.cs.univie.ac.at/5665/1/bare_conf.pdf) 13 | - [Smart Contracts: Security Patterns in the Ethereum Ecosystem and Solidity](https://eprints.cs.univie.ac.at/5433/7/sanerws18iwbosemain-id1-p-380f58e-35576-preprint.pdf) 14 | 15 | -------------------------------------------------------------------------------- /ActionAndControl/Oracle/OracleConsumer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./Oracle.sol"; 4 | 5 | contract OracleConsumer { 6 | Oracle oracle = Oracle(0x123...); 7 | 8 | modifier onlyBy(address _account) { 9 | require(msg.sender == _account); _; 10 | } 11 | 12 | function updateExchangeRate() { 13 | oracle.query("USD", this.oracleResponse); 14 | } 15 | 16 | function oracleResponse(bytes _response) onlyBy(oracle) { 17 | // use the data 18 | } 19 | } -------------------------------------------------------------------------------- /Maintenance/Satellite/Base.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "../../authorization/Ownership.sol"; 4 | import "./Satellite.sol"; 5 | 6 | contract Base is Owned { 7 | uint public variable; 8 | address satelliteAddress; 9 | 10 | function setVariable() public onlyOwner { 11 | Satellite s = Satellite(satelliteAddress); 12 | variable = s.calculateVariable(); 13 | } 14 | 15 | function updateSatelliteAddress(address _address) public onlyOwner { 16 | satelliteAddress = _address; 17 | } 18 | } -------------------------------------------------------------------------------- /Maintenance/Relay.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "../authorization/Ownership.sol"; 4 | 5 | contract Relay is Owned { 6 | address public currentVersion; 7 | 8 | constructor(address initAddr) public { 9 | currentVersion = initAddr; 10 | owner = msg.sender; 11 | } 12 | 13 | function changeContract(address newVersion) public onlyOwner() { 14 | currentVersion = newVersion; 15 | } 16 | 17 | // fallback function 18 | function() public { 19 | require(currentVersion.delegatecall(msg.data)); 20 | } 21 | } -------------------------------------------------------------------------------- /Maintenance/Register.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "../authorization/Ownership.sol"; 4 | 5 | contract Register is Owned { 6 | address backendContract; 7 | address[] previousBackends; 8 | 9 | constructor() public { 10 | owner = msg.sender; 11 | } 12 | 13 | function changeBackend(address newBackend) public onlyOwner() returns (bool) { 14 | if(newBackend != backendContract) { 15 | previousBackends.push(backendContract); 16 | backendContract = newBackend; 17 | return true; 18 | } 19 | return false; 20 | } 21 | } -------------------------------------------------------------------------------- /Maintenance/Segregation/DataSegregation.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract DataStore { 4 | // storage data goes here 5 | uint public x; 6 | } 7 | 8 | contract Consumer { 9 | address _data; 10 | DataStore data; 11 | 12 | constructor(address _d) { 13 | data = DataStore(_d); 14 | _data = _d; 15 | } 16 | 17 | function getData() returns (address) { 18 | return _data; 19 | } 20 | } 21 | 22 | contract Controller is Consumer { 23 | 24 | constructor(address d) Consumer(d) { 25 | } 26 | 27 | function accessData() public returns (uint) { 28 | return data.x; 29 | } 30 | } -------------------------------------------------------------------------------- /Maintenance/ProxyDelegate/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } -------------------------------------------------------------------------------- /ActionAndControl/SendingFunds.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Auction { 4 | 5 | address public highestBidder; 6 | 7 | uint highestBid; 8 | 9 | mapping(address => uint) refunds; 10 | 11 | function bid() public payable { 12 | 13 | require(msg.value >= highestBid); 14 | 15 | if (highestBidder != 0) { 16 | refunds[highestBidder] += highestBid; 17 | } 18 | 19 | highestBidder = msg.sender; 20 | highestBid = msg.value; 21 | } 22 | 23 | function withdrawRefund() public { 24 | uint refund = refunds[msg.sender]; 25 | refunds[msg.sender] = 0; 26 | msg.sender.transfer(refund); 27 | } 28 | } -------------------------------------------------------------------------------- /Security/EmergencyStop.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "../authorization/Ownership.sol"; 4 | 5 | contract EmergencyStop is Owned { 6 | bool public contractStopped = false; 7 | 8 | modifier haltInEmergency { 9 | if (!contractStopped) _; 10 | } 11 | 12 | modifier enableInEmergency { 13 | if (contractStopped) _; 14 | } 15 | 16 | function toggleContractStopped() public onlyOwner { 17 | contractStopped = !contractStopped; 18 | } 19 | 20 | function deposit() public payable haltInEmergency { 21 | // some code 22 | } 23 | 24 | function withdraw() public view enableInEmergency { 25 | // some code 26 | } 27 | } -------------------------------------------------------------------------------- /Lifecycle/AutomaticDeprecation.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract AutoDeprecate { 4 | uint expires; 5 | 6 | function AutoDeprecate(uint _days) public { 7 | expires = now + _days * 1 days; 8 | } 9 | 10 | function expired() internal view returns (bool) { 11 | return now > expires; 12 | } 13 | 14 | modifier willDeprecate() { 15 | require(!expired()); 16 | _; 17 | } 18 | 19 | modifier whenDeprecated() { 20 | require(expired()); 21 | _; 22 | } 23 | 24 | function deposit() public payable willDeprecate { 25 | // some code 26 | } 27 | 28 | function withdraw() public view whenDeprecated { 29 | // some code 30 | } 31 | } -------------------------------------------------------------------------------- /ActionAndControl/Oracle/Oracle.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Oracle { 4 | 5 | address knownSource = 0x123...; 6 | 7 | struct Request { 8 | bytes data; 9 | function(bytes memory) external callback; 10 | } 11 | 12 | Request[] requests; 13 | 14 | event NewRequest(uint); 15 | 16 | modifier onlyBy(address _account) { 17 | require(msg.sender == _account); _; 18 | } 19 | 20 | function query(bytes _data, function(bytes memory) external _callback) public { 21 | requests.push(Request(_data, _callback)); 22 | NewRequest(requests.length - 1); 23 | } 24 | 25 | function reply(uint _requestID, bytes _response) public onlyBy(knownSource) { 26 | requests[_requestID].callback(response); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Maintenance/ProxyDelegate/Dispatcher.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./DispatcherStorage.sol"; 4 | 5 | contract Dispatcher { 6 | function() public { 7 | DispatcherStorage dispatcherStorage = DispatcherStorage(0x1111222233334444555566667777888899990000); 8 | address target = dispatcherStorage.lib(); 9 | 10 | assembly { 11 | calldatacopy(0x0, 0x0, calldatasize) 12 | let success := delegatecall(sub(gas, 10000), target, 0x0, calldatasize, 0, 0) 13 | let retSz := returndatasize 14 | returndatacopy(0, 0, retSz) 15 | switch success 16 | case 0 { 17 | revert(0, retSz) 18 | } 19 | default { 20 | return(0, retSz) 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Authorization/Ownership.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./Ownership.sol"; 4 | 5 | contract AccessRestriction is Owned { 6 | uint public creationTime = now; 7 | 8 | modifier onlyBefore(uint _time) { 9 | require(now < _time); 10 | _; 11 | } 12 | 13 | modifier onlyAfter(uint _time) { 14 | require(now > _time); 15 | _; 16 | } 17 | 18 | modifier onlyBy(address account) { 19 | require(msg.sender == account); 20 | _; 21 | } 22 | 23 | modifier condition(bool _condition) { 24 | require(_condition); 25 | _; 26 | } 27 | 28 | modifier minAmount(uint _amount) { 29 | require(msg.value >= _amount); 30 | _; 31 | } 32 | 33 | function f() payable onlyAfter(creationTime + 1 minutes) onlyBy(owner) minAmount(2 ether) condition(msg.sender.balance >= 50 ether) { 34 | // some code 35 | } 36 | } -------------------------------------------------------------------------------- /Authorization/AccessRestriction.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./Ownership.sol"; 4 | 5 | contract AccessRestriction is Owned { 6 | uint public creationTime = now; 7 | 8 | modifier onlyBefore(uint _time) { 9 | require(now < _time); 10 | _; 11 | } 12 | 13 | modifier onlyAfter(uint _time) { 14 | require(now > _time); 15 | _; 16 | } 17 | 18 | modifier onlyBy(address account) { 19 | require(msg.sender == account); 20 | _; 21 | } 22 | 23 | modifier condition(bool _condition) { 24 | require(_condition); 25 | _; 26 | } 27 | 28 | modifier minAmount(uint _amount) { 29 | require(msg.value >= _amount); 30 | _; 31 | } 32 | 33 | function f() payable onlyAfter(creationTime + 1 minutes) onlyBy(owner) minAmount(2 ether) condition(msg.sender.balance >= 50 ether) { 34 | // some code 35 | } 36 | } -------------------------------------------------------------------------------- /ActionAndControl/Randomness.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Randomness { 4 | 5 | bytes32 sealedSeed; 6 | bool seedSet = false; 7 | bool betsClosed = false; 8 | uint storedBlockNumber; 9 | address trustedParty = 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF; 10 | 11 | function setSealedSeed(bytes32 _sealedSeed) public { 12 | require(!seedSet); 13 | require(msg.sender == trustedParty); 14 | betsClosed = true; 15 | sealedSeed = _sealedSeed; 16 | storedBlockNumber = block.number + 1; 17 | seedSet = true; 18 | } 19 | 20 | function bet() public { 21 | require(!betsClosed); 22 | } 23 | 24 | function reveal(bytes32 _seed) public { 25 | require(seedSet); 26 | require(betsClosed); 27 | require(storedBlockNumber < block.number); 28 | require(keccak256(msg.sender, _seed) == sealedSeed); 29 | uint random = uint(keccak256(_seed, blockhash(storedBlockNumber))); 30 | seedSet = false; 31 | betsClosed = false; 32 | } 33 | } -------------------------------------------------------------------------------- /Security/ChecksEffectsInteraction.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Auction { 4 | address public beneficiary = msg.sender; 5 | address highestBidder; 6 | uint highestBid; 7 | uint public auctionEnd = now + 3 days; 8 | bool ended; 9 | mapping(address => uint) refunds; 10 | 11 | function bid() public payable { 12 | require(msg.value >= highestBid); 13 | if (highestBidder != 0) { 14 | refunds[highestBidder] += highestBid; // record the refund that this user can claim 15 | } 16 | highestBidder = msg.sender; 17 | highestBid = msg.value; 18 | } 19 | 20 | function withdrawRefund() public { 21 | uint refund = refunds[msg.sender]; 22 | refunds[msg.sender] = 0; 23 | msg.sender.transfer(refund); 24 | } 25 | 26 | function auctionEnd() public { 27 | // 1. Checks 28 | require(now >= auctionEnd); 29 | require(!ended); 30 | // 2. Effects 31 | ended = true; 32 | // 3. Interaction 33 | beneficiary.transfer(highestBid); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Security/SpeedBump.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract SpeedBump { 4 | struct Withdrawal { 5 | uint amount; 6 | uint requestedAt; 7 | } 8 | mapping (address => uint) private balances; 9 | mapping (address => Withdrawal) private withdrawals; 10 | uint constant WAIT_PERIOD = 7 days; 11 | 12 | function deposit() public payable { 13 | if(!(withdrawals[msg.sender].amount > 0)) 14 | balances[msg.sender] += msg.value; 15 | } 16 | 17 | function requestWithdrawal() public { 18 | if (balances[msg.sender] > 0) { 19 | uint amountToWithdraw = balances[msg.sender]; 20 | balances[msg.sender] = 0; 21 | withdrawals[msg.sender] = Withdrawal({ 22 | amount: amountToWithdraw, 23 | requestedAt: now 24 | }); 25 | } 26 | } 27 | 28 | function withdraw() public { 29 | if(withdrawals[msg.sender].amount > 0 && now > withdrawals[msg.sender].requestedAt + WAIT_PERIOD) { 30 | uint amount = withdrawals[msg.sender].amount; 31 | withdrawals[msg.sender].amount = 0; 32 | msg.sender.transfer(amount); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /ActionAndControl/StateMachine.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract DepositLock { 4 | 5 | enum Stages { 6 | AcceptingDeposits, 7 | FreezingDeposits, 8 | ReleasingDeposits 9 | } 10 | 11 | Stages public stage = Stages.AcceptingDeposits; 12 | 13 | uint public creationTime = now; 14 | 15 | mapping (address => uint) balances; 16 | 17 | modifier atStage(Stages _stage) { 18 | require(stage == _stage); 19 | _; 20 | } 21 | 22 | modifier timedTransitions() { 23 | if (stage == Stages.AcceptingDeposits && now >= creationTime + 1 days) 24 | nextStage(); 25 | if (stage == Stages.FreezingDeposits && now >= creationTime + 8 days) 26 | nextStage(); 27 | _; 28 | } 29 | 30 | function nextStage() internal { 31 | stage = Stages(uint(stage) + 1); 32 | } 33 | 34 | function deposit() public payable timedTransitions atStage(Stages.AcceptingDeposits) { 35 | balances[msg.sender] += msg.value; 36 | } 37 | 38 | function withdraw() public timedTransitions atStage(Stages.ReleasingDeposits) { 39 | uint amount = balances[msg.sender]; 40 | balances[msg.sender] = 0; 41 | msg.sender.transfer(amount); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Maintenance/Segregation/DataStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract DataStorage { 4 | mapping(bytes32 => uint) uintStorage; 5 | mapping(bytes32 => int) intStorage; 6 | mapping(bytes32 => string) stringStorage; 7 | mapping(bytes32 => address) addressStorage; 8 | mapping(bytes32 => bool) booleanStorage; 9 | mapping(bytes32 => bytes) bytesStorage; 10 | 11 | function getIntValue(bytes32 key) public constant returns (int) { 12 | return intStorage[key]; 13 | } 14 | 15 | function setIntValue(bytes32 key, int value) public { 16 | intStorage[key] = value; 17 | } 18 | 19 | function getUintValue(bytes32 key) public constant returns (uint) { 20 | return uintStorage[key]; 21 | } 22 | 23 | function setUintValue(bytes32 key, uint value) public { 24 | uintStorage[key] = value; 25 | } 26 | 27 | function getStringValue(bytes32 key) public constant returns (string) { 28 | return stringStorage[key]; 29 | } 30 | 31 | function setStringValue(bytes32 key, string value) public { 32 | stringStorage[key] = value; 33 | } 34 | 35 | function getAddressValue(bytes32 key) public constant returns (address) { 36 | return addressStorage[key]; 37 | } 38 | 39 | function setAddressValue(bytes32 key, address value) public { 40 | addressStorage[key] = value; 41 | } 42 | 43 | function getBytesValue(bytes32 key) public constant returns (bytes) { 44 | return bytesStorage[key]; 45 | } 46 | 47 | function setBytesValue(bytes32 key, bytes value) public { 48 | bytesStorage[key] = value; 49 | } 50 | 51 | function getBooleanValue(bytes32 key) public constant returns (bool) { 52 | return booleanStorage[key]; 53 | } 54 | 55 | function setBooleanValue(bytes32 key, bool value) public { 56 | booleanStorage[key] = value; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ActionAndControl/CommitReveal.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract CommitReveal { 4 | 5 | struct Commit { 6 | string choice; 7 | string secret; 8 | string status; 9 | } 10 | 11 | mapping(address => mapping(bytes32 => Commit)) public userCommits; 12 | 13 | event LogCommit(bytes32, address); 14 | event LogReveal(bytes32, address, string, string); 15 | 16 | function constructor() public { 17 | } 18 | 19 | function commit(bytes32 _commit) public returns (bool success) { 20 | 21 | var userCommit = userCommits[msg.sender][_commit]; 22 | 23 | if(bytes(userCommit.status).length != 0) { 24 | return false; 25 | } 26 | 27 | userCommit.status = "c"; 28 | 29 | LogCommit(_commit, msg.sender); 30 | 31 | return true; 32 | } 33 | 34 | function reveal(string _choice, string _secret, bytes32 _commit) public returns (bool success) { 35 | 36 | var userCommit = userCommits[msg.sender][_commit]; 37 | 38 | bytes memory bytesStatus = bytes(userCommit.status); 39 | 40 | if(bytesStatus.length == 0) { 41 | return false; 42 | } else if (bytesStatus[0] == "r") { 43 | return false; 44 | } 45 | 46 | if (_commit != keccak256(_choice, _secret)) { 47 | return false; 48 | } 49 | 50 | userCommit.choice = _choice; 51 | userCommit.secret = _secret; 52 | userCommit.status = "r"; 53 | 54 | LogReveal(_commit, msg.sender, _choice, _secret); 55 | 56 | return true; 57 | } 58 | 59 | function traceCommit(address _address, bytes32 _commit) public view returns (string choice, string secret, string status) { 60 | var userCommit = userCommits[_address][_commit]; 61 | 62 | require(bytes(userCommit.status)[0] == "r"); 63 | return (userCommit.choice, userCommit.secret, userCommit.status); 64 | } 65 | } 66 | 67 | 68 | --------------------------------------------------------------------------------