re-edit renew function

re-edit renew function

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./PLCRVoting.sol";

// to do:
// implement events
// deposit
// token flooring issue

contract Registry {

    /*
     * Storage
     */

    //Registry storage
    StandardToken public token;
    PLCRVoting public voting;
    mapping(bytes32 => Publisher) public whitelist; //domainHash => Publisher struct
    mapping(bytes32 => Application) public appPool; //holds applications for both domain and parameter appPool
    mapping(bytes32 => Params) public paramSnapshots;
    mapping(uint => VoteInfo) public pollInfo; // holds information on individual polls
    mapping(address => mapping(uint => bool)) public voterInfo; // holds information on voters' token claims
    string domain;
    //Parameter storage
    mapping(bytes32 => uint) public Parameters;
    mapping(uint => bytes32) public idToHash;
    string parameter;
    uint value;

    struct Publisher {
        address owner;
        uint expTime;
        uint deposit;
        uint prevExpTime //
        uint nextExpTime //
        uint prevDeposit // total withdrawable amount
        uint nextDeposit //
    }

    struct Application {
        address owner;
        bool challenged;
        uint challengeTime; //End of challenge period
        address challenger;

        string domain;
        string parameter;
        uint value;
    }

    struct Params {
        // parameters concerning the whitelist and application pool
        uint minDeposit;
        uint challengeLen;
        uint registryLen;

        // parameters to be passed into the voting contract
        uint commitVoteLen;
        uint revealVoteLen;
        uint majority;

        // parameter representing the scale of how token rewards are distributed
        uint dispensationPct;
    }

    struct VoteInfo {
        // if the vote has been processed
        bool processed;
        // amount of leftover tokens available for winning party to claim
        // (a byproduct of needing to floor the distributed tokens to voters)
        uint remainder;
    }

    // Constructor
    /// @param _minDeposit      application & challenger deposit amounts
    /// @param _challengeLen    duration of the challenge period
    /// @param _registryLen     duration of a registration’s validity
    /// @param _commitVoteLen   duration of the commit period in token votes
    /// @param _revealVoteLen   duration of reveal period in token votes
    /// @param _dispensationPct percentage of forfeited deposit distributed to winning party; uint between 0 and 100
    /// @param _majority        percentage of votes that constitutes the majority; uint between 0 and 100

    function Registry(address _token,
       uint _minDeposit,
       uint _challengeLen,
       uint _registryLen,
       uint _commitVoteLen,
       uint _revealVoteLen,
       uint _dispensationPct,
       uint _majority) {

       token = StandardToken(_token);
       // initialize values
       Parameters[sha3("minDeposit")]        = _minDeposit;
       Parameters[sha3("challengeLen")]      = _challengeLen;
       Parameters[sha3("registryLen")]       = _registryLen;
       Parameters[sha3("commitVoteLen")]     = _commitVoteLen;
       Parameters[sha3("revealVoteLen")]     = _revealVoteLen;
       Parameters[sha3("dispensationPct")]   = _dispensationPct;
       Parameters[sha3("majority")]          = _majority;
    }

    // called by an applicant to apply (moves them into the application pool on success)
    function apply(string _domain) {
        bytes32 domainHash = sha3(_domain);
        require(hasRenewal[domainHash] == false);
        // initialize with the current values of all parameters
        initializeSnapshot(_domain);
        initApplication(domainHash, msg.sender);
        appPool[domainHash].domain = _domain;
    }

    //helper function to apply() and proposeUpdate()
    //initialize general application
    function initApplication(bytes32 _hash, address _applicant) {
        // applicant must pay the current value of minDeposit
        uint deposit = paramSnapshots[_hash].minDeposit;
        // check to prevent repeat applications
        require(appPool[_hash].owner == 0);
        // check that registry can take sufficient amount of tokens from the applicant
        require(token.allowance(_applicant, this) >= deposit);
        token.transferFrom(_applicant, this, deposit);
        appPool[_hash].challengeTime = now + paramSnapshots[_hash].challengeLen;
        appPool[_hash].owner = _applicant;
    }

    function renew (string _domain) {
        bytes32 domainHash = sha3(_domain);
        // check if no active renewal
        require(hasRenewal(domainHash) == false);
        require(msg.sender == whitelist[domainHash].owner); // checks that you are the owner of the domain
        require(appPool[_hash].owner == 0); // no double renewal
        uint deposit = get('minDeposit');
        //Check if existing deposit is sufficient
        if (whitelist[domainHash].deposit + whitelist[domainHash].prevDeposit >= deposit){
            //apply
            initializeSnapshot(_domain);
            appPool[_hash].challengeTime = now + paramSnapshots[_hash].challengeLen;
            appPool[_hash].owner = msg.sender;
            // only take from deposit
            if (whitelist[domainHash].deposit >= deposit)
            {
                uint difference = whitelist[domainHash].deposit - deposit;
                whitelist[domainHash].deposit = difference;
            }
            // take whole deposit and part of prevDeposit
            else
            {
                // take all of locked deposit and a portion of unlocked deposit
                uint difference = deposit - whitelist[domainHash].deposit;
                uint difference2 = whitelist[domainHash].prevDeposit - difference;
                whitelist[domainHash].prevDeposit = difference2;
            }

        }
        //if insufficient # of tokens, then must send in the difference
        else {
            uint difference = deposit-(whitelist[domainHash].deposit + whitelist.[domainHash].prevDeposit);
            require(token.allowance(msg.sender, this) >= difference);
            token.transferFrom(msg.sender, this, difference);
            //apply
            initializeSnapshot(_domain);
            appPool[_hash].challengeTime = now + paramSnapshots[_hash].challengeLen;
            appPool[_hash].owner = msg.sender;
        }
    }

    function claimDeposit(string _domain) {  // take out only part
        bytes32 domainHash = sha3(_domain);
        require(msg.sender == whitelist[domainHash].owner);
        if (hasRenewal(domainHash))  // updates token values if necessary
        {
            token.transfer(msg.sender, whitelist[domainHash].prevDeposit);
            whitelist[domainHash].prevDeposit = 0;
        }
    }

    // checks to see if a renewal has turned into current whitelist period
    // if there is a renewal and it has become the current whitelist period,
    // process and change state of variables
    function hasRenewal(bytes32 _hash) private returns (bool){
        // renewal start point is in the past
        if (whitelist[_hash].expTime <= now ) {
            if (whitelist[_hash].nextExpTime != whitelist[_hash].expTime)
            { // no active renewal
                return false;
            }
            // renewal is processed to change state of whitelist struct
            whitelist[_hash].prevExpTime = whitelist[_hash].expTime;
            whitelist[_hash].expTime = whitelist[_hash].nextExpTime;
            whitelist[_hash].prevDeposit += whitelist[_hash].deposit;
            whitelist[_hash].deposit = whitelist[_hash].nextDeposit;
            return false;
        }
        else
        {
            return true;
        }
    }

    // called by any adtoken holder to challenge an application to the whitelist
    // initialize vote to accept/reject a domain to the registry
    function challengeApplication(string _domain) {
        bytes32 domainHash = sha3(_domain);
        challenge(domainHash, msg.sender);
        // start a vote
        uint pollID = callVote(_domain
        ,paramSnapshots[domainHash].majority
        ,paramSnapshots[domainHash].commitVoteLen
        ,paramSnapshots[domainHash].revealVoteLen);
        idToHash[pollID] = domainHash;
    }

    //helper function to challengeApplication() and challengeProposal()
    function challenge(bytes32 _hash, address _challenger) {
        // check that registry can take sufficient amount of tokens from the challenger
        uint deposit = paramSnapshots[_hash].minDeposit;
        require(token.allowance(_challenger, this) >= deposit);
        token.transferFrom(_challenger, this, deposit);

        // prevent someone from challenging an unintialized application, rechallenging,
        // or challenging after the challenge period has ended
        require(appPool[_hash].owner != 0);
        require(appPool[_hash].challenged == false);
        require(appPool[_hash].challengeTime > now);

        // update the application's status
        appPool[_hash].challenged = true;
        appPool[_hash].challenger = _challenger;
    }

    // helper function to the challenge() function. Initializes a vote through the voting contract
    // returns a poll id
    function callVote(string _proposalString,
        uint _majority,
        uint _commitVoteLen,
        uint _revealVoteLen
        ) private returns (uint) {
        // event that vote has started
        uint pollID = voting.startPoll( _proposalString, _majority, _commitVoteLen,  _revealVoteLen);
        return pollID;
    }

    // a one-time function for each completed vote
    // if domain won: domain is moved to the whitelist and applicant is rewarded tokens
    // if domain lost: challenger is rewarded tokens
    function processResult(uint _pollID)
    {
        require(pollInfo[_pollID].processed == false);
        bytes32 domainHash = idToHash[_pollID];

        if (voting.isPassed(_pollID)) {
            //??what would happen if didProposalPass() called and vote's still ongoing??
            // add to registry
            add(domainHash, appPool[domainHash].owner);
            delete appPool[domainHash].owner;
            // give tokens to applicant based on dist and total tokens
        }
        else {
            delete appPool[domainHash].owner;
            uint minDeposit = paramSnapshots[domainHash].minDeposit;
            uint dispensationPct = paramSnapshots[domainHash].dispensationPct;
            uint winning = minDeposit * dispensationPct;  // change math to be int between 0-100
            token.transfer(appPool[domainHash].challenger, winning + minDeposit);
        }
        // ensures the result cannot be processed again
        pollInfo[_pollID].processed = true;
    }

    // called by each voter to claim their reward for each completed vote
    function claimReward(uint _pollID, uint _salt) {
        // checks if a voter has claimed tokens
        require(voterInfo[msg.sender][_pollID] == false);
        uint reward = giveTokens(_pollID, _salt, msg.sender);
        // ensures a voter cannot claim tokens again
        token.transfer(msg.sender, reward);
        voterInfo[msg.sender][_pollID] = true;
    }

    // number of tokens person used to vote / total number of tokens for winning side
    // scale using distribution number, give the tokens
    function giveTokens(uint _pollID, uint _salt, address _voter) private returns(uint) {
        bytes32 hash = idToHash[_pollID];
        uint minDeposit = paramSnapshots[hash].minDeposit;
        uint dispensationPct = paramSnapshots[hash].dispensationPct;
        uint totalTokens = voting.getTotalNumberOfTokensForWinningOption(_pollID);
        uint voterTokens = voting.getNumCorrectVote(_pollID, _salt, _voter);
        uint reward = voterTokens*minDeposit*(1-dispensationPct)/totalTokens;

        // check if there will be leftover from flooring, add into a pool claimable by winner
        // (to prevent token locking due to flooring of voter rewards)
        // uint modCheck = voterTokens % ;
        // uint purchaseAmount = msg.value - excessAmount;
        // uint tokenPurchase = purchaseAmount / price;

        return reward;
    }

    // called to move an applying domain to the whitelist
    // iff the domain's challenge period has passed without a challenge
    function moveToRegistry(string _domain) {
        bytes32 domainHash = sha3(_domain);
        require(appPool[domainHash].challengeTime < now);
        require(appPool[domainHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[domainHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        add(domainHash, appPool[domainHash].owner);
        delete appPool[domainHash].owner;
    }

    // private function to add a domain name to the whitelist
    function add(bytes32 _domainHash, address _owner) private {
        uint expiration = paramSnapshots[_domainHash].registryLen;
        whitelist[_domainHash].expTime = now + expiration;
        whitelist[_domainHash].owner = _owner;
        whitelist[_domainHash].deposit = paramSnapshots[_domainHash].minDeposit;
    }

    // checks if a domain name is in the whitelist and unexpired
    function isVerified(string _domain) returns (bool) {
        bytes32 domainHash = sha3(_domain);
        if (whitelist[domainHash].expTime > now) {
            return true;
        }
        else {
            return false;
        }
    }

    // private function to initialize a snapshot of parameters for each application
    function initializeSnapshot(string _domain) private {
        bytes32 domainHash = sha3(_domain);
        initializeSnapshotParam(domainHash);  // maybe put the two together
        paramSnapshots[domainHash].registryLen = get("registryLen");
    }

/*****************************************************************************/

    //called by a user who wishes to change a parameter
    //initializes a proposal to change a parameter
    function proposeUpdate(string _parameter, uint _value) {
        bytes32 parameterHash = sha3(_parameter, _value);
        // initialize application with a with the current values of all parameters
        initializeSnapshotParam(parameterHash);
        initApplication(parameterHash, msg.sender);
        appPool[parameterHash].parameter = _parameter;
        appPool[parameterHash].value = _value;
    }

    //called by user who wishes to reject a proposal
    //initializes a vote to accept/reject the param change proposal
    function challengeProposal(string _parameter, uint _value) {
        bytes32 parameterHash = sha3(_parameter, _value);
        challenge(parameterHash, msg.sender);
        // start a vote
        uint pollID = callVote(_parameter
        ,paramSnapshots[parameterHash].majority
        ,paramSnapshots[parameterHash].commitVoteLen
        ,paramSnapshots[parameterHash].revealVoteLen);
        idToHash[pollID] = parameterHash;
    }

    // called to change parameter
    // iff the proposal's challenge period has passed without a challenge
    function setParams(string _parameter, uint _value) {
        bytes32 parameterHash = sha3(_parameter, _value);
        require(appPool[parameterHash].challengeTime < now);
        require(appPool[parameterHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[parameterHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        Parameters[sha3(_parameter)] = _value;
        delete appPool[parameterHash].owner;
    }

    // a one-time function for each completed vote
    // if proposal won: new parameter value is set, and applicant is rewarded tokens
    // if prospsal lost: challenger is rewarded tokens
    function processProposal(uint _pollID)
    {
        require(pollInfo[_pollID].processed == false);
        bytes32 parameterHash = idToHash[_pollID];
        parameter = appPool[parameterHash].parameter;
        value = appPool[parameterHash].value;
        if (voting.isPassed(_pollID)) {
            // setting the value of parameter
            Parameters[sha3(parameter)] = value;
            delete appPool[parameterHash].owner;
            // give tokens to applicant based on dist and total tokens IMPLEMENT
        }
        else {
            delete appPool[parameterHash].owner;
            // give tokens to challenger based on dist and total tokens
        }
        // ensures the result cannot be processed again
        pollInfo[_pollID].processed = true;
    }

    function claimParamReward(uint _pollID, uint _salt) {
        // checks if a voter has claimed tokens
        require(voterInfo[msg.sender][_pollID] == false);
        uint reward = giveTokens(_pollID, _salt);
        // ensures a voter cannot claim tokens again
        token.transfer(msg.sender, reward);
        voterInfo[msg.sender][_pollID] = true;
    }

     // private function to initialize a snapshot of parameters for each proposal
     function initializeSnapshotParam(bytes32 _hash) private {
        paramSnapshots[_hash].minDeposit = get("minDeposit");
        paramSnapshots[_hash].challengeLen = get("challengeLen");
        paramSnapshots[_hash].commitVoteLen = get("commitVoteLen");
        paramSnapshots[_hash].revealVoteLen = get("revealVoteLen");
        paramSnapshots[_hash].majority = get("majority");
        paramSnapshots[_hash].dispensationPct = get("dispensationPct");
    }

    // interface for retrieving config parameter from hashmapping
    /// @param _keyword key for hashmap (only useful when keyword matches variable name)
    function get(string _keyword) returns (uint) {
       return Parameters[sha3(_keyword)];
    }

/*****************************************************************************/

    // FOR TESTING
    function toHash(string _domain) returns (bytes32){
        return sha3(_domain);
    }
    function getCurrentTime() returns (uint){
        return now;
    }

}

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./PLCRVoting.sol";

// to do:
// implement events
// deposit
// token flooring issue

contract Registry {

    /*
     * Storage
     */

    //Registry storage
    StandardToken public token;
    PLCRVoting public voting;
    mapping(bytes32 => Publisher) public whitelist; //domainHash => Publisher struct
    mapping(bytes32 => Application) public appPool; //holds applications for both domain and parameter appPool
    mapping(bytes32 => Params) public paramSnapshots;
    mapping(uint => VoteInfo) public pollInfo; // holds information on individual polls
    mapping(address => mapping(uint => bool)) public voterInfo; // holds information on voters' token claims
    string domain;
    //Parameter storage
    mapping(bytes32 => uint) public Parameters;
    mapping(uint => bytes32) public idToHash;
    string parameter;
    uint value;

    struct Publisher {
        address owner;
        uint expTime;
        uint deposit;
    }

    struct Application {
        address owner;
        bool challenged;
        uint challengeTime; //End of challenge period
        address challenger;

        string domain;
        string parameter;
        uint value;
    }

    struct Params {
        // parameters concerning the whitelist and application pool
        uint minDeposit;
        uint challengeLen;
        uint registryLen;

        // parameters to be passed into the voting contract
        uint commitVoteLen;
        uint revealVoteLen;
        uint majority;

        // parameter representing the scale of how token rewards are distributed
        uint dispensationPct;
    }

    struct VoteInfo {
        // if the vote has been processed
        bool processed;
        // amount of leftover tokens available for winning party to claim
        // (a byproduct of needing to floor the distributed tokens to voters)
        uint remainder;
    }

    // Constructor
    /// @param _minDeposit      application & challenger deposit amounts
    /// @param _challengeLen    duration of the challenge period
    /// @param _registryLen     duration of a registration’s validity
    /// @param _commitVoteLen   duration of the commit period in token votes
    /// @param _revealVoteLen   duration of reveal period in token votes
    /// @param _dispensationPct percentage of forfeited deposit distributed to winning party; uint between 0 and 100
    /// @param _majority        percentage of votes that constitutes the majority; uint between 0 and 100

    function Registry(address _token,
       uint _minDeposit,
       uint _challengeLen,
       uint _registryLen,
       uint _commitVoteLen,
       uint _revealVoteLen,
       uint _dispensationPct,
       uint _majority) {

       token = StandardToken(_token);
       // initialize values
       Parameters[sha3("minDeposit")]        = _minDeposit;
       Parameters[sha3("challengeLen")]      = _challengeLen;
       Parameters[sha3("registryLen")]       = _registryLen;
       Parameters[sha3("commitVoteLen")]     = _commitVoteLen;
       Parameters[sha3("revealVoteLen")]     = _revealVoteLen;
       Parameters[sha3("dispensationPct")]   = _dispensationPct;
       Parameters[sha3("majority")]          = _majority;
    }

    // called by an applicant to apply (moves them into the application pool on success)
    function apply(string _domain) {
        bytes32 domainHash = sha3(_domain);
        // initialize with the current values of all parameters
        initializeSnapshot(_domain);
        initApplication(domainHash, msg.sender);
        appPool[domainHash].domain = _domain;
    }

    //helper function to apply() and proposeUpdate()
    //initialize general application
    function initApplication(bytes32 _hash, address _applicant) {
        // applicant must pay the current value of minDeposit
        uint deposit = paramSnapshots[_hash].minDeposit;
        // check to prevent repeat applications
        require(appPool[_hash].owner == 0);
        // check that registry can take sufficient amount of tokens from the applicant
        require(token.allowance(_applicant, this) >= deposit);
        token.transferFrom(_applicant, this, deposit);
        appPool[_hash].challengeTime = now + paramSnapshots[_hash].challengeLen;
        appPool[_hash].owner = _applicant;
    }

    function renew (string _domain) {
        bytes32 domainHash = sha3(_domain);
        require(msg.sender == whitelist[domainHash].owner );
        if (whitelist[domainHash].deposit >= get(minDeposit)){
            apply(_domain);
        }
        else {
            //emit event need to send in more money, then the person
            //has to take back deposit then re-apply
        }
    }

    function claimDeposit(string _domain){
        bytes32 domainHash = sha3(_domain);
        require(msg.sender == whitelist[domainHash].owner );
        require(now >= whitelist[domainHash].expTime);
        require(whitelist[domainHash].deposit > 0);
        token.transfer(msg.sender,whitelist[domainHash].deposit);
        whitelist[domainHash].deposit = 0;
    }

    // called by any adtoken holder to challenge an application to the whitelist
    // initialize vote to accept/reject a domain to the registry
    function challengeApplication(string _domain) {
        bytes32 domainHash = sha3(_domain);
        challenge(domainHash, msg.sender);
        // start a vote
        uint pollID = callVote(_domain
        ,paramSnapshots[domainHash].majority
        ,paramSnapshots[domainHash].commitVoteLen
        ,paramSnapshots[domainHash].revealVoteLen);
        idToHash[pollID] = domainHash;
    }

    //helper function to challengeApplication() and challengeProposal()
    function challenge(bytes32 _hash, address _challenger) {
        // check that registry can take sufficient amount of tokens from the challenger
        uint deposit = paramSnapshots[_hash].minDeposit;
        require(token.allowance(_challenger, this) >= deposit);
        token.transferFrom(_challenger, this, deposit);

        // prevent someone from challenging an unintialized application, rechallenging,
        // or challenging after the challenge period has ended
        require(appPool[_hash].owner != 0);
        require(appPool[_hash].challenged == false);
        require(appPool[_hash].challengeTime > now);

        // update the application's status
        appPool[_hash].challenged = true;
        appPool[_hash].challenger = _challenger;
    }

    // helper function to the challenge() function. Initializes a vote through the voting contract
    // returns a poll id
    function callVote(string _proposalString,
        uint _majority,
        uint _commitVoteLen,
        uint _revealVoteLen
        ) private returns (uint) {
        // event that vote has started
        uint pollID = voting.startPoll( _proposalString, _majority, _commitVoteLen,  _revealVoteLen);
        return pollID;
    }

    // a one-time function for each completed vote
    // if domain won: domain is moved to the whitelist and applicant is rewarded tokens
    // if domain lost: challenger is rewarded tokens
    function processResult(uint _pollID)
    {
        require(pollInfo[_pollID].processed == false);
        bytes32 domainHash = idToHash[_pollID];

        if (voting.isPassed(_pollID)) {
            //??what would happen if didProposalPass() called and vote's still ongoing??
            // add to registry
            add(domainHash, appPool[domainHash].owner);
            delete appPool[domainHash].owner;
            // give tokens to applicant based on dist and total tokens
        }
        else {
            delete appPool[domainHash].owner;
            uint minDeposit = paramSnapshots[domainHash].minDeposit;
            uint dispensationPct = paramSnapshots[domainHash].dispensationPct;
            uint winning = minDeposit * dispensationPct;  // change math to be int between 0-100
            token.transfer(appPool[domainHash].challenger, winning + minDeposit);
        }
        // ensures the result cannot be processed again
        pollInfo[_pollID].processed = true;
    }

    // called by each voter to claim their reward for each completed vote
    function claimReward(uint _pollID, uint _salt) {
        // checks if a voter has claimed tokens
        require(voterInfo[msg.sender][_pollID] == false);
        uint reward = giveTokens(_pollID, _salt, msg.sender);
        // ensures a voter cannot claim tokens again
        token.transfer(msg.sender, reward);
        voterInfo[msg.sender][_pollID] = true;
    }

    // number of tokens person used to vote / total number of tokens for winning side
    // scale using distribution number, give the tokens
    function giveTokens(uint _pollID, uint _salt, address _voter) private returns(uint) {
        bytes32 hash = idToHash[_pollID];
        uint minDeposit = paramSnapshots[hash].minDeposit;
        uint dispensationPct = paramSnapshots[hash].dispensationPct;
        uint totalTokens = voting.getTotalNumberOfTokensForWinningOption(_pollID);
        uint voterTokens = voting.getNumCorrectVote(_pollID, _salt, _voter);
        uint reward = voterTokens*minDeposit*(1-dispensationPct)/totalTokens;

        // check if there will be leftover from flooring, add into a pool claimable by winner
        // (to prevent token locking due to flooring of voter rewards)
        // uint modCheck = voterTokens % ;
        // uint purchaseAmount = msg.value - excessAmount;
        // uint tokenPurchase = purchaseAmount / price;

        return reward;
    }

    // called to move an applying domain to the whitelist
    // iff the domain's challenge period has passed without a challenge
    function moveToRegistry(string _domain) {
        bytes32 domainHash = sha3(_domain);
        require(appPool[domainHash].challengeTime < now);
        require(appPool[domainHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[domainHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        add(domainHash, appPool[domainHash].owner);
        delete appPool[domainHash].owner;
    }

    // private function to add a domain name to the whitelist
    function add(bytes32 _domainHash, address _owner) private {
        uint expiration = paramSnapshots[_domainHash].registryLen;
        whitelist[_domainHash].expTime = now + expiration;
        whitelist[_domainHash].owner = _owner;
        whitelist[_domainHash].deposit = paramSnapshots[_domainHash].minDeposit;
    }

    // checks if a domain name is in the whitelist and unexpired
    function isVerified(string _domain) returns (bool) {
        bytes32 domainHash = sha3(_domain);
        if (whitelist[domainHash].expTime > now) {
            return true;
        }
        else {
            return false;
        }
    }

    // private function to initialize a snapshot of parameters for each application
    function initializeSnapshot(string _domain) private {
        bytes32 domainHash = sha3(_domain);
        initializeSnapshotParam(domainHash);  // maybe put the two together
        paramSnapshots[domainHash].registryLen = get("registryLen");
    }

/*****************************************************************************/

    //called by a user who wishes to change a parameter
    //initializes a proposal to change a parameter
    function proposeUpdate(string _parameter, uint _value) {
        bytes32 parameterHash = sha3(_parameter, _value);
        // initialize application with a with the current values of all parameters
        initializeSnapshotParam(parameterHash);
        initApplication(parameterHash, msg.sender);
        appPool[parameterHash].parameter = _parameter;
        appPool[parameterHash].value = _value;
    }

    //called by user who wishes to reject a proposal
    //initializes a vote to accept/reject the param change proposal
    function challengeProposal(string _parameter, uint _value) {
        bytes32 parameterHash = sha3(_parameter, _value);
        challenge(parameterHash, msg.sender);
        // start a vote
        uint pollID = callVote(_parameter
        ,paramSnapshots[parameterHash].majority
        ,paramSnapshots[parameterHash].commitVoteLen
        ,paramSnapshots[parameterHash].revealVoteLen);
        idToHash[pollID] = parameterHash;
    }

    // called to change parameter
    // iff the proposal's challenge period has passed without a challenge
    function setParams(string _parameter, uint _value) {
        bytes32 parameterHash = sha3(_parameter, _value);
        require(appPool[parameterHash].challengeTime < now);
        require(appPool[parameterHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[parameterHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        Parameters[sha3(_parameter)] = _value;
        delete appPool[parameterHash].owner;
    }

    // a one-time function for each completed vote
    // if proposal won: new parameter value is set, and applicant is rewarded tokens
    // if prospsal lost: challenger is rewarded tokens
    function processProposal(uint _pollID)
    {
        require(pollInfo[_pollID].processed == false);
        bytes32 parameterHash = idToHash[_pollID];
        parameter = appPool[parameterHash].parameter;
        value = appPool[parameterHash].value;
        if (voting.isPassed(_pollID)) {
            // setting the value of parameter
            Parameters[sha3(parameter)] = value;
            delete appPool[parameterHash].owner;
            // give tokens to applicant based on dist and total tokens IMPLEMENT
        }
        else {
            delete appPool[parameterHash].owner;
            // give tokens to challenger based on dist and total tokens
        }
        // ensures the result cannot be processed again
        pollInfo[_pollID].processed = true;
    }

    function claimParamReward(uint _pollID, uint _salt) {
        // checks if a voter has claimed tokens
        require(voterInfo[msg.sender][_pollID] == false);
        uint reward = giveTokens(_pollID, _salt);
        // ensures a voter cannot claim tokens again
        token.transfer(msg.sender, reward);
        voterInfo[msg.sender][_pollID] = true;
    }

     // private function to initialize a snapshot of parameters for each proposal
     function initializeSnapshotParam(bytes32 _hash) private {
        paramSnapshots[_hash].minDeposit = get("minDeposit");
        paramSnapshots[_hash].challengeLen = get("challengeLen");
        paramSnapshots[_hash].commitVoteLen = get("commitVoteLen");
        paramSnapshots[_hash].revealVoteLen = get("revealVoteLen");
        paramSnapshots[_hash].majority = get("majority");
        paramSnapshots[_hash].dispensationPct = get("dispensationPct");
    }

    // interface for retrieving config parameter from hashmapping
    /// @param _keyword key for hashmap (only useful when keyword matches variable name)
    function get(string _keyword) returns (uint) {
       return Parameters[sha3(_keyword)];
    }

/*****************************************************************************/

    // FOR TESTING
    function toHash(string _domain) returns (bytes32){
        return sha3(_domain);
    }
    function getCurrentTime() returns (uint){
        return now;
    }

}


forcing constructor to have enough balance to pay for needed oracle queries.

/*
    Copyright 2017 Phillip A. Elsasser

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

pragma solidity ^0.4.0;

import "./Creatable.sol";
import "./oraclize/oraclizeAPI.sol";
import "./libraries/MathLib.sol";
import "./libraries/HashLib.sol";

//TODO: fix style issues
//      add accounting for users and positions
//      add failsafe for pool distribution.
//      push as much into library as possible
contract MarketContract is Creatable, usingOraclize  {
    using MathLib for uint256;
    using MathLib for int;
    using HashLib for address;

    struct UserNetPosition {
        address userAddress;
        Position[] positions;   // all open positions (lifo upon exit - allows us to not reindex array!)
        int netPosition;        // net position across all prices / executions
    }

    struct Position {
        uint price;
        int qty;
    }

    struct Order {
        address maker;
        address taker;
        address feeRecipient;
        uint makerFee;
        uint takerFee;
        uint qty;
        uint price;
        uint8 makerSide;            // 0=Buy 1=Sell
        uint expirationTimeStamp;
        bytes32 orderHash;
    }

    // constants
    string public CONTRACT_NAME;
    address public BASE_TOKEN;
    uint public PRICE_CAP;
    uint public PRICE_FLOOR;
    uint public PRICE_DECIMAL_PLACES;   // how to convert the pricing from decimal format (if valid) to integer
    uint public EXPIRATION;
    string public ORACLE_DATA_SOURCE;
    string public ORACLE_QUERY;
    uint public ORACLE_QUERY_REPEAT;
    uint8 constant public BUY_SIDE = 0;
    uint8 constant public SELL_SIDE = 1;
    uint constant public COST_PER_QUERY = 2 finney;    // leave static for now, price of first query from oraclize is 0
    uint constant public QUERY_CALLBACK_GAS = 300000;

    // state variables
    string public lastPriceQueryResult;
    uint public lastPrice;
    bool public isExpired;
    mapping(bytes32 => bool) validQueryIDs;

    // accounting
    mapping(address => UserNetPosition) addressToUserPosition;

    // events
    event NewOracleQuery(string description);
    event UpdatedLastPrice(string price);
    event ContractSettled();

    function MarketContract(
        string contractName,
        address baseToken,
        string oracleDataSource,
        string oracleQuery,
        uint oracleQueryRepeatSeconds,
        uint floorPrice,
        uint capPrice,
        uint priceDecimalPlaces,
        uint secondsToExpiration
    ) payable {

        require(capPrice > floorPrice);
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        CONTRACT_NAME = contractName;
        BASE_TOKEN = baseToken;
        PRICE_CAP = capPrice;
        PRICE_FLOOR = floorPrice;
        EXPIRATION = now + secondsToExpiration;
        ORACLE_DATA_SOURCE = oracleDataSource;
        ORACLE_QUERY = oracleQuery;
        ORACLE_QUERY_REPEAT = oracleQueryRepeatSeconds;
        PRICE_DECIMAL_PLACES = priceDecimalPlaces;
        require(checkSufficientStartingBalance(secondsToExpiration));
        queryOracle();  // schedules recursive calls to oracle
    }

    function __callback(bytes32 queryID, string result, bytes proof) {
        require(validQueryIDs[queryID]);
        require(msg.sender == oraclize_cbAddress());
        lastPriceQueryResult = result;
        lastPrice = parseInt(result, PRICE_DECIMAL_PLACES);
        UpdatedLastPrice(result);
        delete validQueryIDs[queryID];
        checkSettlement();
        if (!isExpired) {
            queryOracle();  // set up our next query
        }
    }

    function trade(address maker, address taker) {
        require(maker != address(0) && maker != taker);     // do not allow self trade
        // TODO validate orders, etc
    }

    function updatePositions(address maker, address taker, int qty, uint price) private {
        updatePosition(addressToUserPosition[maker], qty, price);   // TODO: ensure struct is passed as storage!
        // continue process for taker, but qty is opposite sign for taker
        updatePosition(addressToUserPosition[taker], qty * -1, price);   // TODO: ensure struct is passed as storage!
    }

    function updatePosition(UserNetPosition storage userNetPosition, int qty, uint price) private {
        if(userNetPosition.netPosition == 0 ||  userNetPosition.netPosition.isSameSign(qty)) {
            // new position or adding to open pos, no collateral returned
            userNetPosition.positions.push(Position(price, newNetPos)); //append array with new position
        }
        else {
            // opposite side from open position, reduce, flattened, or flipped.
            if(userNetPosition.netPosition >= qty * -1) { // pos is reduced of flattened
                reduceUserNetPosition(userNetPosition, qty, price);
            } else {    // pos is flipped, reduce and then create new open pos!
                reduceUserNetPosition(userNetPosition, userNetPosition.netPosition * -1, price); // flatten completely
                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                userNetPosition.positions.push(Position(price, newNetPos));   // append array with new position
            }
        }
        userNetPosition.netPosition += qty;
    }

    function reduceUserNetPosition(UserNetPosition storage userNetPos, int qty, uint price) private {
        // TODO: determine collateral to return
    }

    function queryOracle() private
    {
        if (oraclize_getPrice(ORACLE_DATA_SOURCE) > this.balance) {
            NewOracleQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
            lastPriceQueryResult = "FAILED"; //TODO: failsafe
        } else {
            NewOracleQuery("Oraclize query was sent, standing by for the answer..");
            bytes32 queryId = oraclize_query(ORACLE_QUERY_REPEAT, ORACLE_DATA_SOURCE, ORACLE_QUERY, QUERY_CALLBACK_GAS);
            validQueryIDs[queryId] = true;
        }
    }

    function checkSettlement() private
    {
        if(isExpired)   // already expired.
            return;

        if(now > EXPIRATION) {
            isExpired = true;   // time based expiration has occurred.
        } else if(lastPrice >= PRICE_CAP || lastPrice <= PRICE_FLOOR) {
            isExpired = true;   // we have breached/touched our pricing bands
        }

        if(isExpired) {
            settleContract();
        }
    }

    function settleContract() private
    {
        // TODO: build mechanism for distribution of collateral
        ContractSettled();
    }

    // for now lets require alot of padding for the settlement,
    function checkSufficientStartingBalance(uint secondsToExpiration) private returns (bool isSufficient) {
        //uint costPerQuery = oraclize_getPrice(ORACLE_DATA_SOURCE); this doesn't work prior to first query(its free)
        uint expectedNoOfQueries = secondsToExpiration / ORACLE_QUERY_REPEAT;
        uint approxGasRequired = COST_PER_QUERY * expectedNoOfQueries;
        return this.balance > (approxGasRequired * 2);
    }
}

/*
    Copyright 2017 Phillip A. Elsasser

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

pragma solidity ^0.4.0;

import "./Creatable.sol";
import "./oraclize/oraclizeAPI.sol";
import "./libraries/MathLib.sol";
import "./libraries/HashLib.sol";

//TODO: fix style issues
//      add accounting for users and positions
//      add failsafe for pool distribution.
//      push as much into library as possible
contract MarketContract is Creatable, usingOraclize  {
    using MathLib for uint256;
    using MathLib for int;
    using HashLib for address;

    struct UserNetPosition {
        address userAddress;
        Position[] positions;   // all open positions (lifo upon exit - allows us to not reindex array!)
        int netPosition;        // net position across all prices / executions
    }

    struct Position {
        uint price;
        int qty;
    }

    struct Order {
        address maker;
        address taker;
        address feeRecipient;
        uint makerFee;
        uint takerFee;
        uint qty;
        uint price;
        uint8 makerSide;            // 0=Buy 1=Sell
        uint expirationTimeStamp;
        bytes32 orderHash;
    }

    // constants
    string public CONTRACT_NAME;
    address public BASE_TOKEN;
    uint public PRICE_CAP;
    uint public PRICE_FLOOR;
    uint public PRICE_DECIMAL_PLACES;   // how to convert the pricing from decimal format (if valid) to integer
    uint public EXPIRATION;
    string public ORACLE_DATA_SOURCE;
    string public ORACLE_QUERY;
    uint public ORACLE_QUERY_REPEAT;
    uint8 public BUY_SIDE = 0;
    uint8 public SELL_SIDE = 1;
    uint public COST_PER_QUERY = 2 finney;    // leave static for now, price of first query from oraclize is 0
    uint public QUERY_CALLBACK_GAS = 300000;

    // state variables
    string public lastPriceQueryResult;
    uint public lastPrice;
    bool public isExpired;
    mapping(bytes32 => bool) validQueryIDs;

    // accounting
    mapping(address => UserNetPosition) addressToUserPosition;

    // events
    event NewOracleQuery(string description);
    event UpdatedLastPrice(string price);
    event ContractSettled();

    function MarketContract(
        string contractName,
        address baseToken,
        string oracleDataSource,
        string oracleQuery,
        uint oracleQueryRepeatSeconds,
        uint floorPrice,
        uint capPrice,
        uint priceDecimalPlaces,
        uint secondsToExpiration
    ) payable {

        require(capPrice > floorPrice);
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        CONTRACT_NAME = contractName;
        BASE_TOKEN = baseToken;
        PRICE_CAP = capPrice;
        PRICE_FLOOR = floorPrice;
        EXPIRATION = now + secondsToExpiration;
        ORACLE_DATA_SOURCE = oracleDataSource;
        ORACLE_QUERY = oracleQuery;
        ORACLE_QUERY_REPEAT = oracleQueryRepeatSeconds;
        PRICE_DECIMAL_PLACES = priceDecimalPlaces;
        require(checkSufficientStartingBalance(secondsToExpiration));
        queryOracle();  // schedules recursive calls to oracle
    }

    function __callback(bytes32 queryID, string result, bytes proof) {
        require(validQueryIDs[queryID]);
        require(msg.sender == oraclize_cbAddress());
        lastPriceQueryResult = result;
        lastPrice = parseInt(result, PRICE_DECIMAL_PLACES);
        UpdatedLastPrice(result);
        delete validQueryIDs[queryID];
        checkSettlement();
        if (!isExpired) {
            queryOracle();  // set up our next query
        }
    }

    function trade(address maker, address taker) {
        require(maker != address(0) && maker != taker);     // do not allow self trade
        // TODO validate orders, etc
    }

    function updatePositions(address maker, address taker, int qty, uint price) private {
        updatePosition(addressToUserPosition[maker], qty, price);   // TODO: ensure struct is passed as storage!
        // continue process for taker, but qty is opposite sign for taker
        updatePosition(addressToUserPosition[taker], qty * -1, price);   // TODO: ensure struct is passed as storage!
    }

    function updatePosition(UserNetPosition storage userNetPosition, int qty, uint price) private {
        if(userNetPosition.netPosition == 0 ||  userNetPosition.netPosition.isSameSign(qty)) {
            // new position or adding to open pos, no collateral returned
            userNetPosition.positions.push(Position(price, newNetPos)); //append array with new position
        }
        else {
            // opposite side from open position, reduce, flattened, or flipped.
            if(userNetPosition.netPosition >= qty * -1) { // pos is reduced of flattened
                reduceUserNetPosition(userNetPosition, qty, price);
            } else {    // pos is flipped, reduce and then create new open pos!
                reduceUserNetPosition(userNetPosition, userNetPosition.netPosition * -1, price); // flatten completely
                int newNetPos = userNetPosition.netPosition + qty;            // the portion remaining after flattening
                userNetPosition.positions.push(Position(price, newNetPos));   // append array with new position
            }
        }
        userNetPosition.netPosition += qty;
    }

    function reduceUserNetPosition(UserNetPosition storage userNetPos, int qty, uint price) private {
        // TODO: determine collateral to return
    }

    function queryOracle() private
    {
        if (oraclize_getPrice(ORACLE_DATA_SOURCE) > this.balance) {
            NewOracleQuery("Oraclize query was NOT sent, please add some ETH to cover for the query fee");
            lastPriceQueryResult = "FAILED"; //TODO: failsafe
        } else {
            NewOracleQuery("Oraclize query was sent, standing by for the answer..");
            bytes32 queryId = oraclize_query(ORACLE_QUERY_REPEAT, ORACLE_DATA_SOURCE, ORACLE_QUERY, QUERY_CALLBACK_GAS);
            validQueryIDs[queryId] = true;
        }
    }

    function checkSettlement() private
    {
        if(isExpired)   // already expired.
            return;

        if(now > EXPIRATION) {
            isExpired = true;   // time based expiration has occurred.
        } else if(lastPrice >= PRICE_CAP || lastPrice <= PRICE_FLOOR) {
            isExpired = true;   // we have breached/touched our pricing bands
        }

        if(isExpired) {
            settleContract();
        }
    }

    function settleContract() private
    {
        // TODO: build mechanism for distribution of collateral
        ContractSettled();
    }

    // for now lets require alot of padding for the settlement,
    function checkSufficientStartingBalance(uint secondsToExpiration) private returns (bool isSufficient) {
        //uint costPerQuery = oraclize_getPrice(ORACLE_DATA_SOURCE); this doesn't work prior to first query(its free)
        uint expectedNoOfQueries = secondsToExpiration / ORACLE_QUERY_REPEAT;
        uint approxGasRequired = COST_PER_QUERY * expectedNoOfQueries;
        return this.balance > (approxGasRequired * 2);
    }
}


fixed name of mapping

fixed name of mapping

pragma solidity ^0.4.2;

contract TokenContract {
  function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
}

contract USDZAR {

  uint256 public rate;

  struct Approval{
    address approver;
    address tokenContract;
    uint256 value;
    uint256 rate;
  }

  mapping(address => Approval) approvals;

  event Approval(
      address indexed approver
    , address indexed tokenContract
    , uint256 indexed value
    , uint256 rate
  );

  function USDZAR(uint256 rate_) {
    rate = rate_;
  }

  // TODO: Should approvals only be valid for a specified time?
  function addApproval(address requester, address tokenContract, uint256 value, uint256 rate)
    returns (bool success){

    Approval memory newApproval = Approval({
      approver: msg.sender,
      tokenContract: tokenContract,
      value: value,
      rate: rate
    });
    approvals[requester] = newApproval;
    Approval(msg.sender, tokenContract, value, rate);
    return true;
  }

  function receiveApproval(address requester, uint256 value, address tokenContract, bytes extraData)
    returns (bool success) {
    //TODO: first check both balances
    var approval = approvals[requester]
    //TODO: check that approval hasn't expired yet
    //TODO: check rate against value approved for
    TokenContract token1 = TokenContract(approval.tokenContract);
    TokenContract token2 = TokenContract(tokenContract);
    //TODO: add checks for success
    token1.transferFrom(requester, approval.approver, value);
    token2.transferFrom(approval.approver, requester, approval.value);

    return true;
  }

  function setRate(uint256 _value) {
    rate = _value;
  }

  function getRate() returns (uint256){
    return rate;
  }

  function () {
    throw;
  }
}

pragma solidity ^0.4.2;

contract TokenContract {
  function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
}

contract USDZAR {

  uint256 public rate;

  struct Approval{
    address approver;
    address tokenContract;
    uint256 value;
    uint256 rate;
  }

  mapping(address => Approval) approvals;

  event Approval(
      address indexed approver
    , address indexed tokenContract
    , uint256 indexed value
    , uint256 rate
  );

  function USDZAR(uint256 rate_) {
    rate = rate_;
  }

  // TODO: Should approvals only be valid for a specified time?
  function addApproval(address requester, address tokenContract, uint256 value, uint256 rate)
    returns (bool success){

    Approval memory newApproval = Approval({
      approver: msg.sender,
      tokenContract: tokenContract,
      value: value,
      rate: rate
    });
    mapping[requester] = newApproval;
    Approval(msg.sender, tokenContract, value, rate);
    return true;
  }

  function receiveApproval(address requester, uint256 value, address tokenContract, bytes extraData)
    returns (bool success) {
    //TODO: first check both balances
    var approval = approvals[requester]
    //TODO: check that approval hasn't expired yet
    //TODO: check rate against value approved for
    TokenContract token1 = TokenContract(approval.tokenContract);
    TokenContract token2 = TokenContract(tokenContract);
    //TODO: add checks for success
    token1.transferFrom(requester, approval.approver, value);
    token2.transferFrom(approval.approver, requester, approval.value);

    return true;
  }

  function setRate(uint256 _value) {
    rate = _value;
  }

  function getRate() returns (uint256){
    return rate;
  }

  function () {
    throw;
  }
}


Micro-optimizations.

Micro-optimizations.

pragma solidity ^0.4.0;

import "AbstractBlobStore.sol";

/**
 * @title BlobStore
 * @author Jonathan Brown <jbrown@bluedroplet.com>
 */
contract BlobStore is AbstractBlobStore {

    struct BlobInfo {               // Single slot.
        bool updatable;             // True if the blob is updatable. After creation can only be disabled.
        bool enforceRevisions;      // True if the blob is enforcing revisions. After creation can only be enabled.
        bool retractable;           // True if the blob can be retracted. After creation can only be disabled.
        bool transferable;          // True if the blob be transfered to another user or disowned. After creation can only be disabled.
        uint32 revisionCount;       // Number of revisions including revision 0.
        uint32 blockNumber;         // Block number which contains revision 0.
        address owner;              // Who owns this blob.
    }

    mapping (bytes32 => BlobInfo) blobInfo;
    mapping (bytes32 => mapping (uint => bytes32)) packedBlockNumbers;
    mapping (bytes32 => mapping (address => bool)) enabledTransfers;

    // Create a 96-bit id for this contract. This is unique across all blockchains.
    // Wait a few minutes after deploying for this id to settle.
    bytes12 contractId = bytes12(sha3(this, block.blockhash(block.number - 1)));

    /**
     * @dev A blob revision has been published.
     * @param id Id of the blob.
     * @param revisionId Id of the revision (the highest at time of logging).
     * @param blob Contents of the blob.
     */
    event logBlob(bytes32 indexed id, uint indexed revisionId, bytes blob);

    /**
     * @dev A revision has been retracted.
     * @param revisionId Id of the revision.
     */
    event logRetractRevision(bytes32 indexed id, uint revisionId);

    /**
     * @dev An entire blob has been retracted. This cannot be undone.
     * @param id Id of the blob.
     */
    event logRetract(bytes32 indexed id);

    /**
     * @dev A blob has been transfered to a new address.
     * @param id Id of the blob.
     * @param recipient The address that now owns the blob.
     */
    event logTransfer(bytes32 indexed id, address recipient);

    /**
     * @dev A blob has been disowned. This cannot be undone.
     * @param id Id of the blob.
     */
    event logDisown(bytes32 indexed id);

    /**
     * @dev A blob has been set as not updatable. This cannot be undone.
     * @param id Id of the blob.
     */
    event logSetNotUpdatable(bytes32 indexed id);

    /**
     * @dev A blob has been set as enforcing revisions. This cannot be undone.
     * @param id Id of the blob.
     */
    event logSetEnforceRevisions(bytes32 indexed id);

    /**
     * @dev A blob has been set as not retractable. This cannot be undone.
     * @param id Id of the blob.
     */
    event logSetNotRetractable(bytes32 indexed id);

    /**
     * @dev A blob has been set as not transferable. This cannot be undone.
     * @param id Id of the blob.
     */
    event logSetNotTransferable(bytes32 indexed id);

    /**
     * @dev Throw if the blob has not been used before or it has been retracted.
     * @param id Id of the blob.
     */
    modifier exists(bytes32 id) {
        BlobInfo info = blobInfo[id];
        if (info.blockNumber == 0 || info.blockNumber == uint32(-1)) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the owner of the blob is not the message sender.
     * @param id Id of the blob.
     */
    modifier isOwner(bytes32 id) {
        if (blobInfo[id].owner != msg.sender) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob is not updatable.
     * @param id Id of the blob.
     */
    modifier isUpdatable(bytes32 id) {
        if (!blobInfo[id].updatable) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob is not enforcing revisions.
     * @param id Id of the blob.
     */
    modifier isNotEnforceRevisions(bytes32 id) {
        if (blobInfo[id].enforceRevisions) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw is the blob is not retractable.
     * @param id Id of the blob.
     */
    modifier isRetractable(bytes32 id) {
        if (!blobInfo[id].retractable) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob is not transferable.
     * @param id Id of the blob.
     */
    modifier isTransferable(bytes32 id) {
        if (!blobInfo[id].transferable) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob is not transferable to a specific user.
     * @param id Id of the blob.
     * @param recipient Address of the user.
     */
    modifier isTransferEnabled(bytes32 id, address recipient) {
        if (!enabledTransfers[id][recipient]) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob only has one revision.
     * @param id Id of the blob.
     */
    modifier hasAdditionalRevisions(bytes32 id) {
        if (blobInfo[id].revisionCount < 2) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if a specific blob revision does not exist.
     * @param id Id of the blob.
     * @param revisionId Id of the revision.
     */
    modifier revisionExists(bytes32 id, uint revisionId) {
        if (revisionId >= blobInfo[id].revisionCount) {
            throw;
        }
        _;
    }

    /**
     * @dev Stores new blob in the transaction log. It is guaranteed that each user will get a different id from the same nonce.
     * @param blob Blob that should be stored.
     * @param nonce Any value that the user has not used previously to create a blob.
     * @param updatable True if the blob should be updatable.
     * @param enforceRevisions True if the blob should enforce revisions.
     * @param retractable True if the blob should be retractable.
     * @param transferable True if the blob should be transferable.
     * @param anon True if the blob should be anonymous.
     * @return id Id of the blob.
     */
    function create(bytes blob, bytes32 nonce, bool updatable, bool enforceRevisions, bool retractable, bool transferable, bool anon) external returns (bytes32 id) {
        // Determine the id.
        id = contractId | (sha3(msg.sender, nonce) & (2 ** 160 - 1));
        // Make sure this id has not been used before.
        if (blobInfo[id].blockNumber != 0) {
            throw;
        }
        // Store blob info in state.
        blobInfo[id] = BlobInfo({
            updatable: updatable,
            enforceRevisions: enforceRevisions,
            retractable: retractable,
            transferable: transferable,
            revisionCount: 1,
            blockNumber: uint32(block.number),
            owner: anon ? 0 : msg.sender,
        });
        // Store the blob in a log in the current block.
        logBlob(id, 0, blob);
    }

    /**
     * @dev Store a blob revision block number in a packed slot.
     * @param id Id of the blob.
     * @param offset The offset of the block number should be retreived.
     */
    function _setPackedBlockNumber(bytes32 id, uint offset) internal {
        // Get the slot.
        bytes32 slot = packedBlockNumbers[id][offset / 8];
        // Wipe the previous block number.
        slot &= ~bytes32(uint32(-1) * 2 ** ((offset % 8) * 32));
        // Insert the current block number.
        slot |= bytes32(uint32(block.number) * 2 ** ((offset % 8) * 32));
        // Store the slot.
        packedBlockNumbers[id][offset / 8] = slot;
    }

    /**
     * @dev Create a new blob revision.
     * @param id Id of the blob.
     * @param blob Blob that should be stored as the new revision. Typically a VCDIFF of an earlier revision.
     * @return revisionId The new revisionId.
     */
    function createNewRevision(bytes32 id, bytes blob) isOwner(id) isUpdatable(id) external returns (uint revisionId) {
        // Increment the number of revisions.
        revisionId = blobInfo[id].revisionCount++;
        // Store the block number.
        _setPackedBlockNumber(id, revisionId - 1);
        // Store the new blob in a log in the current block.
        logBlob(id, revisionId, blob);
    }

    /**
     * @dev Update a blob's latest revision.
     * @param id Id of the blob.
     * @param blob Blob that should replace the latest revision. Typically a VCDIFF if there is an earlier revision.
     */
    function updateLatestRevision(bytes32 id, bytes blob) isOwner(id) isUpdatable(id) isNotEnforceRevisions(id) external {
        BlobInfo info = blobInfo[id];
        uint revisionId = info.revisionCount - 1;
        // Update the block number.
        if (revisionId == 0) {
            info.blockNumber = uint32(block.number);
        }
        else {
            _setPackedBlockNumber(id, revisionId - 1);
        }
        // Store the new blob in a log in the current block.
        logBlob(id, revisionId, blob);
    }

    /**
     * @dev Retract a blob's latest revision. Revision 0 cannot be retracted.
     * @param id Id of the blob.
     */
    function retractLatestRevision(bytes32 id) isOwner(id) isUpdatable(id) isNotEnforceRevisions(id) hasAdditionalRevisions(id) external {
        uint revisionId = --blobInfo[id].revisionCount;
        // Delete the slot if it is no longer required.
        if (revisionId % 8 == 1) {
            delete packedBlockNumbers[id][revisionId / 8];
        }
        // Log the retraction.
        logRetractRevision(id, revisionId);
    }

    /**
     * @dev Delete all of a blob's packed revision block numbers.
     * @param id Id of the blob.
     */
    function _deleteAllPackedRevisionBlockNumbers(bytes32 id) internal {
        // Determine how many slots should be deleted.
        // Block number of the first revision is stored in the blob info, so the first slot only needs to be deleted of there are at least 2 revisions.
        uint slotCount = (blobInfo[id].revisionCount + 6) / 8;
        // Delete the slots.
        for (uint i = 0; i < slotCount; i++) {
            delete packedBlockNumbers[id][i];
        }
    }

    /**
     * @dev Delete all a blob's revisions and replace it with a new blob.
     * @param id Id of the blob.
     * @param blob Blob that should be stored.
     */
    function restart(bytes32 id, bytes blob) isOwner(id) isUpdatable(id) isNotEnforceRevisions(id) external {
        // Delete the packed revision block numbers.
        _deleteAllPackedRevisionBlockNumbers(id);
        // Update the blob state info.
        BlobInfo info = blobInfo[id];
        info.revisionCount = 1;
        info.blockNumber = uint32(block.number);
        // Store the blob in a log in the current block.
        logBlob(id, 0, blob);
    }

    /**
     * @dev Retract a blob.
     * @param id Id of the blob. This id can never be used again.
     */
    function retract(bytes32 id) isOwner(id) isRetractable(id) external {
        // Delete the packed revision block numbers.
        _deleteAllPackedRevisionBlockNumbers(id);
        // Mark this blob as retracted.
        blobInfo[id] = BlobInfo({
            updatable: false,
            enforceRevisions: false,
            retractable: false,
            transferable: false,
            revisionCount: 0,
            blockNumber: uint32(-1),
            owner: 0,
        });
        // Log that the blob has been retracted.
        logRetract(id);
    }

    /**
     * @dev Enable transfer of the blob to the current user.
     * @param id Id of the blob.
     */
    function transferEnable(bytes32 id) isTransferable(id) external {
        // Record in state that the current user will accept this blob.
        enabledTransfers[id][msg.sender] = true;
    }

    /**
     * @dev Disable transfer of the blob to the current user.
     * @param id Id of the blob.
     */
    function transferDisable(bytes32 id) isTransferEnabled(id, msg.sender) external {
        // Record in state that the current user will not accept this blob.
        enabledTransfers[id][msg.sender] = false;
    }

    /**
     * @dev Transfer a blob to a new user.
     * @param id Id of the blob.
     * @param recipient Address of the user to transfer to blob to.
     */
    function transfer(bytes32 id, address recipient) isOwner(id) isTransferable(id) isTransferEnabled(id, recipient) external {
        // Update ownership of the blob.
        blobInfo[id].owner = recipient;
        // Disable this transfer in future and free up the slot.
        enabledTransfers[id][recipient] = false;
        // Log the transfer.
        logTransfer(id, recipient);
    }

    /**
     * @dev Disown a blob.
     * @param id Id of the blob.
     */
    function disown(bytes32 id) isOwner(id) isTransferable(id) external {
        // Remove the owner from the blob's state.
        delete blobInfo[id].owner;
        // Log as blob as disowned.
        logDisown(id);
    }

    /**
     * @dev Set a blob as not updatable.
     * @param id Id of the blob.
     */
    function setNotUpdatable(bytes32 id) isOwner(id) external {
        // Record in state that the blob is not updatable.
        blobInfo[id].updatable = false;
        // Log that the blob is not updatable.
        logSetNotUpdatable(id);
    }

    /**
     * @dev Set a blob to enforce revisions.
     * @param id Id of the blob.
     */
    function setEnforceRevisions(bytes32 id) isOwner(id) external {
        // Record in state that all changes to this blob must be new revisions.
        blobInfo[id].enforceRevisions = true;
        // Log that the blob now forces new revisions.
        logSetEnforceRevisions(id);
    }

    /**
     * @dev Set a blob to not be retractable.
     * @param id Id of the blob.
     */
    function setNotRetractable(bytes32 id) isOwner(id) external {
        // Record in state that the blob is not retractable.
        blobInfo[id].retractable = false;
        // Log that the blob is not retractable.
        logSetNotRetractable(id);
    }

    /**
     * @dev Set a blob to not be transferable.
     * @param id Id of the blob.
     */
    function setNotTransferable(bytes32 id) isOwner(id) external {
        // Record in state that the blob is not transferable.
        blobInfo[id].transferable = false;
        // Log that the blob is not transferable.
        logSetNotTransferable(id);
    }

    /**
     * @dev Get the id for this BlobStore contract.
     * @return Id of the contract.
     */
    function getContractId() constant external returns (bytes12) {
        return contractId;
    }

    /**
     * @dev Check if a blob exists.
     * @param id Id of the blob.
     * @return exists True if the blob exists.
     */
    function getExists(bytes32 id) constant external returns (bool exists) {
        BlobInfo info = blobInfo[id];
        exists = info.blockNumber != 0 && info.blockNumber != uint32(-1);
    }

    /**
     * @dev Get the block number for a specific blob revision.
     * @param id Id of the blob.
     * @param revisionId Id of the revision.
     * @return blockNumber Block number of the specified revision.
     */
    function _getRevisionBlockNumber(bytes32 id, uint revisionId) internal returns (uint blockNumber) {
        if (revisionId == 0) {
            blockNumber = blobInfo[id].blockNumber;
        }
        else {
            bytes32 slot = packedBlockNumbers[id][(revisionId - 1) / 8];
            blockNumber = uint32(uint256(slot) / 2 ** (((revisionId - 1) % 8) * 32));
        }
    }

    /**
     * @dev Get the block numbers for all of a blob's revisions.
     * @param id Id of the blob.
     * @return blockNumbers Revision block numbers.
     */
    function _getAllRevisionBlockNumbers(bytes32 id) internal returns (uint[] blockNumbers) {
        uint revisionCount = blobInfo[id].revisionCount;
        blockNumbers = new uint[](revisionCount);
        for (uint revisionId = 0; revisionId < revisionCount; revisionId++) {
            blockNumbers[revisionId] = _getRevisionBlockNumber(id, revisionId);
        }
    }

    /**
     * @dev Get info about a blob.
     * @param id Id of the blob.
     * @return owner Owner of the blob.
     * @return revisionCount How many revisions the blob has.
     * @return blockNumbers The block numbers of the revisions.
     * @return updatable Is the blob updatable?
     * @return enforceRevisions Does the blob enforce revisions?
     * @return retractable Is the blob retractable?
     * @return transferable Is the blob transferable?
     */
    function getInfo(bytes32 id) exists(id) constant external returns (address owner, uint revisionCount, uint[] blockNumbers, bool updatable, bool enforceRevisions, bool retractable, bool transferable) {
        BlobInfo info = blobInfo[id];
        owner = info.owner;
        revisionCount = info.revisionCount;
        blockNumbers = _getAllRevisionBlockNumbers(id);
        updatable = info.updatable;
        enforceRevisions = info.enforceRevisions;
        retractable = info.retractable;
        transferable = info.transferable;
    }

    /**
     * @dev Get the owner of a blob.
     * @param id Id of the blob.
     * @return owner Owner of the blob.
     */
    function getOwner(bytes32 id) exists(id) constant external returns (address owner) {
        owner = blobInfo[id].owner;
    }

    /**
     * @dev Get the number of revisions a blob has.
     * @param id Id of the blob.
     * @return revisionCount How many revisions the blob has.
     */
    function getRevisionCount(bytes32 id) exists(id) constant external returns (uint revisionCount) {
        revisionCount = blobInfo[id].revisionCount;
    }

    /**
     * @dev Get the block number for a specific blob revision.
     * @param id Id of the blob.
     * @param revisionId Id of the revision.
     * @return blockNumber Block number of the specified revision.
     */
    function getRevisionBlockNumber(bytes32 id, uint revisionId) constant external returns (uint blockNumber) {
        blockNumber = _getRevisionBlockNumber(id, revisionId);
    }

    /**
     * @dev Get the block numbers for all of a blob's revisions.
     * @param id Id of the blob.
     * @return blockNumbers Revision block numbers.
     */
    function getAllRevisionBlockNumbers(bytes32 id) exists(id) constant external returns (uint[] blockNumbers) {
        blockNumbers = _getAllRevisionBlockNumbers(id);
    }

    /**
     * @dev Determine if a blob is updatable.
     * @param id Id of the blob.
     * @return updatable True if the blob is updatable.
     */
    function getUpdatable(bytes32 id) exists(id) constant external returns (bool updatable) {
        updatable = blobInfo[id].updatable;
    }

    /**
     * @dev Determine if a blob enforces revisions.
     * @param id Id of the blob.
     * @return enforceRevisions True if the blob enforces revisions.
     */
    function getEnforceRevisions(bytes32 id) exists(id) constant external returns (bool enforceRevisions) {
        enforceRevisions = blobInfo[id].enforceRevisions;
    }

    /**
     * @dev Determine if a blob is retractable.
     * @param id Id of the blob.
     * @return retractable True if the blob is blob retractable.
     */
    function getRetractable(bytes32 id) exists(id) constant external returns (bool retractable) {
        retractable = blobInfo[id].retractable;
    }

    /**
     * @dev Determine if a blob is transferable.
     * @param id Id of the blob.
     * @return transferable True if the blob is transferable.
     */
    function getTransferable(bytes32 id) exists(id) constant external returns (bool transferable) {
        transferable = blobInfo[id].transferable;
    }

}

pragma solidity ^0.4.0;

import "AbstractBlobStore.sol";

/**
 * @title BlobStore
 * @author Jonathan Brown <jbrown@bluedroplet.com>
 */
contract BlobStore is AbstractBlobStore {

    struct BlobInfo {               // Single slot.
        bool updatable;             // True if the blob is updatable. After creation can only be disabled.
        bool enforceRevisions;      // True if the blob is enforcing revisions. After creation can only be enabled.
        bool retractable;           // True if the blob can be retracted. After creation can only be disabled.
        bool transferable;          // True if the blob be transfered to another user or disowned. After creation can only be disabled.
        uint32 revisionCount;       // Number of revisions including revision 0.
        uint32 blockNumber;         // Block number which contains revision 0.
        address owner;              // Who owns this blob.
    }

    mapping (bytes32 => BlobInfo) blobInfo;
    mapping (bytes32 => mapping (uint => bytes32)) packedBlockNumbers;
    mapping (bytes32 => mapping (address => bool)) enabledTransfers;

    // Create a 96-bit id for this contract. This is unique across all blockchains.
    // Wait a few minutes after deploying for this id to settle.
    bytes12 contractId = bytes12(sha3(this, block.blockhash(block.number - 1)));

    /**
     * @dev A blob revision has been published.
     * @param id Id of the blob.
     * @param revisionId Id of the revision (the highest at time of logging).
     * @param blob Contents of the blob.
     */
    event logBlob(bytes32 indexed id, uint indexed revisionId, bytes blob);

    /**
     * @dev A revision has been retracted.
     * @param revisionId Id of the revision.
     */
    event logRetractRevision(bytes32 indexed id, uint revisionId);

    /**
     * @dev An entire blob has been retracted. This cannot be undone.
     * @param id Id of the blob.
     */
    event logRetract(bytes32 indexed id);

    /**
     * @dev A blob has been transfered to a new address.
     * @param id Id of the blob.
     * @param recipient The address that now owns the blob.
     */
    event logTransfer(bytes32 indexed id, address recipient);

    /**
     * @dev A blob has been disowned. This cannot be undone.
     * @param id Id of the blob.
     */
    event logDisown(bytes32 indexed id);

    /**
     * @dev A blob has been set as not updatable. This cannot be undone.
     * @param id Id of the blob.
     */
    event logSetNotUpdatable(bytes32 indexed id);

    /**
     * @dev A blob has been set as enforcing revisions. This cannot be undone.
     * @param id Id of the blob.
     */
    event logSetEnforceRevisions(bytes32 indexed id);

    /**
     * @dev A blob has been set as not retractable. This cannot be undone.
     * @param id Id of the blob.
     */
    event logSetNotRetractable(bytes32 indexed id);

    /**
     * @dev A blob has been set as not transferable. This cannot be undone.
     * @param id Id of the blob.
     */
    event logSetNotTransferable(bytes32 indexed id);

    /**
     * @dev Throw if the blob has not been used before or it has been retracted.
     * @param id Id of the blob.
     */
    modifier exists(bytes32 id) {
        if (blobInfo[id].blockNumber == 0 || blobInfo[id].blockNumber == uint32(-1)) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the owner of the blob is not the message sender.
     * @param id Id of the blob.
     */
    modifier isOwner(bytes32 id) {
        if (blobInfo[id].owner != msg.sender) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob is not updatable.
     * @param id Id of the blob.
     */
    modifier isUpdatable(bytes32 id) {
        if (!blobInfo[id].updatable) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob is not enforcing revisions.
     * @param id Id of the blob.
     */
    modifier isNotEnforceRevisions(bytes32 id) {
        if (blobInfo[id].enforceRevisions) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw is the blob is not retractable.
     * @param id Id of the blob.
     */
    modifier isRetractable(bytes32 id) {
        if (!blobInfo[id].retractable) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob is not transferable.
     * @param id Id of the blob.
     */
    modifier isTransferable(bytes32 id) {
        if (!blobInfo[id].transferable) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob is not transferable to a specific user.
     * @param id Id of the blob.
     * @param recipient Address of the user.
     */
    modifier isTransferEnabled(bytes32 id, address recipient) {
        if (!enabledTransfers[id][recipient]) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if the blob only has one revision.
     * @param id Id of the blob.
     */
    modifier hasAdditionalRevisions(bytes32 id) {
        if (blobInfo[id].revisionCount < 2) {
            throw;
        }
        _;
    }

    /**
     * @dev Throw if a specific blob revision does not exist.
     * @param id Id of the blob.
     * @param revisionId Id of the revision.
     */
    modifier revisionExists(bytes32 id, uint revisionId) {
        if (revisionId >= blobInfo[id].revisionCount) {
            throw;
        }
        _;
    }

    /**
     * @dev Stores new blob in the transaction log. It is guaranteed that each user will get a different id from the same nonce.
     * @param blob Blob that should be stored.
     * @param nonce Any value that the user has not used previously to create a blob.
     * @param updatable True if the blob should be updatable.
     * @param enforceRevisions True if the blob should enforce revisions.
     * @param retractable True if the blob should be retractable.
     * @param transferable True if the blob should be transferable.
     * @param anon True if the blob should be anonymous.
     * @return id Id of the blob.
     */
    function create(bytes blob, bytes32 nonce, bool updatable, bool enforceRevisions, bool retractable, bool transferable, bool anon) external returns (bytes32 id) {
        // Determine the id.
        id = contractId | (sha3(msg.sender, nonce) & (2 ** 160 - 1));
        // Make sure this id has not been used before.
        if (blobInfo[id].blockNumber != 0) {
            throw;
        }
        // Store blob info in state.
        blobInfo[id] = BlobInfo({
            updatable: updatable,
            enforceRevisions: enforceRevisions,
            retractable: retractable,
            transferable: transferable,
            revisionCount: 1,
            blockNumber: uint32(block.number),
            owner: anon ? 0 : msg.sender,
        });
        // Store the blob in a log in the current block.
        logBlob(id, 0, blob);
    }

    /**
     * @dev Store a blob revision block number in a packed slot.
     * @param id Id of the blob.
     * @param offset The offset of the block number should be retreived.
     */
    function _setPackedBlockNumber(bytes32 id, uint offset) internal {
        // Get the slot.
        bytes32 slot = packedBlockNumbers[id][offset / 8];
        // Wipe the previous block number.
        slot &= ~bytes32(uint32(-1) * 2 ** ((offset % 8) * 32));
        // Insert the current block number.
        slot |= bytes32(uint32(block.number) * 2 ** ((offset % 8) * 32));
        // Store the slot.
        packedBlockNumbers[id][offset / 8] = slot;
    }

    /**
     * @dev Create a new blob revision.
     * @param id Id of the blob.
     * @param blob Blob that should be stored as the new revision. Typically a VCDIFF of an earlier revision.
     * @return revisionId The new revisionId.
     */
    function createNewRevision(bytes32 id, bytes blob) isOwner(id) isUpdatable(id) external returns (uint revisionId) {
        // Increment the number of revisions.
        revisionId = blobInfo[id].revisionCount++;
        // Store the block number.
        _setPackedBlockNumber(id, revisionId - 1);
        // Store the new blob in a log in the current block.
        logBlob(id, revisionId, blob);
    }

    /**
     * @dev Update a blob's latest revision.
     * @param id Id of the blob.
     * @param blob Blob that should replace the latest revision. Typically a VCDIFF if there is an earlier revision.
     */
    function updateLatestRevision(bytes32 id, bytes blob) isOwner(id) isUpdatable(id) isNotEnforceRevisions(id) external {
        uint revisionId = blobInfo[id].revisionCount - 1;
        // Update the block number.
        if (revisionId == 0) {
            blobInfo[id].blockNumber = uint32(block.number);
        }
        else {
            _setPackedBlockNumber(id, revisionId - 1);
        }
        // Store the new blob in a log in the current block.
        logBlob(id, revisionId, blob);
    }

    /**
     * @dev Retract a blob's latest revision. Revision 0 cannot be retracted.
     * @param id Id of the blob.
     */
    function retractLatestRevision(bytes32 id) isOwner(id) isUpdatable(id) isNotEnforceRevisions(id) hasAdditionalRevisions(id) external {
        uint revisionId = --blobInfo[id].revisionCount;
        // Delete the slot if it is no longer required.
        if (revisionId % 8 == 1) {
            delete packedBlockNumbers[id][revisionId / 8];
        }
        // Log the retraction.
        logRetractRevision(id, revisionId);
    }

    /**
     * @dev Delete all of a blob's packed revision block numbers.
     * @param id Id of the blob.
     */
    function _deleteAllPackedRevisionBlockNumbers(bytes32 id) internal {
        // Determine how many slots should be deleted.
        // Block number of the first revision is stored in the blob info, so the first slot only needs to be deleted of there are at least 2 revisions.
        uint slotCount = (blobInfo[id].revisionCount + 6) / 8;
        // Delete the slots.
        for (uint i = 0; i < slotCount; i++) {
            delete packedBlockNumbers[id][i];
        }
    }

    /**
     * @dev Delete all a blob's revisions and replace it with a new blob.
     * @param id Id of the blob.
     * @param blob Blob that should be stored.
     */
    function restart(bytes32 id, bytes blob) isOwner(id) isUpdatable(id) isNotEnforceRevisions(id) external {
        // Delete the packed revision block numbers.
        _deleteAllPackedRevisionBlockNumbers(id);
        // Update the blob state info.
        blobInfo[id].revisionCount = 1;
        blobInfo[id].blockNumber = uint32(block.number);
        // Store the blob in a log in the current block.
        logBlob(id, 0, blob);
    }

    /**
     * @dev Retract a blob.
     * @param id Id of the blob. This id can never be used again.
     */
    function retract(bytes32 id) isOwner(id) isRetractable(id) external {
        // Delete the packed revision block numbers.
        _deleteAllPackedRevisionBlockNumbers(id);
        // Mark this blob as retracted.
        blobInfo[id] = BlobInfo({
            updatable: false,
            enforceRevisions: false,
            retractable: false,
            transferable: false,
            revisionCount: 0,
            blockNumber: uint32(-1),
            owner: 0,
        });
        // Log that the blob has been retracted.
        logRetract(id);
    }

    /**
     * @dev Enable transfer of the blob to the current user.
     * @param id Id of the blob.
     */
    function transferEnable(bytes32 id) isTransferable(id) external {
        // Record in state that the current user will accept this blob.
        enabledTransfers[id][msg.sender] = true;
    }

    /**
     * @dev Disable transfer of the blob to the current user.
     * @param id Id of the blob.
     */
    function transferDisable(bytes32 id) isTransferEnabled(id, msg.sender) external {
        // Record in state that the current user will not accept this blob.
        enabledTransfers[id][msg.sender] = false;
    }

    /**
     * @dev Transfer a blob to a new user.
     * @param id Id of the blob.
     * @param recipient Address of the user to transfer to blob to.
     */
    function transfer(bytes32 id, address recipient) isOwner(id) isTransferable(id) isTransferEnabled(id, recipient) external {
        // Update ownership of the blob.
        blobInfo[id].owner = recipient;
        // Disable this transfer in future and free up the slot.
        enabledTransfers[id][recipient] = false;
        // Log the transfer.
        logTransfer(id, recipient);
    }

    /**
     * @dev Disown a blob.
     * @param id Id of the blob.
     */
    function disown(bytes32 id) isOwner(id) isTransferable(id) external {
        // Remove the owner from the blob's state.
        delete blobInfo[id].owner;
        // Log as blob as disowned.
        logDisown(id);
    }

    /**
     * @dev Set a blob as not updatable.
     * @param id Id of the blob.
     */
    function setNotUpdatable(bytes32 id) isOwner(id) external {
        // Record in state that the blob is not updatable.
        blobInfo[id].updatable = false;
        // Log that the blob is not updatable.
        logSetNotUpdatable(id);
    }

    /**
     * @dev Set a blob to enforce revisions.
     * @param id Id of the blob.
     */
    function setEnforceRevisions(bytes32 id) isOwner(id) external {
        // Record in state that all changes to this blob must be new revisions.
        blobInfo[id].enforceRevisions = true;
        // Log that the blob now forces new revisions.
        logSetEnforceRevisions(id);
    }

    /**
     * @dev Set a blob to not be retractable.
     * @param id Id of the blob.
     */
    function setNotRetractable(bytes32 id) isOwner(id) external {
        // Record in state that the blob is not retractable.
        blobInfo[id].retractable = false;
        // Log that the blob is not retractable.
        logSetNotRetractable(id);
    }

    /**
     * @dev Set a blob to not be transferable.
     * @param id Id of the blob.
     */
    function setNotTransferable(bytes32 id) isOwner(id) external {
        // Record in state that the blob is not transferable.
        blobInfo[id].transferable = false;
        // Log that the blob is not transferable.
        logSetNotTransferable(id);
    }

    /**
     * @dev Get the id for this BlobStore contract.
     * @return Id of the contract.
     */
    function getContractId() constant external returns (bytes12) {
        return contractId;
    }

    /**
     * @dev Check if a blob exists.
     * @param id Id of the blob.
     * @return exists True if the blob exists.
     */
    function getExists(bytes32 id) constant external returns (bool exists) {
        BlobInfo info = blobInfo[id];
        exists = info.blockNumber != 0 && info.blockNumber != uint32(-1);
    }

    /**
     * @dev Get the block number for a specific blob revision.
     * @param id Id of the blob.
     * @param revisionId Id of the revision.
     * @return blockNumber Block number of the specified revision.
     */
    function _getRevisionBlockNumber(bytes32 id, uint revisionId) internal returns (uint blockNumber) {
        if (revisionId == 0) {
            blockNumber = blobInfo[id].blockNumber;
        }
        else {
            bytes32 slot = packedBlockNumbers[id][(revisionId - 1) / 8];
            blockNumber = uint32(uint256(slot) / 2 ** (((revisionId - 1) % 8) * 32));
        }
    }

    /**
     * @dev Get the block numbers for all of a blob's revisions.
     * @param id Id of the blob.
     * @return blockNumbers Revision block numbers.
     */
    function _getAllRevisionBlockNumbers(bytes32 id) internal returns (uint[] blockNumbers) {
        uint revisionCount = blobInfo[id].revisionCount;
        blockNumbers = new uint[](revisionCount);
        for (uint revisionId = 0; revisionId < revisionCount; revisionId++) {
            blockNumbers[revisionId] = _getRevisionBlockNumber(id, revisionId);
        }
    }

    /**
     * @dev Get info about a blob.
     * @param id Id of the blob.
     * @return owner Owner of the blob.
     * @return revisionCount How many revisions the blob has.
     * @return blockNumbers The block numbers of the revisions.
     * @return updatable Is the blob updatable?
     * @return enforceRevisions Does the blob enforce revisions?
     * @return retractable Is the blob retractable?
     * @return transferable Is the blob transferable?
     */
    function getInfo(bytes32 id) exists(id) constant external returns (address owner, uint revisionCount, uint[] blockNumbers, bool updatable, bool enforceRevisions, bool retractable, bool transferable) {
        BlobInfo info = blobInfo[id];
        owner = info.owner;
        revisionCount = info.revisionCount;
        blockNumbers = _getAllRevisionBlockNumbers(id);
        updatable = info.updatable;
        enforceRevisions = info.enforceRevisions;
        retractable = info.retractable;
        transferable = info.transferable;
    }

    /**
     * @dev Get the owner of a blob.
     * @param id Id of the blob.
     * @return owner Owner of the blob.
     */
    function getOwner(bytes32 id) exists(id) constant external returns (address owner) {
        owner = blobInfo[id].owner;
    }

    /**
     * @dev Get the number of revisions a blob has.
     * @param id Id of the blob.
     * @return revisionCount How many revisions the blob has.
     */
    function getRevisionCount(bytes32 id) exists(id) constant external returns (uint revisionCount) {
        revisionCount = blobInfo[id].revisionCount;
    }

    /**
     * @dev Get the block number for a specific blob revision.
     * @param id Id of the blob.
     * @param revisionId Id of the revision.
     * @return blockNumber Block number of the specified revision.
     */
    function getRevisionBlockNumber(bytes32 id, uint revisionId) constant external returns (uint blockNumber) {
        blockNumber = _getRevisionBlockNumber(id, revisionId);
    }

    /**
     * @dev Get the block numbers for all of a blob's revisions.
     * @param id Id of the blob.
     * @return blockNumbers Revision block numbers.
     */
    function getAllRevisionBlockNumbers(bytes32 id) exists(id) constant external returns (uint[] blockNumbers) {
        blockNumbers = _getAllRevisionBlockNumbers(id);
    }

    /**
     * @dev Determine if a blob is updatable.
     * @param id Id of the blob.
     * @return updatable True if the blob is updatable.
     */
    function getUpdatable(bytes32 id) exists(id) constant external returns (bool updatable) {
        updatable = blobInfo[id].updatable;
    }

    /**
     * @dev Determine if a blob enforces revisions.
     * @param id Id of the blob.
     * @return enforceRevisions True if the blob enforces revisions.
     */
    function getEnforceRevisions(bytes32 id) exists(id) constant external returns (bool enforceRevisions) {
        enforceRevisions = blobInfo[id].enforceRevisions;
    }

    /**
     * @dev Determine if a blob is retractable.
     * @param id Id of the blob.
     * @return retractable True if the blob is blob retractable.
     */
    function getRetractable(bytes32 id) exists(id) constant external returns (bool retractable) {
        retractable = blobInfo[id].retractable;
    }

    /**
     * @dev Determine if a blob is transferable.
     * @param id Id of the blob.
     * @return transferable True if the blob is transferable.
     */
    function getTransferable(bytes32 id) exists(id) constant external returns (bool transferable) {
        transferable = blobInfo[id].transferable;
    }

}