Spike to see how code looks when transcoder candidacy is separating from delegation

pragma solidity ^0.4.13;

import "./IBondingManager.sol";
import "./libraries/TranscoderPools.sol";
import "../Manager.sol";
import "../ContractRegistry.sol";
import "../LivepeerToken.sol";
import "../rounds/IRoundsManager.sol";
import "../jobs/IJobsManager.sol";

import "zeppelin-solidity/contracts/math/SafeMath.sol";

contract BondingManager is IBondingManager, Manager {
    using SafeMath for uint256;
    using TranscoderPools for TranscoderPools.TranscoderPools;

    // Token address
    LivepeerToken public token;

    // Start with 10M tokens. 1 LPT == 10^18th units
    uint256 public initialTokenSupply = 10000000 * (10 ** 18);

    // Upper bound inflation rate
    // Initially fixed at 26%
    uint8 public initialYearlyInflation = 26;

    // Time between unbonding and possible withdrawl in rounds
    uint64 public unbondingPeriod;

    // Represents a transcoder's current state
    struct Transcoder {
        uint256 delegatorWithdrawRound;                      // The round at which delegators to this transcoder can withdraw if this transcoder resigns
        uint256 lastRewardRound;                             // Last round that the transcoder called reward
        uint8 blockRewardCut;                                // % of block reward cut paid to transcoder by a delegator
        uint8 feeShare;                                      // % of fees paid to delegators by transcoder
        uint256 pricePerSegment;                             // Price per segment (denominated in LPT units) for a stream
        uint8 pendingBlockRewardCut;                         // Pending block reward cut for next round if the transcoder is active
        uint8 pendingFeeShare;                               // Pending fee share for next round if the transcoder is active
        uint256 pendingPricePerSegment;                      // Pending price per segment for next round if the transcoder is active
        mapping (uint256 => TokenPools) tokenPoolsPerRound;  // Mapping of round => token pools for the round
    }

    // The various states a transcoder can be in
    enum TranscoderStatus { NotRegistered, Registered, Resigned }

    // Represents rewards and fees to be distributed to delegators
    struct TokenPools {
        RewardPool rewardPool;
        ClaimFees[] feePool;
    }

    // Represents rewards to be distributed to delegators
    struct RewardPool {
        uint256 rewards;
        uint256 transcoderTotalStake;
    }

    // Represents fees to be distributed to delegators
    struct ClaimFees {
        uint256 claimBlock;
        uint256 fees;
        uint256 transcoderTotalStake;
    }

    // Represents a delegator's current state
    struct Delegator {
        uint256 bondedAmount;              // The amount of bonded tokens
        address delegationTarget;          // The address delegated to
        uint256 startRound;                // The round the delegator transitions to bonded phase
        uint256 delegateBlock;             // The block the delegator bonds to a transcoder
        uint256 withdrawRound;             // The round at which a delegator can withdraw
        uint256 lastStakeUpdateRound;      // The last round the delegator transitioned states
    }

    // The various states a delegator can be in
    enum DelegatorStatus { Pending, Bonded, Unbonding, Unbonded }

    // Keep track of the known transcoders and delegators
    // Note: Casper style implementation would have us using arrays and bitmaps to index these
    mapping (address => Delegator) public delegators;
    mapping (address => Transcoder) public transcoders;

    // Active and candidate transcoder pools
    TranscoderPools.TranscoderPools transcoderPools;

    // Current active transcoders for current round
    Node.Node[] activeTranscoders;
    // Mapping to track which addresses are in the current active transcoder set
    mapping (address => bool) public isActiveTranscoder;
    // Mapping to track the index position of an address in the current active transcoder set
    mapping (address => uint256) public activeTranscoderPositions;
    // Total stake of all active transcoders
    uint256 public totalActiveTranscoderStake;

    // Only the RoundsManager can call
    modifier onlyRoundsManager() {
        require(msg.sender == address(roundsManager()));
        _;
    }

    // Only the JobsManager can call
    modifier onlyJobsManager() {
        require(msg.sender == address(jobsManager()));
        _;
    }

    // Check if current round is initialized
    modifier currentRoundInitialized() {
        require(roundsManager().currentRoundInitialized());
        _;
    }

    // Update delegator stake with rewards/fees from past rounds
    modifier updateDelegatorStake() {
        Delegator storage del = delegators[msg.sender];

        if (delegatorStatus(msg.sender) == DelegatorStatus.Bonded && transcoderStatus(del.delegationTarget) == TranscoderStatus.Registered) {
            // Update stake if delegator is bonded to a registered transcoder
            del.bondedAmount = del.bondedAmount.add(delegatorTokenPoolsShare(del));
        }

        del.lastStakeUpdateRound = roundsManager().currentRound();

        _;
    }

    /*
     * @dev BondingManager constructor. Sets a pre-existing address for the LivepeerToken contract
     * @param _token LivepeerToken contract address
     */
    function BondingManager(
        address _registry,
        address _token,
        uint256 _numActiveTranscoders,
        uint64 _unbondingPeriod
    )
        Manager(_registry)
    {
        // Set LivepeerToken address
        token = LivepeerToken(_token);
        // Set unbonding period
        unbondingPeriod = _unbondingPeriod;
        // Set up transcoder pools
        transcoderPools.init(_numActiveTranscoders, _numActiveTranscoders);
    }

    /*
     * @dev The sender is declaring themselves as a candidate for active transcoding.
     * @param _blockRewardCut % of block reward paid to transcoder by a delegator
     * @param _feeShare % of fees paid to delegators by a transcoder
     * @param _pricePerSegment Price per segment (denominated in LPT units) for a stream
     */
    function transcoder(uint8 _blockRewardCut, uint8 _feeShare, uint256 _pricePerSegment)
        external
        whenSystemNotPaused
        currentRoundInitialized
        returns (bool)
    {
        // Block reward cut must a valid percentage
        require(_blockRewardCut <= 100);
        // Fee share must be a valid percentage
        require(_feeShare <= 100);
        // Sender must not be a resigned transcoder
        require(transcoderStatus(msg.sender) != TranscoderStatus.Resigned);

        Transcoder storage t = transcoders[msg.sender];
        t.pendingBlockRewardCut = _blockRewardCut;
        t.pendingFeeShare = _feeShare;
        t.pendingPricePerSegment = _pricePerSegment;

        if (transcoderStatus(msg.sender) == TranscoderStatus.NotRegistered) {
            t.delegatorWithdrawRound = 0;

            if (delegatorStatus(msg.sender) == DelegatorStatus.Bonded && delegators[msg.sender].delegationTarget == msg.sender) {
                // Sender is delegated to itself
                // Add new transcoder with delegated stake to pools. Fails for insufficient delegated stake
                transcoderPools.addTranscoder(msg.sender, delegators[msg.sender].bondedAmount);
            } else {
                // Sender is not delegated to itself
                // Add new transcoder with zero stake to pools. Fails if pools are full
                transcoderPools.addTranscoder(msg.sender, 0);
            }
        }

        return true;
    }

    /*
     * @dev Remove the sender as a transcoder
     */
    function resignAsTranscoder() external whenSystemNotPaused currentRoundInitialized returns (bool) {
        // Sender must be registered transcoder
        require(transcoderStatus(msg.sender) == TranscoderStatus.Registered);
        // Remove transcoder from pools
        transcoderPools.removeTranscoder(msg.sender);
        // Set delegator withdraw round
        transcoders[msg.sender].delegatorWithdrawRound = roundsManager().currentRound().add(unbondingPeriod);

        return true;
    }

    /**
     * @dev Delegate stake towards a specific address.
     * @param _amount The amount of LPT to stake.
     * @param _to The address of the transcoder to stake towards.
     */
    function bond(
        uint256 _amount,
        address _to
    )
        external
        whenSystemNotPaused
        currentRoundInitialized
        updateDelegatorStake
        returns (bool)
    {
        Delegator storage del = delegators[msg.sender];
        del.delegationTarget = _to;
        del.bondedAmount = del.bondedAmount.add(_amount);

        if (delegatorStatus(msg.sender) == DelegatorStatus.Unbonded
            || (del.delegationTarget != address(0) && _to != del.delegationTarget))
        {
            // New delegation target
            // Set start round
            del.startRound = roundsManager().currentRound().add(1);
            // Set delegate block
            del.delegateBlock = block.number;
        }

        if (transcoderStatus(_to) == TranscoderStatus.Registered) {
            // Bonding to registered transcoder
            uint256 stakeForTranscoder = _amount;

            if (del.delegationTarget != address(0) && _to != del.delegationTarget) {
                // New delegation target
                // Decrease old transcoder total stake
                transcoderPools.decreaseTranscoderStake(del.delegationTarget, del.bondedAmount);
                stakeForTranscoder = stakeForTranscoder.add(del.bondedAmount);
            }

            transcoderPools.increaseTranscoderStake(_to, stakeForTranscoder);
        }

        if (_amount > 0) {
            // Only transfer tokens if _amount is greater than 0
            // Transfer the token. This call throws if it fails.
            token.transferFrom(msg.sender, this, _amount);
        }

        return true;
    }

    /*
     * @dev Unbond delegator's current stake. Delegator enters unbonding state
     * @param _amount Amount of tokens to unbond
     */
    function unbond(uint256 _amount)
        external
        whenSystemNotPaused
        currentRoundInitialized
        updateDelegatorStake
        returns (bool)
    {
        // Sender must be in bonded state
        require(delegatorStatus(msg.sender) == DelegatorStatus.Bonded);

        Delegator storage del = delegators[msg.sender];

        // Transition to unbonding phase
        del.withdrawRound = roundsManager().currentRound().add(unbondingPeriod);

        if (transcoderStatus(del.delegationTarget) == TranscoderStatus.Registered) {
            // Decrease transcoder total stake
            transcoderPools.decreaseTranscoderStake(del.delegationTarget, del.bondedAmount);
        }

        // Delegator no longer bonded to anyone
        del.delegationTarget = address(0);

        return true;
    }

    /**
     * @dev Withdraws withdrawable funds back to the caller after unbonding period.
     */
    function withdraw() external whenSystemNotPaused currentRoundInitialized returns (bool) {
        // Delegator must be unbonded
        require(delegatorStatus(msg.sender) == DelegatorStatus.Unbonded);

        token.transfer(msg.sender, delegators[msg.sender].bondedAmount);

        delete delegators[msg.sender];

        return true;
    }

    /*
     * @dev Set active transcoder set for the current round
     */
    function setActiveTranscoders() external whenSystemNotPaused onlyRoundsManager returns (bool) {
        if (activeTranscoders.length != transcoderPools.candidateTranscoders.nodes.length) {
            // Set length of array if it has not already been set
            activeTranscoders.length = transcoderPools.candidateTranscoders.nodes.length;
        }

        uint256 stake = 0;

        for (uint256 i = 0; i < transcoderPools.candidateTranscoders.nodes.length; i++) {
            if (activeTranscoders[i].initialized) {
                // Set address of old node to not be present in active transcoder set
                isActiveTranscoder[activeTranscoders[i].id] = false;
            }

            // Copy node
            activeTranscoders[i] = transcoderPools.candidateTranscoders.nodes[i];

            address activeTranscoder = activeTranscoders[i].id;

            // Set address of node to be present in active transcoder set
            isActiveTranscoder[activeTranscoder] = true;
            // Set index position of node in active transcoder set
            activeTranscoderPositions[activeTranscoder] = i;
            // Set pending rates as current rates
            transcoders[activeTranscoder].blockRewardCut = transcoders[activeTranscoder].pendingBlockRewardCut;
            transcoders[activeTranscoder].feeShare = transcoders[activeTranscoder].pendingFeeShare;
            transcoders[activeTranscoder].pricePerSegment = transcoders[activeTranscoder].pendingPricePerSegment;

            stake = stake.add(transcoderTotalStake(activeTranscoder));
        }

        // Update total stake of all active transcoders
        totalActiveTranscoderStake = stake;

        return true;
    }

    /*
     * @dev Distribute the token rewards to transcoder and delegates.
     * Active transcoders call this once per cycle when it is their turn.
     */
    function reward() external whenSystemNotPaused currentRoundInitialized returns (bool) {
        // Sender must be an active transcoder
        require(isActiveTranscoder[msg.sender]);

        Transcoder storage t = transcoders[msg.sender];

        uint256 currentRound = roundsManager().currentRound();

        // Transcoder must not have called reward for this round already
        require(t.lastRewardRound != currentRound);
        // Set last round that transcoder called reward
        t.lastRewardRound = currentRound;

        // Calculate number of tokens to mint
        uint256 mintedTokens = mintedTokensPerReward(msg.sender);
        // Mint token reward and allocate to this protocol contract
        token.mint(this, mintedTokens);

        // Compute transcoder share of minted tokens
        uint256 transcoderRewardShare = mintedTokens.mul(t.blockRewardCut).div(100);

        delegators[msg.sender].bondedAmount = delegators[msg.sender].bondedAmount.add(transcoderRewardShare);

        if (delegatorStatus(msg.sender) == DelegatorStatus.Unbonded) {
            // Create new delegator if transcoder is not already a bonded delegator
            delegators[msg.sender].delegationTarget = msg.sender;
            delegators[msg.sender].startRound = currentRound;
            delegators[msg.sender].delegateBlock = block.number;
            delegators[msg.sender].withdrawRound = 0;
            delegators[msg.sender].lastStakeUpdateRound = currentRound;
        }

        // Update transcoder's reward pool for the current round
        RewardPool storage rewardPool = t.tokenPoolsPerRound[currentRound].rewardPool;
        rewardPool.rewards = rewardPool.rewards.add(mintedTokens.sub(transcoderRewardShare));

        if (rewardPool.transcoderTotalStake == 0) {
            rewardPool.transcoderTotalStake = transcoderTotalStake(msg.sender);
        }

        // Update transcoder total bonded stake with minted tokens
        transcoderPools.increaseTranscoderStake(msg.sender, mintedTokens);

        return true;
    }

    /*
     * @dev Update transcoder's fee pool
     * @param _transcoder Transcoder address
     * @param _fees Fees from verified job claims
     */
    function updateTranscoderFeePool(
        address _transcoder,
        uint256 _fees,
        uint256 _claimBlock,
        uint256 _transcoderTotalStake
    )
        external
        whenSystemNotPaused
        onlyJobsManager
        returns (bool)
    {
        // Transcoder must be registered
        require(transcoderStatus(_transcoder) == TranscoderStatus.Registered);

        Transcoder storage t = transcoders[_transcoder];

        uint256 currentRound = roundsManager().currentRound();
        uint256 delegatorsFeeShare = _fees.mul(t.feeShare).div(100);

        t.tokenPoolsPerRound[currentRound].feePool.push(ClaimFees({
            claimBlock: _claimBlock,
            fees: delegatorsFeeShare,
            transcoderTotalStake: _transcoderTotalStake
        }));

        // Calculate transcoder fees
        uint256 transcoderFeeShare = _fees.sub(delegatorsFeeShare);

        delegators[msg.sender].bondedAmount = delegators[msg.sender].bondedAmount.add(transcoderFeeShare);

        if (delegatorStatus(msg.sender) == DelegatorStatus.Unbonded) {
            // Create new delegator if transcoder is not already a bonded delegator
            delegators[msg.sender].delegationTarget = msg.sender;
            delegators[msg.sender].startRound = currentRound;
            delegators[msg.sender].delegateBlock = block.number;
            delegators[msg.sender].withdrawRound = 0;
            delegators[msg.sender].lastStakeUpdateRound = currentRound;
        }

        // Update transcoder total bonded stake with fee share
        transcoderPools.increaseTranscoderStake(_transcoder, _fees);

        return true;
    }

    /*
     * @dev Slash a transcoder. Slashing can be invoked by the protocol or a finder.
     * @param _transcoder Transcoder address
     * @param _finder Finder that proved a transcoder violated a slashing condition. Null address if there is no finder
     * @param _slashAmount Percentage of transcoder bond to be slashed
     * @param _finderFee Percentage of penalty awarded to finder. Zero if there is no finder
     */
    function slashTranscoder(
        address _transcoder,
        address _finder,
        uint64 _slashAmount,
        uint64 _finderFee
    )
        external
        whenSystemNotPaused
        onlyJobsManager
        returns (bool)
    {
        // Transcoder must be valid
        require(transcoderStatus(_transcoder) == TranscoderStatus.Registered);

        Delegator storage del = delegators[_transcoder];

        uint256 penalty = del.bondedAmount.mul(_slashAmount).div(100);

        if (penalty > del.bondedAmount) {
            // Decrease transcoder's total stake by transcoder's bond
            transcoderPools.decreaseTranscoderStake(_transcoder, del.bondedAmount);
            // Set transcoder's bond to 0 since
            // the penalty is greater than its bond
            del.bondedAmount = 0;
        } else {
            // Decrease transcoder's total stake by the penalty
            transcoderPools.decreaseTranscoderStake(_transcoder, penalty);
            // Decrease transcoder's bond
            del.bondedAmount = del.bondedAmount.sub(penalty);
        }

        // Set withdraw round for delegators
        transcoders[msg.sender].delegatorWithdrawRound = roundsManager().currentRound().add(unbondingPeriod);

        // Remove transcoder from pools
        transcoderPools.removeTranscoder(_transcoder);

        if (_finder != address(0)) {
            // Award finder fee
            token.transfer(_finder, penalty.mul(_finderFee).div(100));
        }

        return true;
    }

    /*
     * @dev Pseudorandomly elect a currently active transcoder that charges a price per segment less than or equal to the max price per segment for a job
     * Returns address of elected active transcoder and its price per segment
     * @param _maxPricePerSegment Max price (in LPT base units) per segment of a stream
     */
    function electActiveTranscoder(uint256 _maxPricePerSegment) external constant returns (address, uint256) {
        // Create array to store available transcoders charging an acceptable price per segment
        Node.Node[] memory availableTranscoders = new Node.Node[](activeTranscoders.length);
        // Keep track of the actual number of available transcoders
        uint256 numAvailableTranscoders = 0;
        // Kepp track of total stake of available transcoders
        uint256 totalAvailableTranscoderStake = 0;

        for (uint256 i = 0; i < activeTranscoders.length; i++) {
            // If a transcoders charges an acceptable price per segment add it to the array of available transcoders
            if (transcoders[activeTranscoders[i].id].pricePerSegment <= _maxPricePerSegment) {
                availableTranscoders[numAvailableTranscoders] = activeTranscoders[i];
                numAvailableTranscoders++;
                totalAvailableTranscoderStake = totalAvailableTranscoderStake.add(activeTranscoders[i].key);
            }
        }

        if (numAvailableTranscoders == 0) {
            // There is no currently available transcoder that charges a price per segment less than or equal to the max price per segment for a job
            return (address(0), 0);
        } else {
            // Pseudorandomly pick an available transcoder weighted by its stake relative to the total stake of all available transcoders
            uint256 r = uint256(block.blockhash(block.number - 1)) % totalAvailableTranscoderStake;
            uint256 s = 0;

            for (uint256 j = 0; j < numAvailableTranscoders; j++) {
                s = s.add(availableTranscoders[j].key);

                if (s > r) {
                    return (availableTranscoders[j].id, transcoders[availableTranscoders[j].id].pricePerSegment);
                }
            }

            return (availableTranscoders[numAvailableTranscoders - 1].id, transcoders[availableTranscoders[numAvailableTranscoders - 1].id].pricePerSegment);
        }

        return (address(0), 0);
    }

    /*
     * @dev Returns bonded stake for a delegator. Accounts for token distribution since last state transition
     * @param _delegator Address of delegator
     */
    function delegatorStake(address _delegator) public constant returns (uint256) {
        // Must be valid delegator
        require(delegatorStatus(_delegator) == DelegatorStatus.Bonded);

        Delegator storage del = delegators[_delegator];

        return del.bondedAmount.add(delegatorTokenPoolsShare(del));
    }

    /*
     * @dev Returns total bonded stake for an active transcoder
     * @param _transcoder Address of a transcoder
     */
    function activeTranscoderTotalStake(address _transcoder) public constant returns (uint256) {
        // Must be active transcoder
        require(isActiveTranscoder[_transcoder]);

        return activeTranscoders[activeTranscoderPositions[_transcoder]].key;
    }

    /*
     * @dev Returns total bonded stake for a transcoder
     * @param _transcoder Address of transcoder
     */
    function transcoderTotalStake(address _transcoder) public constant returns (uint256) {
        return transcoderPools.transcoderStake(_transcoder);
    }

    /*
     * @dev Computes transcoder status
     * @param _transcoder Address of transcoder
     */
    function transcoderStatus(address _transcoder) public constant returns (TranscoderStatus) {
        Transcoder storage t = transcoders[_transcoder];

        if (t.delegatorWithdrawRound > 0) {
            if (roundsManager().currentRound() >= t.delegatorWithdrawRound) {
                return TranscoderStatus.NotRegistered;
            } else {
                return TranscoderStatus.Resigned;
            }
        } else if (transcoderPools.isInPools(_transcoder)) {
            return TranscoderStatus.Registered;
        } else {
            return TranscoderStatus.NotRegistered;
        }
    }

    /*
     * @dev Computes delegator status
     * @param _delegator Address of delegator
     */
    function delegatorStatus(address _delegator) public constant returns (DelegatorStatus) {
        Delegator storage del = delegators[_delegator];

        if (del.withdrawRound > 0) {
            // Delegator called unbond
            if (roundsManager().currentRound() >= del.withdrawRound) {
                return DelegatorStatus.Unbonded;
            } else {
                return DelegatorStatus.Unbonding;
            }
        } else if (transcoderStatus(del.delegationTarget) == TranscoderStatus.NotRegistered && transcoders[del.delegationTarget].delegatorWithdrawRound > 0) {
            // Transcoder resigned
            if (roundsManager().currentRound() >= transcoders[del.delegationTarget].delegatorWithdrawRound) {
                return DelegatorStatus.Unbonded;
            } else {
                return DelegatorStatus.Unbonding;
            }
        } else if (del.startRound > roundsManager().currentRound()) {
            // Delegator round start is in the future
            return DelegatorStatus.Pending;
        } else if (del.startRound > 0 && del.startRound <= roundsManager().currentRound()) {
            // Delegator round start is now or in the past
            return DelegatorStatus.Bonded;
        } else {
            // Default to unbonded
            return DelegatorStatus.Unbonded;
        }
    }

    /*
     * @dev Return number of minted tokens for a reward call
     */
    function mintedTokensPerReward(address _transcoder) public constant returns (uint256) {
        uint256 transcoderActiveStake = activeTranscoders[activeTranscoderPositions[_transcoder]].key;
        return initialTokenSupply.mul(initialYearlyInflation).div(100).div(roundsManager().roundsPerYear()).mul(transcoderActiveStake).div(totalActiveTranscoderStake);
    }

    /*
     * @dev Return current size of candidate transcoder pool
     */
    function getCandidatePoolSize() public constant returns (uint256) {
        return transcoderPools.getCandidatePoolSize();
    }

    /*
     * @dev Return current size of reserve transcoder pool
     */
    function getReservePoolSize() public constant returns (uint256) {
        return transcoderPools.getReservePoolSize();
    }

    /*
     * @dev Return candidate transcoder at position in candidate pool
     * @param _position Position in candidate pool
     */
    function getCandidateTranscoderAtPosition(uint256 _position) public constant returns (address) {
        return transcoderPools.getCandidateTranscoderAtPosition(_position);
    }

    /*
     * @dev Return reserve transcoder at postiion in reserve pool
     * @param _position Position in reserve pool
     */
    function getReserveTranscoderAtPosition(uint256 _position) public constant returns (address) {
        return transcoderPools.getReserveTranscoderAtPosition(_position);
    }

     /*
     * @dev Computes token distribution for delegator since its last state transition
     * @param _delegator Address of delegator
     */
    function delegatorTokenPoolsShare(Delegator storage del) internal constant returns (uint256) {
        uint256 tokens = 0;
        // Iterate from round that delegator last transitioned states to current round
        // If the delegator is bonded to a transcoder, it has been bonded to the transcoder since lastStakeUpdateRound
        for (uint256 i = del.lastStakeUpdateRound; i <= roundsManager().currentRound(); i++) {
            tokens = tokens.add(delegatorRewardPoolShare(del, i)).add(delegatorFeePoolShare(del, i));
        }

        return tokens;
    }

    /*
     * @dev Computes delegator's share of reward pool for a round
     */
    function delegatorRewardPoolShare(Delegator storage del, uint256 _round) internal constant returns (uint256) {
        RewardPool storage rewardPool = transcoders[del.delegationTarget].tokenPoolsPerRound[_round].rewardPool;

        if (rewardPool.rewards == 0) {
            return 0;
        } else {
            return rewardPool.rewards.mul(del.bondedAmount).div(rewardPool.transcoderTotalStake);
        }
    }

    /*
     * @dev Computes delegator's share of fee pool for a round
     */
    function delegatorFeePoolShare(Delegator storage del, uint256 _round) internal constant returns (uint256) {
        ClaimFees[] storage feePool = transcoders[del.delegationTarget].tokenPoolsPerRound[_round].feePool;

        if (feePool.length == 0) {
            return 0;
        } else {
            uint256 feeShare = 0;

            for (uint256 i = 0; i < feePool.length; i++) {
                // Fees are only claimable if delegator bonded to transcoder before claim submission
                if (del.delegateBlock < feePool[i].claimBlock) {
                    feeShare = feeShare.add(feePool[i].fees.mul(del.bondedAmount).div(feePool[i].transcoderTotalStake));
                }
            }

            return feeShare;
        }
    }

    /*
     * @dev Return rounds manager
     */
    function roundsManager() internal constant returns (IRoundsManager) {
        return IRoundsManager(ContractRegistry(registry).registry(keccak256("RoundsManager")));
    }

    /*
     * @dev Return jobs manager
     */
    function jobsManager() internal constant returns (IJobsManager) {
        return IJobsManager(ContractRegistry(registry).registry(keccak256("JobsManager")));
    }
}

pragma solidity ^0.4.13;

import "./IBondingManager.sol";
import "./libraries/TranscoderPools.sol";
import "../Manager.sol";
import "../ContractRegistry.sol";
import "../LivepeerToken.sol";
import "../rounds/IRoundsManager.sol";
import "../jobs/IJobsManager.sol";

import "zeppelin-solidity/contracts/math/SafeMath.sol";

contract BondingManager is IBondingManager, Manager {
    using SafeMath for uint256;
    using TranscoderPools for TranscoderPools.TranscoderPools;

    // Token address
    LivepeerToken public token;

    // Start with 10M tokens. 1 LPT == 10^18th units
    uint256 public initialTokenSupply = 10000000 * (10 ** 18);

    // Upper bound inflation rate
    // Initially fixed at 26%
    uint8 public initialYearlyInflation = 26;

    // Time between unbonding and possible withdrawl in rounds
    uint64 public unbondingPeriod;

    // Represents a transcoder's current state
    struct Transcoder {
        address transcoderAddress;                           // The address of this transcoder
        uint256 bondedAmount;                                // The amount they have bonded themselves
        uint256 withdrawRound;                               // The round at which delegators to this transcoder can withdraw if this transcoder resigns
        uint256 lastRewardRound;                             // Last round that the transcoder called reward
        uint8 blockRewardCut;                                // % of block reward cut paid to transcoder by a delegator
        uint8 feeShare;                                      // % of fees paid to delegators by transcoder
        uint256 pricePerSegment;                             // Price per segment (denominated in LPT units) for a stream
        uint8 pendingBlockRewardCut;                         // Pending block reward cut for next round if the transcoder is active
        uint8 pendingFeeShare;                               // Pending fee share for next round if the transcoder is active
        uint256 pendingPricePerSegment;                      // Pending price per segment for next round if the transcoder is active
        mapping (uint256 => TokenPools) tokenPoolsPerRound;  // Mapping of round => token pools for the round
    }

    // The various states a transcoder can be in
    enum TranscoderStatus { NotRegistered, Registered, Unbonding, Unbonded }

    // Represents rewards and fees to be distributed to delegators
    struct TokenPools {
        RewardPool rewardPool;
        ClaimFees[] feePool;
    }

    // Represents rewards to be distributed to delegators
    struct RewardPool {
        uint256 rewards;
        uint256 transcoderTotalStake;
    }

    // Represents fees to be distributed to delegators
    struct ClaimFees {
        uint256 claimBlock;
        uint256 fees;
        uint256 transcoderTotalStake;
    }

    // Represents a delegator's current state
    struct Delegator {
        address delegatorAddress;          // The ethereum address of this delegator
        uint256 bondedAmount;              // The amount they have bonded
        address transcoderAddress;         // The ethereum address of the transcoder they are delgating towards
        uint256 startRound;                // The round the delegator transitions to bonded phase
        uint256 delegateBlock;             // The block the delegator bonds to a transcoder
        uint256 withdrawRound;             // The round at which a delegator can withdraw
        uint256 lastStakeUpdateRound;      // The last round the delegator transitioned states
    }

    // The various states a delegator can be in
    enum DelegatorStatus { NotRegistered, Pending, Bonded, Unbonding, Unbonded }

    // Keep track of the known transcoders and delegators
    // Note: Casper style implementation would have us using arrays and bitmaps to index these
    mapping (address => Delegator) public delegators;
    mapping (address => Transcoder) public transcoders;

    // Active and candidate transcoder pools
    TranscoderPools.TranscoderPools transcoderPools;

    // Current active transcoders for current round
    Node.Node[] activeTranscoders;
    // Mapping to track which addresses are in the current active transcoder set
    mapping (address => bool) public isActiveTranscoder;
    // Mapping to track the index position of an address in the current active transcoder set
    mapping (address => uint256) public activeTranscoderPositions;
    // Total stake of all active transcoders
    uint256 public totalActiveTranscoderStake;

    // Only the RoundsManager can call
    modifier onlyRoundsManager() {
        require(msg.sender == address(roundsManager()));
        _;
    }

    // Only the JobsManager can call
    modifier onlyJobsManager() {
        require(msg.sender == address(jobsManager()));
        _;
    }

    /*
     * @dev BondingManager constructor. Sets a pre-existing address for the LivepeerToken contract
     * @param _token LivepeerToken contract address
     */
    function BondingManager(
        address _registry,
        address _token,
        uint256 _numActiveTranscoders,
        uint64 _unbondingPeriod
    ) Manager(_registry) {
        // Set LivepeerToken address
        token = LivepeerToken(_token);

        // Set unbonding period
        unbondingPeriod = _unbondingPeriod;

        // Set up transcoder pools
        transcoderPools.init(_numActiveTranscoders, _numActiveTranscoders);
    }

    /*
     * @dev The sender is declaring themselves as a candidate for active transcoding.
     * @param _blockRewardCut % of block reward paid to transcoder by a delegator
     * @param _feeShare % of fees paid to delegators by a transcoder
     * @param _pricePerSegment Price per segment (denominated in LPT units) for a stream
     */
    function transcoder(uint8 _blockRewardCut, uint8 _feeShare, uint256 _pricePerSegment)
        external
        whenSystemNotPaused
        returns (bool)
    {
        // Current round must be initialized
        require(roundsManager().currentRoundInitialized());
        // Block reward cut must a valid percentage
        require(_blockRewardCut <= 100);
        // Fee share must be a valid percentage
        require(_feeShare <= 100);

        Transcoder storage t = transcoders[msg.sender];
        t.transcoderAddress = msg.sender;
        t.pendingBlockRewardCut = _blockRewardCut;
        t.pendingFeeShare = _feeShare;
        t.pendingPricePerSegment = _pricePerSegment;

        if (!transcoderPools.isInPools(msg.sender)) {
            transcoderPools.addTranscoder(msg.sender, 0);
        }

        return true;
    }

    /*
     * @dev Remove the sender as a transcoder
     */
    function resignAsTranscoder() external whenSystemNotPaused returns (bool) {
        // Current round must be initialized
        require(roundsManager().currentRoundInitialized());
        // Sender must be registered transcoder
        require(transcoderStatus(msg.sender) == TranscoderStatus.Registered);

        // Set withdraw round
        transcoders[msg.sender].withdrawRound = roundsManager().currentRound().add(unbondingPeriod);

        if (transcoderPools.isInPools(msg.sender)) {
            // Remove transcoder from pools
            transcoderPools.removeTranscoder(msg.sender);
        }

        return true;
    }

    /**
     * @dev Delegate stake towards a specific address.
     * @param _amount The amount of LPT to stake.
     * @param _to The address of the transcoder to stake towards.
     */
    function bond(uint256 _amount, address _to) external whenSystemNotPaused returns (bool) {
        // Current round must be initialized
        require(roundsManager().currentRoundInitialized());
        // Must bond to a valid transcoder
        require(transcoderStatus(_to) == TranscoderStatus.Registered);

        uint256 stakeForTranscoder = _amount;

        if (_to == msg.sender) {
            // Sender is a transcoder bonding to self
            transcoders[msg.sender].bondedAmount = transcoders[msg.sender].bondedAmount.add(_amount);
        } else {
            // Sender is not a transcoder
            // Update/create delegator
            Delegator storage del = delegators[msg.sender];

            // Update delegator stake if necessary
            updateDelegatorStake(msg.sender);

            if (delegatorStatus(msg.sender) == DelegatorStatus.NotRegistered
                || delegatorStatus(msg.sender) == DelegatorStatus.Unbonded)
            {
                // Registering as delegator or bonding to transcoder from the unbonded state
                // Set start round and delegate block
                del.startRound = roundsManager().currentRound().add(1);
                del.delegateBlock = block.number;
            }

            if (del.transcoderAddress != address(0) && _to != del.transcoderAddress) {
                // Delegator is moving bond
                // Set round start if delegator moves bond to another active transcoder
                del.startRound = roundsManager().currentRound().add(1);
                // Decrease former transcoder cumulative stake
                transcoderPools.decreaseTranscoderStake(del.transcoderAddress, del.bondedAmount);
                // Stake amount includes delegator's total bonded amount since delegator is moving its bond
                stakeForTranscoder = stakeForTranscoder.add(del.bondedAmount);
            }

            del.delegatorAddress = msg.sender;
            del.transcoderAddress = _to;
            del.bondedAmount = del.bondedAmount.add(_amount);
        }

        if (transcoderPools.isInPools(_to)) {
            // Target transcoder is in a pool
            // Increase transcoder cumulative stake
            transcoderPools.increaseTranscoderStake(_to, stakeForTranscoder);
        } else {
            // Target transcoder is not in a pool
            // Add transcoder
            transcoderPools.addTranscoder(_to, stakeForTranscoder);
        }

        if (_amount > 0) {
            // Only transfer tokens if _amount is greater than 0
            // Transfer the token. This call throws if it fails.
            token.transferFrom(msg.sender, this, _amount);
        }

        return true;
    }

    /*
     * @dev Unbond delegator's current stake. Delegator enters unbonding state
     */
    function unbond() external whenSystemNotPaused returns (bool) {
        // Current round must be initialized
        require(roundsManager().currentRoundInitialized());

        Delegator storage del = delegators[msg.sender];

        // Sender must be in bonded state
        require(delegatorStatus(msg.sender) == DelegatorStatus.Bonded);

        // Update delegator stake
        updateDelegatorStake(msg.sender);

        // Transition to unbonding phase
        del.withdrawRound = roundsManager().currentRound().add(unbondingPeriod);

        // Decrease transcoder total stake
        transcoderPools.decreaseTranscoderStake(del.transcoderAddress, del.bondedAmount);

        // Delegator no longer bonded to anyone
        del.transcoderAddress = address(0);

        return true;
    }

    /**
     * @dev Withdraws withdrawable funds back to the caller after unbonding period.
     */
    function withdraw() external whenSystemNotPaused returns (bool) {
        // Current round must be initialized
        require(roundsManager().currentRoundInitialized());

        if (transcoderStatus(msg.sender) == TranscoderStatus.Unbonded) {
            token.transfer(msg.sender, transcoders[msg.sender].bondedAmount);

            delete transcoders[msg.sender];
        } else if (delegatorStatus(msg.sender) == DelegatorStatus.Unbonded){
            token.transfer(msg.sender, delegators[msg.sender].bondedAmount);

            delete delegators[msg.sender];
        } else {
            // Sender is neither a transcoder or delegator
            revert();
        }

        return true;
    }

    /*
     * @dev Set active transcoder set for the current round
     */
    function setActiveTranscoders() external whenSystemNotPaused onlyRoundsManager returns (bool) {
        if (activeTranscoders.length != transcoderPools.candidateTranscoders.nodes.length) {
            // Set length of array if it has not already been set
            activeTranscoders.length = transcoderPools.candidateTranscoders.nodes.length;
        }

        uint256 stake = 0;

        for (uint256 i = 0; i < transcoderPools.candidateTranscoders.nodes.length; i++) {
            if (activeTranscoders[i].initialized) {
                // Set address of old node to not be present in active transcoder set
                isActiveTranscoder[activeTranscoders[i].id] = false;
            }

            // Copy node
            activeTranscoders[i] = transcoderPools.candidateTranscoders.nodes[i];

            address activeTranscoder = activeTranscoders[i].id;

            // Set address of node to be present in active transcoder set
            isActiveTranscoder[activeTranscoder] = true;
            // Set index position of node in active transcoder set
            activeTranscoderPositions[activeTranscoder] = i;
            // Set pending rates as current rates
            transcoders[activeTranscoder].blockRewardCut = transcoders[activeTranscoder].pendingBlockRewardCut;
            transcoders[activeTranscoder].feeShare = transcoders[activeTranscoder].pendingFeeShare;
            transcoders[activeTranscoder].pricePerSegment = transcoders[activeTranscoder].pendingPricePerSegment;

            stake = stake.add(transcoderTotalStake(activeTranscoder));
        }

        // Update total stake of all active transcoders
        totalActiveTranscoderStake = stake;

        return true;
    }

    /*
     * @dev Distribute the token rewards to transcoder and delegates.
     * Active transcoders call this once per cycle when it is their turn.
     */
    function reward() external whenSystemNotPaused returns (bool) {
        // Current round must be initialized
        require(roundsManager().currentRoundInitialized());
        // Sender must be an active transcoder
        require(isActiveTranscoder[msg.sender]);

        Transcoder storage t = transcoders[msg.sender];

        uint256 currentRound = roundsManager().currentRound();

        // Transcoder must not have called reward for this round already
        require(t.lastRewardRound != currentRound);
        // Set last round that transcoder called reward
        t.lastRewardRound = currentRound;

        // Calculate number of tokens to mint
        uint256 mintedTokens = mintedTokensPerReward(msg.sender);
        /* // Mint token reward and allocate to this protocol contract */
        token.mint(this, mintedTokens);

        // Compute transcoder share of minted tokens
        uint256 transcoderRewardShare = mintedTokens.mul(t.blockRewardCut).div(100);
        // Update transcoder's reward pool for the current round
        RewardPool storage rewardPool = t.tokenPoolsPerRound[currentRound].rewardPool;
        rewardPool.rewards = rewardPool.rewards.add(mintedTokens.sub(transcoderRewardShare));

        if (rewardPool.transcoderTotalStake == 0) {
            rewardPool.transcoderTotalStake = transcoderTotalStake(msg.sender);
        }

        // Update transcoder stake with share of minted tokens
        t.bondedAmount = t.bondedAmount.add(transcoderRewardShare);
        // Update transcoder total bonded stake with minted tokens
        transcoderPools.increaseTranscoderStake(msg.sender, mintedTokens);

        return true;
    }

    /*
     * @dev Update transcoder's fee pool
     * @param _transcoder Transcoder address
     * @param _fees Fees from verified job claims
     */
    function updateTranscoderFeePool(
        address _transcoder,
        uint256 _fees,
        uint256 _claimBlock,
        uint256 _transcoderTotalStake
    )
        external
        whenSystemNotPaused
        onlyJobsManager
        returns (bool)
    {
        Transcoder storage t = transcoders[_transcoder];

        // Transcoder must be valid
        require(transcoderStatus(_transcoder) == TranscoderStatus.Registered);

        uint256 currentRound = roundsManager().currentRound();
        uint256 delegatorsFeeShare = _fees.mul(t.feeShare).div(100);

        t.tokenPoolsPerRound[currentRound].feePool.push(ClaimFees({
            claimBlock: _claimBlock,
            fees: delegatorsFeeShare,
            transcoderTotalStake: _transcoderTotalStake
        }));

        // Calculate transcoder fees including share of delegator fees based upon the amount it bonded to self
        uint256 transcoderFeeShare = _fees.sub(delegatorsFeeShare).add(delegatorsFeeShare.mul(t.bondedAmount).div(transcoderTotalStake(_transcoder)));
        // Update transcoder stake with fee share
        t.bondedAmount = t.bondedAmount.add(transcoderFeeShare);
        // Update transcoder total bonded stake with fee share
        transcoderPools.increaseTranscoderStake(_transcoder, _fees);

        return true;
    }

    /*
     * @dev Slash a transcoder. Slashing can be invoked by the protocol or a finder.
     * @param _transcoder Transcoder address
     * @param _finder Finder that proved a transcoder violated a slashing condition. Null address if there is no finder
     * @param _slashAmount Percentage of transcoder bond to be slashed
     * @param _finderFee Percentage of penalty awarded to finder. Zero if there is no finder
     */
    function slashTranscoder(
        address _transcoder,
        address _finder,
        uint64 _slashAmount,
        uint64 _finderFee
    )
        external
        whenSystemNotPaused
        onlyJobsManager
        returns (bool)
    {
        // Transcoder must be valid
        require(transcoderStatus(_transcoder) == TranscoderStatus.Registered);

        Transcoder storage t = transcoders[_transcoder];

        uint256 penalty = t.bondedAmount.mul(_slashAmount).div(100);

        if (penalty > t.bondedAmount) {
            // Decrease transcoder's total stake by transcoder's bond
            transcoderPools.decreaseTranscoderStake(_transcoder, t.bondedAmount);
            // Set transcoder's bond to 0 since
            // the penalty is greater than its bond
            t.bondedAmount = 0;
        } else {
            // Decrease transcoder's total stake by the penalty
            transcoderPools.decreaseTranscoderStake(_transcoder, penalty);
            // Decrease transcoder's bond
            t.bondedAmount = t.bondedAmount.sub(penalty);
        }

        // Set withdraw round for delegators
        transcoders[msg.sender].withdrawRound = roundsManager().currentRound().add(unbondingPeriod);

        if (transcoderPools.isInPools(msg.sender)) {
            // Remove transcoder from pools
            transcoderPools.removeTranscoder(msg.sender);
        }

        if (_finder != address(0)) {
            // Award finder fee
            token.transfer(_finder, penalty.mul(_finderFee).div(100));
        }

        return true;
    }

    /*
     * @dev Pseudorandomly elect a currently active transcoder that charges a price per segment less than or equal to the max price per segment for a job
     * Returns address of elected active transcoder and its price per segment
     * @param _maxPricePerSegment Max price (in LPT base units) per segment of a stream
     */
    function electActiveTranscoder(uint256 _maxPricePerSegment) external constant returns (address, uint256) {
        // Create array to store available transcoders charging an acceptable price per segment
        Node.Node[] memory availableTranscoders = new Node.Node[](activeTranscoders.length);
        // Keep track of the actual number of available transcoders
        uint256 numAvailableTranscoders = 0;
        // Kepp track of total stake of available transcoders
        uint256 totalAvailableTranscoderStake = 0;

        for (uint256 i = 0; i < activeTranscoders.length; i++) {
            // If a transcoders charges an acceptable price per segment add it to the array of available transcoders
            if (transcoders[activeTranscoders[i].id].pricePerSegment <= _maxPricePerSegment) {
                availableTranscoders[numAvailableTranscoders] = activeTranscoders[i];
                numAvailableTranscoders++;
                totalAvailableTranscoderStake = totalAvailableTranscoderStake.add(activeTranscoders[i].key);
            }
        }

        if (numAvailableTranscoders == 0) {
            // There is no currently available transcoder that charges a price per segment less than or equal to the max price per segment for a job
            return (address(0), 0);
        } else {
            // Pseudorandomly pick an available transcoder weighted by its stake relative to the total stake of all available transcoders
            uint256 r = uint256(block.blockhash(block.number - 1)) % totalAvailableTranscoderStake;
            uint256 s = 0;

            for (uint256 j = 0; j < numAvailableTranscoders; j++) {
                s = s.add(availableTranscoders[j].key);

                if (s > r) {
                    return (availableTranscoders[j].id, transcoders[availableTranscoders[j].id].pricePerSegment);
                }
            }

            return (availableTranscoders[numAvailableTranscoders - 1].id, transcoders[availableTranscoders[numAvailableTranscoders - 1].id].pricePerSegment);
        }

        return (address(0), 0);
    }

    /*
     * @dev Update delegator and transcoder stake with rewards from past rounds when a delegator calls bond() or unbond()
     * @param _target Address of delegator/transcoder
     */
    function updateDelegatorStake(address _delegator) public returns (bool) {
        Delegator storage del = delegators[_delegator];

        if (delegatorStatus(_delegator) == DelegatorStatus.Bonded) {
            del.bondedAmount = del.bondedAmount.add(delegatorTokenPoolsShare(del));
        }

        del.lastStakeUpdateRound = roundsManager().currentRound();

        return true;
    }

    /*
     * @dev Returns bonded stake for a delegator. Accounts for token distribution since last state transition
     * @param _delegator Address of delegator
     */
    function delegatorStake(address _delegator) public constant returns (uint256) {
        Delegator storage del = delegators[_delegator];

        // Must be valid delegator
        require(delegatorStatus(_delegator) == DelegatorStatus.Bonded);

        return del.bondedAmount.add(delegatorTokenPoolsShare(del));
    }

    /*
     * @dev Returns total bonded stake for an active transcoder
     * @param _transcoder Address of a transcoder
     */
    function activeTranscoderTotalStake(address _transcoder) public constant returns (uint256) {
        // Must be active transcoder
        require(isActiveTranscoder[_transcoder]);

        return activeTranscoders[activeTranscoderPositions[_transcoder]].key;
    }

    /*
     * @dev Returns total bonded stake for a transcoder
     * @param _transcoder Address of transcoder
     */
    function transcoderTotalStake(address _transcoder) public constant returns (uint256) {
        return transcoderPools.transcoderStake(_transcoder);
    }

    /*
     * @dev Computes transcoder status
     * @param _transcoder Address of transcoder
     */
    function transcoderStatus(address _transcoder) public constant returns (TranscoderStatus) {
        Transcoder storage t = transcoders[_transcoder];

        if (t.withdrawRound > 0) {
            // Transcoder resigned
            if (roundsManager().currentRound() >= t.withdrawRound) {
                return TranscoderStatus.Unbonded;
            } else {
                return TranscoderStatus.Unbonding;
            }
        } else if (t.transcoderAddress != address(0)) {
            // Transcoder registered
            return TranscoderStatus.Registered;
        } else {
            // Default to not registered
            return TranscoderStatus.NotRegistered;
        }
    }

    /*
     * @dev Computes delegator status
     * @param _delegator Address of delegator
     */
    function delegatorStatus(address _delegator) public constant returns (DelegatorStatus) {
        Delegator storage del = delegators[_delegator];

        if (del.withdrawRound > 0) {
            // Delegator called unbond
            if (roundsManager().currentRound() >= del.withdrawRound) {
                return DelegatorStatus.Unbonded;
            } else {
                return DelegatorStatus.Unbonding;
            }
        } else if (del.transcoderAddress != address(0) && transcoders[del.transcoderAddress].withdrawRound > 0) {
            // Transcoder resigned
            if (roundsManager().currentRound() >= transcoders[del.transcoderAddress].withdrawRound) {
                return DelegatorStatus.Unbonded;
            } else {
                return DelegatorStatus.Unbonding;
            }
        } else if (del.startRound > roundsManager().currentRound()) {
            // Delegator round start is in the future
            return DelegatorStatus.Pending;
        } else if (del.startRound > 0 && del.startRound <= roundsManager().currentRound()) {
            // Delegator round start is now or in the past
            return DelegatorStatus.Bonded;
        } else {
            // Default to not registered
            return DelegatorStatus.NotRegistered;
        }
    }

    /*
     * @dev Return number of minted tokens for a reward call
     */
    function mintedTokensPerReward(address _transcoder) public constant returns (uint256) {
        uint256 transcoderActiveStake = activeTranscoders[activeTranscoderPositions[_transcoder]].key;
        return initialTokenSupply.mul(initialYearlyInflation).div(100).div(roundsManager().roundsPerYear()).mul(transcoderActiveStake).div(totalActiveTranscoderStake);
    }

    /*
     * @dev Return current size of candidate transcoder pool
     */
    function getCandidatePoolSize() public constant returns (uint256) {
        return transcoderPools.getCandidatePoolSize();
    }

    /*
     * @dev Return current size of reserve transcoder pool
     */
    function getReservePoolSize() public constant returns (uint256) {
        return transcoderPools.getReservePoolSize();
    }

    /*
     * @dev Return candidate transcoder at position in candidate pool
     * @param _position Position in candidate pool
     */
    function getCandidateTranscoderAtPosition(uint256 _position) public constant returns (address) {
        return transcoderPools.getCandidateTranscoderAtPosition(_position);
    }

    /*
     * @dev Return reserve transcoder at postiion in reserve pool
     * @param _position Position in reserve pool
     */
    function getReserveTranscoderAtPosition(uint256 _position) public constant returns (address) {
        return transcoderPools.getReserveTranscoderAtPosition(_position);
    }

     /*
     * @dev Computes token distribution for delegator since its last state transition
     * @param _delegator Address of delegator
     */
    function delegatorTokenPoolsShare(Delegator storage del) internal constant returns (uint256) {
        uint256 tokens = 0;

        if (del.transcoderAddress != address(0)) {
            // Iterate from round that delegator last transitioned states to current round
            // If the delegator is bonded to a transcoder, it has been bonded to the transcoder since lastStakeUpdateRound
            for (uint256 i = del.lastStakeUpdateRound; i <= roundsManager().currentRound(); i++) {
                tokens = tokens.add(delegatorRewardPoolShare(del, i)).add(delegatorFeePoolShare(del, i));
            }
        }

        return tokens;
    }

    /*
     * @dev Computes delegator's share of reward pool for a round
     */
    function delegatorRewardPoolShare(Delegator storage del, uint256 _round) internal constant returns (uint256) {
        RewardPool storage rewardPool = transcoders[del.transcoderAddress].tokenPoolsPerRound[_round].rewardPool;

        if (rewardPool.rewards == 0) {
            return 0;
        } else {
            return rewardPool.rewards.mul(del.bondedAmount).div(rewardPool.transcoderTotalStake);
        }
    }

    /*
     * @dev Computes delegator's share of fee pool for a round
     */
    function delegatorFeePoolShare(Delegator storage del, uint256 _round) internal constant returns (uint256) {
        ClaimFees[] storage feePool = transcoders[del.transcoderAddress].tokenPoolsPerRound[_round].feePool;

        if (feePool.length == 0) {
            return 0;
        } else {
            uint256 feeShare = 0;

            for (uint256 i = 0; i < feePool.length; i++) {
                // Fees are only claimable if delegator bonded to transcoder before claim submission
                if (del.delegateBlock < feePool[i].claimBlock) {
                    feeShare = feeShare.add(feePool[i].fees.mul(del.bondedAmount).div(feePool[i].transcoderTotalStake));
                }
            }

            return feeShare;
        }
    }

    /*
     * @dev Return rounds manager
     */
    function roundsManager() internal constant returns (IRoundsManager) {
        return IRoundsManager(ContractRegistry(registry).registry(keccak256("RoundsManager")));
    }

    /*
     * @dev Return jobs manager
     */
    function jobsManager() internal constant returns (IJobsManager) {
        return IJobsManager(ContractRegistry(registry).registry(keccak256("JobsManager")));
    }
}


DOC: improved code comments

DOC: improved code comments

pragma solidity ^0.4.17;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Marketplace.sol";

contract TestMarketplace {
    Marketplace marketplace = Marketplace(DeployedAddresses.Marketplace());

    // Testing the subscribe() function
    function testUserCanSubscribe() public {
        uint returnedId = marketplace.subscribe(2);

        uint expected = 2;

        Assert.equal(returnedId, expected, "Data source 2 should be recorded.");
    }

    // Testing retrieval of a single subscriber
    function testGetSubscriberAddressByDataSourceId() public {
        // Expected owner is this contract
        address expected = this;

        address subscriber = marketplace.subscribers(2);

        Assert.equal(subscriber, expected, "Subscriber of data source ID 2 should be recorded.");
    }

    // Testing retrieval of all subscribers
    function testGetSubscriberAddressByDataSourceIdInArray() public {
        // Expected subscriber is this contract
        address expected = this;

        // Store subscribers in memory rather than contract's storage
        address[16] memory subscribers = marketplace.getSubscribers();

        Assert.equal(subscribers[2], expected, "Subscriber of data source 2 should be recorded.");
    }
}

pragma solidity ^0.4.17;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Marketplace.sol";

contract TestMarketplace {
    Marketplace marketplace = Marketplace(DeployedAddresses.Marketplace());

    // Testing the subscribe() function
    function testUserCanSubscribe() public {
        uint returnedId = marketplace.subscribe(2);

        uint expected = 2;

        Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
    }

    // Testing retrieval of a single subscriber
    function testGetSubscriberAddressByDataSourceId() public {
        // Expected owner is this contract
        address expected = this;

        address subscriber = marketplace.subscribers(2);

        Assert.equal(subscriber, expected, "Owner of data source ID 0 should be recorded.");
    }

    // Testing retrieval of all subscribers
    function testGetSubscriberAddressByDataSourceIdInArray() public {
        // Expected subscriber is this contract
        address expected = this;

        // Store subscribers in memory rather than contract's storage
        address[16] memory subscribers = marketplace.getSubscribers();

        Assert.equal(subscribers[2], expected, "Subscriber of data source 2 should be recorded.");
    }
}


Make VestingWallet token event argument indexed (#2988)

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (finance/VestingWallet.sol)
pragma solidity ^0.8.0;

import "../token/ERC20/utils/SafeERC20.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
import "../utils/math/Math.sol";

/**
 * @title VestingWallet
 * @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens
 * can be given to this contract, which will release the token to the beneficiary following a given vesting schedule.
 * The vesting schedule is customizable through the {vestedAmount} function.
 *
 * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning.
 * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
 * be immediately releasable.
 */
contract VestingWallet is Context {
    event EtherReleased(uint256 amount);
    event ERC20Released(address indexed token, uint256 amount);

    uint256 private _released;
    mapping(address => uint256) private _erc20Released;
    address private immutable _beneficiary;
    uint64 private immutable _start;
    uint64 private immutable _duration;

    /**
     * @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet.
     */
    constructor(
        address beneficiaryAddress,
        uint64 startTimestamp,
        uint64 durationSeconds
    ) {
        require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address");
        _beneficiary = beneficiaryAddress;
        _start = startTimestamp;
        _duration = durationSeconds;
    }

    /**
     * @dev The contract should be able to receive Eth.
     */
    receive() external payable virtual {}

    /**
     * @dev Getter for the beneficiary address.
     */
    function beneficiary() public view virtual returns (address) {
        return _beneficiary;
    }

    /**
     * @dev Getter for the start timestamp.
     */
    function start() public view virtual returns (uint256) {
        return _start;
    }

    /**
     * @dev Getter for the vesting duration.
     */
    function duration() public view virtual returns (uint256) {
        return _duration;
    }

    /**
     * @dev Amount of eth already released
     */
    function released() public view virtual returns (uint256) {
        return _released;
    }

    /**
     * @dev Amount of token already released
     */
    function released(address token) public view virtual returns (uint256) {
        return _erc20Released[token];
    }

    /**
     * @dev Release the native token (ether) that have already vested.
     *
     * Emits a {TokensReleased} event.
     */
    function release() public virtual {
        uint256 releasable = vestedAmount(uint64(block.timestamp)) - released();
        _released += releasable;
        emit EtherReleased(releasable);
        Address.sendValue(payable(beneficiary()), releasable);
    }

    /**
     * @dev Release the tokens that have already vested.
     *
     * Emits a {TokensReleased} event.
     */
    function release(address token) public virtual {
        uint256 releasable = vestedAmount(token, uint64(block.timestamp)) - released(token);
        _erc20Released[token] += releasable;
        emit ERC20Released(token, releasable);
        SafeERC20.safeTransfer(IERC20(token), beneficiary(), releasable);
    }

    /**
     * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve.
     */
    function vestedAmount(uint64 timestamp) public view virtual returns (uint256) {
        return _vestingSchedule(address(this).balance + released(), timestamp);
    }

    /**
     * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.
     */
    function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) {
        return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp);
    }

    /**
     * @dev Virtual implementation of the vesting formula. This returns the amout vested, as a function of time, for
     * an asset given its total historical allocation.
     */
    function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
        if (timestamp < start()) {
            return 0;
        } else if (timestamp > start() + duration()) {
            return totalAllocation;
        } else {
            return (totalAllocation * (timestamp - start())) / duration();
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.3.2 (finance/VestingWallet.sol)
pragma solidity ^0.8.0;

import "../token/ERC20/utils/SafeERC20.sol";
import "../utils/Address.sol";
import "../utils/Context.sol";
import "../utils/math/Math.sol";

/**
 * @title VestingWallet
 * @dev This contract handles the vesting of Eth and ERC20 tokens for a given beneficiary. Custody of multiple tokens
 * can be given to this contract, which will release the token to the beneficiary following a given vesting schedule.
 * The vesting schedule is customizable through the {vestedAmount} function.
 *
 * Any token transferred to this contract will follow the vesting schedule as if they were locked from the beginning.
 * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
 * be immediately releasable.
 */
contract VestingWallet is Context {
    event EtherReleased(uint256 amount);
    event ERC20Released(address token, uint256 amount);

    uint256 private _released;
    mapping(address => uint256) private _erc20Released;
    address private immutable _beneficiary;
    uint64 private immutable _start;
    uint64 private immutable _duration;

    /**
     * @dev Set the beneficiary, start timestamp and vesting duration of the vesting wallet.
     */
    constructor(
        address beneficiaryAddress,
        uint64 startTimestamp,
        uint64 durationSeconds
    ) {
        require(beneficiaryAddress != address(0), "VestingWallet: beneficiary is zero address");
        _beneficiary = beneficiaryAddress;
        _start = startTimestamp;
        _duration = durationSeconds;
    }

    /**
     * @dev The contract should be able to receive Eth.
     */
    receive() external payable virtual {}

    /**
     * @dev Getter for the beneficiary address.
     */
    function beneficiary() public view virtual returns (address) {
        return _beneficiary;
    }

    /**
     * @dev Getter for the start timestamp.
     */
    function start() public view virtual returns (uint256) {
        return _start;
    }

    /**
     * @dev Getter for the vesting duration.
     */
    function duration() public view virtual returns (uint256) {
        return _duration;
    }

    /**
     * @dev Amount of eth already released
     */
    function released() public view virtual returns (uint256) {
        return _released;
    }

    /**
     * @dev Amount of token already released
     */
    function released(address token) public view virtual returns (uint256) {
        return _erc20Released[token];
    }

    /**
     * @dev Release the native token (ether) that have already vested.
     *
     * Emits a {TokensReleased} event.
     */
    function release() public virtual {
        uint256 releasable = vestedAmount(uint64(block.timestamp)) - released();
        _released += releasable;
        emit EtherReleased(releasable);
        Address.sendValue(payable(beneficiary()), releasable);
    }

    /**
     * @dev Release the tokens that have already vested.
     *
     * Emits a {TokensReleased} event.
     */
    function release(address token) public virtual {
        uint256 releasable = vestedAmount(token, uint64(block.timestamp)) - released(token);
        _erc20Released[token] += releasable;
        emit ERC20Released(token, releasable);
        SafeERC20.safeTransfer(IERC20(token), beneficiary(), releasable);
    }

    /**
     * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve.
     */
    function vestedAmount(uint64 timestamp) public view virtual returns (uint256) {
        return _vestingSchedule(address(this).balance + released(), timestamp);
    }

    /**
     * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.
     */
    function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) {
        return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp);
    }

    /**
     * @dev Virtual implementation of the vesting formula. This returns the amout vested, as a function of time, for
     * an asset given its total historical allocation.
     */
    function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
        if (timestamp < start()) {
            return 0;
        } else if (timestamp > start() + duration()) {
            return totalAllocation;
        } else {
            return (totalAllocation * (timestamp - start())) / duration();
        }
    }
}


changed 'pronounce' for 'proclaim'

changed 'pronounce' for 'proclaim'

pragma solidity ^0.4.11;

import './Ownable.sol';

/**
 * @title Inheritable
 * @dev The Inheritable contract provides ownership transfer capabilities, in the
 * case that the current owner stops "heartbeating". Only the heir can pronounce the
 * owner's death.
 */
contract Inheritable is Ownable {
  address public heir;

  // Time window the owner has to notify they are alive.
  uint public heartbeatTimeout;

  // Timestamp of the owner's death, as pronounced by the heir.
  uint public timeOfDeath;

  event HeirChanged(address indexed owner, address indexed newHeir);
  event OwnerHeartbeated(address indexed owner);
  event OwnerProclaimedDead(address indexed owner, address indexed heir, uint timeOfDeath);
  event Inherited(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev Throw an exception if called by any account other than the heir's.
   */
  modifier onlyHeir() {
    require(msg.sender == heir);
    _;
  }

  /**
   * @notice Create a new Inheritable Contract with heir address 0x0.
   * @param _heartbeatTimeout time available for the owner to notify they are alive,
   * before the heir can take ownership.
   */
  function Inheritable(uint _heartbeatTimeout) public {
    heartbeatTimeout = _heartbeatTimeout;
  }

  function setHeir(address newHeir) public onlyOwner {
    heartbeat();
    HeirChanged(owner, newHeir);
    heir = newHeir;
  }

  /**
   * @dev set heir = 0x0
   */
  function removeHeir() public onlyOwner {
    heartbeat();
    heir = 0;
  }

  function setHeartbeatTimeout(uint newHeartbeatTimeout) public onlyOwner {
    require(ownerLives());
    heartbeatTimeout = newHeartbeatTimeout;
  }

  /**
   * @dev Heir can pronounce the owners death. To inherit the ownership, they will
   * have to wait for `heartbeatTimeout` seconds.
   */
  function proclaimDeath() public onlyHeir {
    require(ownerLives());
    OwnerProclaimedDead(owner, heir, timeOfDeath);
    timeOfDeath = now;
  }

  /**
   * @dev Owner can send a heartbeat if they were mistakenly pronounced dead.
   */
  function heartbeat() public onlyOwner {
    OwnerHeartbeated(owner);
    timeOfDeath = 0;
  }

  /**
   * @dev Allows heir to transfer ownership only if heartbeat has timed out.
   */
  function inherit() public onlyHeir {
    require(!ownerLives());
    require(now >= timeOfDeath + heartbeatTimeout);
    Inherited(owner, heir);
    owner = heir;
    timeOfDeath = 0;
  }

  function ownerLives() internal returns (bool) {
    return timeOfDeath == 0;
  }
}

pragma solidity ^0.4.11;

import './Ownable.sol';

/**
 * @title Inheritable
 * @dev The Inheritable contract provides ownership transfer capabilities, in the
 * case that the current owner stops "heartbeating". Only the heir can pronounce the
 * owner's death.
 */
contract Inheritable is Ownable {
  address public heir;

  // Time window the owner has to notify they are alive.
  uint public heartbeatTimeout;

  // Timestamp of the owner's death, as pronounced by the heir.
  uint public timeOfDeath;

  event HeirChanged(address indexed owner, address indexed newHeir);
  event OwnerHeartbeated(address indexed owner);
  event OwnerPronouncedDead(address indexed owner, address indexed heir, uint timeOfDeath);
  event Inherited(address indexed previousOwner, address indexed newOwner);

  /**
   * @dev Throw an exception if called by any account other than the heir's.
   */
  modifier onlyHeir() {
    require(msg.sender == heir);
    _;
  }

  /**
   * @notice Create a new Inheritable Contract with heir address 0x0.
   * @param _heartbeatTimeout time available for the owner to notify they are alive,
   * before the heir can take ownership.
   */
  function Inheritable(uint _heartbeatTimeout) public {
    heartbeatTimeout = _heartbeatTimeout;
  }

  function setHeir(address newHeir) public onlyOwner {
    heartbeat();
    HeirChanged(owner, newHeir);
    heir = newHeir;
  }

  /**
   * @dev set heir = 0x0
   */
  function removeHeir() public onlyOwner {
    heartbeat();
    heir = 0;
  }

  function setHeartbeatTimeout(uint newHeartbeatTimeout) public onlyOwner {
    require(ownerLives());
    heartbeatTimeout = newHeartbeatTimeout;
  }

  /**
   * @dev Heir can pronounce the owners death. To inherit the ownership, they will
   * have to wait for `heartbeatTimeout` seconds.
   */
  function pronounceDeath() public onlyHeir {
    require(ownerLives());
    OwnerPronouncedDead(owner, heir, timeOfDeath);
    timeOfDeath = now;
  }

  /**
   * @dev Owner can send a heartbeat if they were mistakenly pronounced dead.
   */
  function heartbeat() public onlyOwner {
    OwnerHeartbeated(owner);
    timeOfDeath = 0;
  }

  /**
   * @dev Allows heir to transfer ownership only if heartbeat has timed out.
   */
  function inherit() public onlyHeir {
    require(!ownerLives());
    require(now >= timeOfDeath + heartbeatTimeout);
    Inherited(owner, heir);
    owner = heir;
    timeOfDeath = 0;
  }

  function ownerLives() internal returns (bool) {
    return timeOfDeath == 0;
  }
}


Make NeufundTestToken accept supply in ether

pragma solidity ^0.4.2;

import "./StandardToken.sol";

contract NeufundTestToken is StandardToken {
    function NeufundTestToken(uint totalSupplyInEther) StandardToken(totalSupplyInEther * 1 ether) {}
}

pragma solidity ^0.4.2;

import "./StandardToken.sol";

contract NeufundTestToken is StandardToken {
    function NeufundTestToken(uint _totalSupply) StandardToken(_totalSupply) {}
}


Fixed constructor name

Fixed constructor name

import "BulletinBoard.sol";

contract UnanimousConsent {

	enum ConsentState {
		NONE,
		CONSENTED,
		EXECUTED
	}

	mapping (bytes32 =>
			mapping (address => ConsentState)) consentStates;
	address[] participants;
	BulletinBoard public bulletinBoard;

	modifier onlySelf {
		if (msg.sender == address(this)) {
			_
		} else {
			throw;
		}
	}

	function UnanimousConsent(address[] _participants) {
		participants = _participants;
		bulletinBoard = new BulletinBoard(this);
	}

	function eval(
		address[] _calledContracts,
		bytes4[] _methodSignatures,
		uint[] _argumentLengths,
		bytes32[] _arguments
	) {
		if (
			_calledContracts.length != _methodSignatures.length ||
			_calledContracts.length != _argumentLengths.length
		) {
			throw;
		}

		uint i;
		uint j;
		bytes32 hash = sha3(_calledContracts, _methodSignatures, _argumentLengths, _arguments);

		for (i = 0; i < participants.length; i++) {
			if (consentStates[hash][participants[i]] == ConsentState.CONSENTED) {
				consentStates[hash][participants[i]] = ConsentState.EXECUTED;
			} else {
				throw;
			}
		}

		uint argumentsIndex = 0;
		for (i = 0; i < _calledContracts.length; i++) {

			bytes32[] memory arguments = new bytes32[](_argumentLengths[i]);
			for (j = 0; j < _argumentLengths[i]; j++) {
				arguments[j] = _arguments[argumentsIndex++];
			}

			if (!_calledContracts[i].call(_methodSignatures[i], arguments[i])) {
				throw;
			}
		}
	}

	function consent(bytes32 _hash) {
		consentStates[_hash][msg.sender] = ConsentState.CONSENTED;
	}

	function sendEther(address _recipient, uint _amount) external onlySelf {
		if (!_recipient.send(_amount)) {
			throw;
		}
	}

	function changeParticipants(address[] _participants) external onlySelf {
		participants = _participants;
	}

	function clean(bytes32[] _hashes, address[] _participants) external onlySelf {
		for (uint i = 0; i < _hashes.length; i++) {
			for (uint j = 0; j < _participants.length; j++) {
				delete consentStates[_hashes[i]][_participants[j]];
			}
		}
	}

	function kill(address _recipient) external onlySelf {
		selfdestruct(_recipient);
	}
}

import "BulletinBoard.sol";

contract UnanimousConsent {

	enum ConsentState {
		NONE,
		CONSENTED,
		EXECUTED
	}

	mapping (bytes32 =>
			mapping (address => ConsentState)) consentStates;
	address[] participants;
	BulletinBoard public bulletinBoard;

	modifier onlySelf {
		if (msg.sender == address(this)) {
			_
		} else {
			throw;
		}
	}

	function StateChannel(address[] _participants) {
		participants = _participants;
		bulletinBoard = new BulletinBoard(this);
	}

	function eval(
		address[] _calledContracts,
		bytes4[] _methodSignatures,
		uint[] _argumentLengths,
		bytes32[] _arguments
	) {
		if (
			_calledContracts.length != _methodSignatures.length ||
			_calledContracts.length != _argumentLengths.length
		) {
			throw;
		}

		uint i;
		uint j;
		bytes32 hash = sha3(_calledContracts, _methodSignatures, _argumentLengths, _arguments);

		for (i = 0; i < participants.length; i++) {
			if (consentStates[hash][participants[i]] == ConsentState.CONSENTED) {
				consentStates[hash][participants[i]] = ConsentState.EXECUTED;
			} else {
				throw;
			}
		}

		uint argumentsIndex = 0;
		for (i = 0; i < _calledContracts.length; i++) {

			bytes32[] memory arguments = new bytes32[](_argumentLengths[i]);
			for (j = 0; j < _argumentLengths[i]; j++) {
				arguments[j] = _arguments[argumentsIndex++];
			}

			if (!_calledContracts[i].call(_methodSignatures[i], arguments[i])) {
				throw;
			}
		}
	}

	function consent(bytes32 _hash) {
		consentStates[_hash][msg.sender] = ConsentState.CONSENTED;
	}

	function sendEther(address _recipient, uint _amount) external onlySelf {
		if (!_recipient.send(_amount)) {
			throw;
		}
	}

	function changeParticipants(address[] _participants) external onlySelf {
		participants = _participants;
	}

	function clean(bytes32[] _hashes, address[] _participants) external onlySelf {
		for (uint i = 0; i < _hashes.length; i++) {
			for (uint j = 0; j < _participants.length; j++) {
				delete consentStates[_hashes[i]][_participants[j]];
			}
		}
	}

	function kill(address _recipient) external onlySelf {
		selfdestruct(_recipient);
	}
}


Update pragma version for test contract

Update pragma version for test contract

pragma solidity ^0.5.0;

contract NewPragma {
  uint x;

  constructor() public {
    x = 5;
  }
}

pragma solidity ^0.4.24;

contract NewPragma {
  uint x;

  constructor() public {
    x = 5;
  }
}


contract: added comments, hardcoded pixel price

pragma solidity ^0.4.14;

contract KetherHomepage {
    /// Buy is emitted when an ad unit is reserved.
    event Buy(
        uint idx,
        address owner,
        uint x,
        uint y,
        uint width,
        uint height
    );

    /// Publish is emitted whenever the contents of an ad is changed.
    event Publish(
        uint idx,
        string link,
        string image,
        bool NSFW
    );

    /// Price is 1 kether divided by 1,000,000 pixels
    uint constant weiPixelPrice = 1000000000000000;

    /// Each grid cell represents 10 pixels.
    uint constant pixelsPerCell = 10;

    bool[100][100] grid;

    /// owner can withdraw the funds and override NSFW status of ad units.
    address owner;

    struct Ad {
        address owner;
        uint x;
        uint y;
        uint width;
        uint height;
        string link;
        string image;

        /// NSFW is whether the ad is suitable for people of all
        /// ages and workplaces.
        bool NSFW;
        /// forceNSFW can be set by owner.
        bool forceNSFW;
    }

    /// ads are stored in an array, the id of an ad is its index in this array.
    Ad[] public ads;

    function KetherHomepage(address _owner) {
        owner = _owner;
    }

    /// Ads must be purchased in 10x10 pixel blocks.
    /// Each coordinate represents 10 pixels. That is,
    ///   _x=5, _y=10, _width=3, _height=3
    /// Represents a 30x30 pixel ad at coordinates (50, 100)
    function buy(uint _x, uint _y, uint _width, uint _height) payable returns (uint idx) {
        uint cost = _width * _height * pixelsPerCell * weiPixelPrice;
        require(msg.value >= cost);

        // Loop over relevant grid entries
        for(uint i=0; i<_width; i++) {
            for(uint j=0; j<_height; j++) {
                if (grid[_x+i][_y+j]) {
                    // Already taken, undo.
                    revert();
                }
                grid[_x+i][_y+j] = true;
            }
        }

        // We reserved space in the grid, now make a placeholder entry.
        idx = ads.push(Ad(msg.sender, _x, _y, _width, _height, "", "", false, false)) - 1;

        Buy(idx, msg.sender, _x, _y, _width, _height);
        return idx;
    }

    /// Publish allows for setting the link, image, and NSFW status for the ad
    /// unit that is identified by the idx which was returned during the buy step.
    /// The link and image must be full web3-recognizeable URLs, such as:
    ///  - bzz://mydomain.eth/ad.png
    ///  - https://cdn.mydomain.com/ad.png
    function publish(uint _idx, string _link, string _image, bool _NSFW) {
        Ad storage ad = ads[_idx];
        require(ad.owner == msg.sender);
        ad.link = _link;
        ad.image = _image;
        ad.NSFW = _NSFW;

        Publish(_idx, ad.link, ad.image, ad.NSFW || ad.forceNSFW);
    }

    /// forceNSFW allows the owner to override the NSFW status for a specific ad unit.
    function forceNSFW(uint _idx, bool _NSFW) {
        require(msg.sender == owner);
        Ad storage ad = ads[_idx];
        ad.forceNSFW = _NSFW;

        Publish(_idx, ad.link, ad.image, ad.NSFW || ad.forceNSFW);
    }

    /// withdraw allows the owner to transfer out the balance of the contract.
    function withdraw() {
        require(msg.sender == owner);
        owner.transfer(this.balance);
    }
}

pragma solidity ^0.4.14;

contract KetherHomepage {
    /// Buy is emitted when an ad unit is reserved.
    event Buy(
        uint idx,
        address owner,
        uint x,
        uint y,
        uint width,
        uint height
    );

    /// Publish is emitted whenever the contents of an ad is changed.
    event Publish(
        uint idx,
        string link,
        string image,
        bool NSFW
    );

    uint weiPixelPrice;
    address owner;

    /// Each grid cell represents 10 pixels.
    uint constant pixelsPerCell = 100;
    bool[100][100] grid;

    struct Ad {
        address owner;
        uint x;
        uint y;
        uint width;
        uint height;
        string link;
        string image;

        /// NSFW is whether the ad is suitable for people of all
        /// ages and workplaces.
        bool NSFW;
        /// forceNSFW can be set by owner.
        bool forceNSFW;
    }

    Ad[] public ads;

    function KetherHomepage(address _owner, uint _weiPixelPrice) {
        owner = _owner;
        weiPixelPrice = _weiPixelPrice;
    }

    /// Ads must be purchased in 10x10 pixel blocks.
    /// Each coordinate represents 10 pixels. That is,
    ///   _x=5, _y=10, _width=3, _height=3
    /// Represents a 30x30 pixel ad at coordinates (50, 100)
    function buy(uint _x, uint _y, uint _width, uint _height) payable returns (uint idx) {
        uint cost = _width * _height * pixelsPerCell * weiPixelPrice;
        require(msg.value >= cost);

        // Loop over relevant grid entries
        for(uint i=0; i<_width; i++) {
            for(uint j=0; j<_height; j++) {
                if (grid[_x+i][_y+j]) {
                    // Already taken, undo.
                    revert();
                }
                grid[_x+i][_y+j] = true;
            }
        }

        // We reserved space in the grid, now make a placeholder entry.
        idx = ads.push(Ad(msg.sender, _x, _y, _width, _height, "", "", false, false)) - 1;

        Buy(idx, msg.sender, _x, _y, _width, _height);
        return idx;
    }

    function publish(uint _idx, string _link, string _image, bool _NSFW) {
        Ad storage ad = ads[_idx];
        require(ad.owner == msg.sender);
        ad.link = _link;
        ad.image = _image;
        ad.NSFW = _NSFW;

        Publish(_idx, ad.link, ad.image, ad.NSFW || ad.forceNSFW);
    }

    function forceNSFW(uint _idx, bool _NSFW) {
        require(msg.sender == owner);
        Ad storage ad = ads[_idx];
        ad.forceNSFW = _NSFW;

        Publish(_idx, ad.link, ad.image, ad.NSFW || ad.forceNSFW);
    }

    function withdraw() {
        require(msg.sender == owner);
        owner.transfer(this.balance);
    }
}