Fixes for delegator and transcoder status and withdrawing bond

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;

    // Keep track of all transcoder addresses
    address[] public registeredTranscoders;

    // 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;

        registeredTranscoders.push(msg.sender);

        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 number of registered transcoders
     */
    function getTotalRegisteredTranscoders() public constant returns (uint256) {
        return registeredTranscoders.length;
    }

     /*
     * @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")));
    }
}

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;

    // Keep track of all transcoder addresses
    address[] public registeredTranscoders;

    // 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;

        registeredTranscoders.push(msg.sender);

        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.Unbonding) {
            token.transfer(msg.sender, transcoders[msg.sender].bondedAmount);

            delete transcoders[msg.sender];
        } else if (delegatorStatus(msg.sender) == DelegatorStatus.Unbonding){
            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 <= 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 number of registered transcoders
     */
    function getTotalRegisteredTranscoders() public constant returns (uint256) {
        return registeredTranscoders.length;
    }

     /*
     * @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")));
    }
}


UpgradeableStandard23Token add require( _amount > 0 && balances[owner].add(_amount) > balances[owner]);

pragma solidity ^0.4.15;

 /**
 * @title UpgradeableToken
 *
 * Created by IaM <DEV> (Elky Bachtiar)
 * https://www.iamdeveloper.io
 *
 */

import './Standard23Token.sol';
import '../installed_contracts/zeppelin-solidity/contracts/ownership/Ownable.sol';
import '../installed_contracts/zeppelin-solidity/contracts/math/SafeMath.sol';

contract UpgradeableStandard23Token is Ownable, Standard23Token {
	using SafeMath for uint256;

    bytes32 public name;
    bytes32 public symbol;
    uint256 public decimals;

    address public owner;

    function UpgradeableStandard23Token(address _centralAdmin, uint256 _initialBalance, bytes32 _name, bytes32 _symbol, uint256 _decimals) {
        if (_centralAdmin != 0) {
          owner = _centralAdmin;
        } else {
          owner = msg.sender;
        }
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        balances[owner] = _initialBalance; // balance of Token address will be 100% of the HME company shares when initialize the contract
        totalSupply = _initialBalance;
    }

    function setName(bytes32 _name) onlyOwner {
        name = _name;
    }

    function setSymbol(bytes32 _symbol) onlyOwner {
        symbol = _symbol;
    }

    function setDecimals(uint256 _decimals) onlyOwner {
        decimals = _decimals;
    }

    function addSupply(uint256 _amount) onlyOwner {
        require( _amount > 0 && balances[owner].add(_amount) > balances[owner]);

    	balances[owner] = balances[owner].add(_amount);
    	totalSupply = totalSupply.add(_amount);
    }

    function subSupply(uint256 _amount) onlyOwner {
        require( _amount > 0 && balances[owner].add(_amount) > balances[owner]);

    	balances[owner] = balances[owner].sub(_amount);
    	totalSupply = totalSupply.sub(_amount);
    }
}

pragma solidity ^0.4.15;

 /**
 * @title UpgradeableToken
 *
 * Created by IaM <DEV> (Elky Bachtiar)
 * https://www.iamdeveloper.io
 *
 */

import './Standard23Token.sol';
import '../installed_contracts/zeppelin-solidity/contracts/ownership/Ownable.sol';
import '../installed_contracts/zeppelin-solidity/contracts/math/SafeMath.sol';

contract UpgradeableStandard23Token is Ownable, Standard23Token {
	using SafeMath for uint256;

    bytes32 public name;
    bytes32 public symbol;
    uint256 public decimals;

    address public owner;

    function UpgradeableStandard23Token(address _centralAdmin, uint256 _initialBalance, bytes32 _name, bytes32 _symbol, uint256 _decimals) {
        if (_centralAdmin != 0) {
          owner = _centralAdmin;
        } else {
          owner = msg.sender;
        }
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        balances[owner] = _initialBalance; // balance of Token address will be 100% of the HME company shares when initialize the contract
        totalSupply = _initialBalance;
    }

    function setName(bytes32 _name) onlyOwner {
        name = _name;
    }

    function setSymbol(bytes32 _symbol) onlyOwner {
        symbol = _symbol;
    }

    function setDecimals(uint256 _decimals) onlyOwner {
        decimals = _decimals;
    }

    function addSupply(uint256 _amount) onlyOwner {
        require( _amount > 0 );

    	balances[owner] = balances[owner].add(_amount);
    	totalSupply = totalSupply.add(_amount);
    }

    function subSupply(uint256 _amount) onlyOwner {
        require( _amount > 0 );

    	balances[owner] = balances[owner].sub(_amount);
    	totalSupply = totalSupply.sub(_amount);
    }
}


Solidity version fix.

Solidity version fix.

This contract will only compile with 0.4.16 and higher versions.

0.4.15 and earlier compiler will throw the following error because of `pure` keyword:

```
browser/NettingChannelLibrary.sol:389:9: ParserError: Expected token LBrace got reserved keyword 'Pure'
        pure
        ^
```

pragma solidity ^0.4.16;

import "./Token.sol";

library NettingChannelLibrary {
    string constant public contract_version = "0.2._";

    struct Participant
    {
        address node_address;

        // Total amount of token transferred to this smart contract through the
        // `deposit` function, note that direct token transfer cannot be
        // tracked and will be burned.
        uint256 balance;

        // The latest known merkle root of the pending hash-time locks, used to
        // validate the withdrawn proofs.
        bytes32 locksroot;

        // The latest known transferred_amount from this node to the other
        // participant, used to compute the net balance on settlement.
        uint256 transferred_amount;

        // Value used to order transfers and only accept the latest on calls to
        // update, this will only be relevant after either #182 or #293 is
        // implemented.
        uint64 nonce;

        // A mapping to keep track of locks that have been withdrawn.
        mapping(bytes32 => bool) withdrawn_locks;
    }

    struct Data {
        uint settle_timeout;
        uint opened;
        uint closed;
        address closing_address;
        Token token;
        Participant[2] participants;
        mapping(address => uint8) participant_index;
        bool updated;
    }

    modifier isClosed(Data storage self) {
        require(self.closed > 0);
        _;
    }

    modifier stillTimeout(Data storage self) {
        require(self.closed + self.settle_timeout >= block.number);
        _;
    }

    modifier timeoutOver(Data storage self) {
        require(self.closed + self.settle_timeout <= block.number);
        _;
    }

    /// @notice Deposit amount to channel.
    /// @dev Deposit an amount to the channel. At least one of the participants
    /// must deposit before the channel is opened.
    /// @param amount The amount to be deposited to the address
    /// @return Success if the transfer was successful
    /// @return The new balance of the invoker
    function deposit(Data storage self, uint256 amount)
        public
        returns (bool success, uint256 balance)
    {
        uint8 index;

        require(self.opened > 0);
        require(self.closed == 0);
        require(self.token.balanceOf(msg.sender) >= amount);

        index = index_or_throw(self, msg.sender);
        Participant storage participant = self.participants[index];

        success = self.token.transferFrom(msg.sender, this, amount);
        if (success == true) {
            balance = participant.balance;
            balance += amount;
            participant.balance = balance;

            return (true, balance);
        }

        return (false, 0);
    }

    /// @notice Close a channel between two parties that was used bidirectionally
    function close(
        Data storage self,
        uint64 nonce,
        uint256 transferred_amount,
        bytes32 locksroot,
        bytes32 extra_hash,
        bytes signature
    )
        public
    {
        address transfer_address;
        uint closer_index;
        uint counterparty_index;

        // close can be called only once
        require(self.closed == 0);
        self.closed = block.number;

        // Only a participant can call close
        closer_index = index_or_throw(self, msg.sender);
        self.closing_address = msg.sender;

        // Only the closing party can provide a transfer from the counterparty,
        // and only when this function is called, i.e. this value can not be
        // updated afterwards.

        // An empty value means that the closer never received a transfer, or
        // he is intentionally not providing the latest transfer, in which case
        // the closing party is going to lose the tokens that were transferred
        // to him.
        if (signature.length == 65) {
            transfer_address = recoverAddressFromSignature(
                nonce,
                transferred_amount,
                locksroot,
                extra_hash,
                signature
            );

            counterparty_index = index_or_throw(self, transfer_address);
            require(closer_index != counterparty_index);

            // update the structure of the counterparty with its data provided
            // by the closing node
            Participant storage counterparty = self.participants[counterparty_index];
            counterparty.nonce = uint64(nonce);
            counterparty.locksroot = locksroot;
            counterparty.transferred_amount = transferred_amount;
        }
    }

    /// @notice Updates counter party transfer after closing.
    function updateTransfer(
        Data storage self,
        uint64 nonce,
        uint256 transferred_amount,
        bytes32 locksroot,
        bytes32 extra_hash,
        bytes signature
    )
        isClosed(self)
        stillTimeout(self)
        public
    {
        address transfer_address;
        uint8 caller_index;
        uint8 closer_index;

        // updateTransfer can be called by the counter party only once
        require(!self.updated);
        self.updated = true;

        // Only a participant can call updateTransfer (#293 for third parties)
        caller_index = index_or_throw(self, msg.sender);

        // The closer is not allowed to call updateTransfer
        require(self.closing_address != msg.sender);

        // Counter party can only update the closer transfer
        transfer_address = recoverAddressFromSignature(
            nonce,
            transferred_amount,
            locksroot,
            extra_hash,
            signature
        );
        require(transfer_address == self.closing_address);

        // Update the structure of the closer with its data provided by the
        // counterparty
        closer_index = 1 - caller_index;

        self.participants[closer_index].nonce = nonce;
        self.participants[closer_index].locksroot = locksroot;
        self.participants[closer_index].transferred_amount = transferred_amount;
    }

    function recoverAddressFromSignature(
        uint64 nonce,
        uint256 transferred_amount,
        bytes32 locksroot,
        bytes32 extra_hash,
        bytes signature
    )
        constant
        internal
        returns (address)
    {
        bytes32 signed_hash;

        require(signature.length == 65);

        signed_hash = keccak256(
            nonce,
            transferred_amount,
            locksroot,
            this,
            extra_hash
        );

        var (r, s, v) = signatureSplit(signature);
        return ecrecover(signed_hash, v, r, s);
    }

    /// @notice Unlock a locked transfer
    /// @dev Unlock a locked transfer
    /// @param locked_encoded The lock
    /// @param merkle_proof The merkle proof
    /// @param secret The secret
    function withdraw(
        Data storage self,
        bytes locked_encoded,
        bytes merkle_proof,
        bytes32 secret
    )
        isClosed(self)
        public
    {
        uint amount;
        uint8 index;
        uint64 expiration;
        bytes32 h;
        bytes32 hashlock;

        // Check if msg.sender is a participant and select the partner (for
        // third party unlock see #541)
        index = 1 - index_or_throw(self, msg.sender);
        Participant storage counterparty = self.participants[index];

        // An empty locksroot means there are no pending locks
        require(counterparty.locksroot != 0);

        (expiration, amount, hashlock) = decodeLock(locked_encoded);

        // A lock can be withdrawn only once per participant
        require(!counterparty.withdrawn_locks[hashlock]);

        counterparty.withdrawn_locks[hashlock] = true;

        // The lock must not have expired, it does not matter how far in the
        // future it would have expired
        require(expiration >= block.number);
        require(hashlock == keccak256(secret));

        h = computeMerkleRoot(locked_encoded, merkle_proof);

        require(counterparty.locksroot == h);

        // This implementation allows for each transfer to be set only once, so
        // it's safe to update the transferred_amount in place.
        //
        // Once third parties are allowed to update the counter party transfer
        // (#293, #182) the locksroot may change, if the locksroot change the
        // transferred_amount must be reset and locks must be re-withdrawn, so
        // this is also safe.
        //
        // This may be problematic if an update changes the transferred_amount
        // but not the locksroot, since the locks don't need to be
        // re-withdrawn, the difference in the transferred_amount must be
        // accounted for.
        counterparty.transferred_amount += amount;
    }

    /// @notice Settles the balance between the two parties
    /// @dev Settles the balances of the two parties fo the channel
    /// @return The participants with netted balances
    function settle(Data storage self)
        isClosed(self)
        timeoutOver(self)
        public
    {
        uint8 closing_index;
        uint8 counter_index;
        uint256 total_deposit;
        uint256 counter_net;
        uint256 closer_amount;
        uint256 counter_amount;

        closing_index = index_or_throw(self, self.closing_address);
        counter_index = 1 - closing_index;

        Participant storage closing_party = self.participants[closing_index];
        Participant storage counter_party = self.participants[counter_index];

        counter_net = (
            counter_party.balance
            + closing_party.transferred_amount
            - counter_party.transferred_amount
        );

        // Direct token transfers done through the token `transfer` function
        // cannot be accounted for, these superfluous tokens will be burned,
        // this is because there is no way to tell which participant (if any)
        // had ownership over the token.
        total_deposit = closing_party.balance + counter_party.balance;

        // When the closing party does not provide the counter party transfer,
        // the `counter_net` may be larger than the `total_deposit`, without
        // the min the token transfer fail and the token is locked.
        counter_amount = min(counter_net, total_deposit);

        // When the counter party does not provide the closing party transfer,
        // then `counter_amount` may be negative and the transfer fails, force
        // the value to 0.
        counter_amount = max(counter_amount, 0);

        // At this point `counter_amount` is between [0,total_deposit], so this
        // is safe.
        closer_amount = total_deposit - counter_amount;

        if (counter_amount > 0) {
            require(self.token.transfer(counter_party.node_address, counter_amount));
        }

        if (closer_amount > 0) {
            require(self.token.transfer(closing_party.node_address, closer_amount));
        }

        selfdestruct(0x00000000000000000000);
    }

    function index_or_throw(Data storage self, address participant_address)
        constant
        private
        returns (uint8)
    {
        uint8 n;
        // Return index of participant, or throw
        n = self.participant_index[participant_address];
        assert(n != 0);
        return n - 1;
    }

    // NOTES:
    //
    // - The EVM is a big-endian, byte addressing machine, with 32bytes/256bits
    //   words.
    // - The Ethereum Contract ABI specifies that variable length types have a
    //   32bytes prefix to define the variable size.
    // - Solidity has additional data types that are narrower than 32bytes
    //   (e.g. uint128 uses a half word).
    // - Solidity uses the *least-significant* bits of the word to store the
    //   values of a narrower type.
    //
    // GENERAL APPROACH:
    //
    // Add to the message pointer the number of bytes required to move the
    // address so that the target data is at the end of the 32bytes word.
    //
    // EXAMPLE:
    //
    // To decode the cmdid, consider this initial state:
    //
    //
    //     v- pointer word start
    //     [ 32 bytes length prefix ][ cmdid ] ----
    //                              ^- pointer word end
    //
    //
    // Because the cmdid has 1 byte length the type uint8 is used, the decoder
    // needs to move the pointer so the cmdid is at the end of the pointer
    // word.
    //
    //
    //             v- pointer word start [moved 1byte ahead]
    //     [ 32 bytes length prefix ][ cmdid ] ----
    //                                       ^- pointer word end
    //
    //
    // Now the data of the cmdid can be loaded to the uint8 variable.
    //
    // REFERENCES:
    // - https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
    // - http://solidity.readthedocs.io/en/develop/assembly.html

    function decodeLock(bytes lock)
        pure
        internal
        returns (uint64 expiration, uint amount, bytes32 hashlock)
    {
        require(lock.length == 72);

        // Lock format:
        // [0:8] expiration
        // [8:40] amount
        // [40:72] hashlock
        assembly {
            expiration := mload(add(lock, 8))
            amount := mload(add(lock, 40))
            hashlock := mload(add(lock, 72))
        }
    }

    function signatureSplit(bytes signature)
        pure
        internal
        returns (bytes32 r, bytes32 s, uint8 v)
    {
        // The signature format is a compact form of:
        //   {bytes32 r}{bytes32 s}{uint8 v}
        // Compact means, uint8 is not padded to 32 bytes.
        assembly {
            r := mload(add(signature, 32))
            s := mload(add(signature, 64))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(signature, 65)), 0xff)
        }

        require(v == 27 || v == 28);
    }

    function computeMerkleRoot(bytes lock, bytes merkle_proof)
        pure
        internal
        returns (bytes32)
    {
        require(merkle_proof.length % 32 == 0);

        uint i;
        bytes32 h;
        bytes32 el;

        h = keccak256(lock);
        for (i = 32; i <= merkle_proof.length; i += 32) {
            assembly {
                el := mload(add(merkle_proof, i))
            }

            if (h < el) {
                h = keccak256(h, el);
            } else {
                h = keccak256(el, h);
            }
        }

        return h;
    }

    function min(uint a, uint b) pure internal returns (uint)
    {
        return a > b ? b : a;
    }

    function max(uint a, uint b) pure internal returns (uint)
    {
        return a > b ? a : b;
    }
}

pragma solidity ^0.4.11;

import "./Token.sol";

library NettingChannelLibrary {
    string constant public contract_version = "0.2._";

    struct Participant
    {
        address node_address;

        // Total amount of token transferred to this smart contract through the
        // `deposit` function, note that direct token transfer cannot be
        // tracked and will be burned.
        uint256 balance;

        // The latest known merkle root of the pending hash-time locks, used to
        // validate the withdrawn proofs.
        bytes32 locksroot;

        // The latest known transferred_amount from this node to the other
        // participant, used to compute the net balance on settlement.
        uint256 transferred_amount;

        // Value used to order transfers and only accept the latest on calls to
        // update, this will only be relevant after either #182 or #293 is
        // implemented.
        uint64 nonce;

        // A mapping to keep track of locks that have been withdrawn.
        mapping(bytes32 => bool) withdrawn_locks;
    }

    struct Data {
        uint settle_timeout;
        uint opened;
        uint closed;
        address closing_address;
        Token token;
        Participant[2] participants;
        mapping(address => uint8) participant_index;
        bool updated;
    }

    modifier isClosed(Data storage self) {
        require(self.closed > 0);
        _;
    }

    modifier stillTimeout(Data storage self) {
        require(self.closed + self.settle_timeout >= block.number);
        _;
    }

    modifier timeoutOver(Data storage self) {
        require(self.closed + self.settle_timeout <= block.number);
        _;
    }

    /// @notice Deposit amount to channel.
    /// @dev Deposit an amount to the channel. At least one of the participants
    /// must deposit before the channel is opened.
    /// @param amount The amount to be deposited to the address
    /// @return Success if the transfer was successful
    /// @return The new balance of the invoker
    function deposit(Data storage self, uint256 amount)
        public
        returns (bool success, uint256 balance)
    {
        uint8 index;

        require(self.opened > 0);
        require(self.closed == 0);
        require(self.token.balanceOf(msg.sender) >= amount);

        index = index_or_throw(self, msg.sender);
        Participant storage participant = self.participants[index];

        success = self.token.transferFrom(msg.sender, this, amount);
        if (success == true) {
            balance = participant.balance;
            balance += amount;
            participant.balance = balance;

            return (true, balance);
        }

        return (false, 0);
    }

    /// @notice Close a channel between two parties that was used bidirectionally
    function close(
        Data storage self,
        uint64 nonce,
        uint256 transferred_amount,
        bytes32 locksroot,
        bytes32 extra_hash,
        bytes signature
    )
        public
    {
        address transfer_address;
        uint closer_index;
        uint counterparty_index;

        // close can be called only once
        require(self.closed == 0);
        self.closed = block.number;

        // Only a participant can call close
        closer_index = index_or_throw(self, msg.sender);
        self.closing_address = msg.sender;

        // Only the closing party can provide a transfer from the counterparty,
        // and only when this function is called, i.e. this value can not be
        // updated afterwards.

        // An empty value means that the closer never received a transfer, or
        // he is intentionally not providing the latest transfer, in which case
        // the closing party is going to lose the tokens that were transferred
        // to him.
        if (signature.length == 65) {
            transfer_address = recoverAddressFromSignature(
                nonce,
                transferred_amount,
                locksroot,
                extra_hash,
                signature
            );

            counterparty_index = index_or_throw(self, transfer_address);
            require(closer_index != counterparty_index);

            // update the structure of the counterparty with its data provided
            // by the closing node
            Participant storage counterparty = self.participants[counterparty_index];
            counterparty.nonce = uint64(nonce);
            counterparty.locksroot = locksroot;
            counterparty.transferred_amount = transferred_amount;
        }
    }

    /// @notice Updates counter party transfer after closing.
    function updateTransfer(
        Data storage self,
        uint64 nonce,
        uint256 transferred_amount,
        bytes32 locksroot,
        bytes32 extra_hash,
        bytes signature
    )
        isClosed(self)
        stillTimeout(self)
        public
    {
        address transfer_address;
        uint8 caller_index;
        uint8 closer_index;

        // updateTransfer can be called by the counter party only once
        require(!self.updated);
        self.updated = true;

        // Only a participant can call updateTransfer (#293 for third parties)
        caller_index = index_or_throw(self, msg.sender);

        // The closer is not allowed to call updateTransfer
        require(self.closing_address != msg.sender);

        // Counter party can only update the closer transfer
        transfer_address = recoverAddressFromSignature(
            nonce,
            transferred_amount,
            locksroot,
            extra_hash,
            signature
        );
        require(transfer_address == self.closing_address);

        // Update the structure of the closer with its data provided by the
        // counterparty
        closer_index = 1 - caller_index;

        self.participants[closer_index].nonce = nonce;
        self.participants[closer_index].locksroot = locksroot;
        self.participants[closer_index].transferred_amount = transferred_amount;
    }

    function recoverAddressFromSignature(
        uint64 nonce,
        uint256 transferred_amount,
        bytes32 locksroot,
        bytes32 extra_hash,
        bytes signature
    )
        constant
        internal
        returns (address)
    {
        bytes32 signed_hash;

        require(signature.length == 65);

        signed_hash = keccak256(
            nonce,
            transferred_amount,
            locksroot,
            this,
            extra_hash
        );

        var (r, s, v) = signatureSplit(signature);
        return ecrecover(signed_hash, v, r, s);
    }

    /// @notice Unlock a locked transfer
    /// @dev Unlock a locked transfer
    /// @param locked_encoded The lock
    /// @param merkle_proof The merkle proof
    /// @param secret The secret
    function withdraw(
        Data storage self,
        bytes locked_encoded,
        bytes merkle_proof,
        bytes32 secret
    )
        isClosed(self)
        public
    {
        uint amount;
        uint8 index;
        uint64 expiration;
        bytes32 h;
        bytes32 hashlock;

        // Check if msg.sender is a participant and select the partner (for
        // third party unlock see #541)
        index = 1 - index_or_throw(self, msg.sender);
        Participant storage counterparty = self.participants[index];

        // An empty locksroot means there are no pending locks
        require(counterparty.locksroot != 0);

        (expiration, amount, hashlock) = decodeLock(locked_encoded);

        // A lock can be withdrawn only once per participant
        require(!counterparty.withdrawn_locks[hashlock]);

        counterparty.withdrawn_locks[hashlock] = true;

        // The lock must not have expired, it does not matter how far in the
        // future it would have expired
        require(expiration >= block.number);
        require(hashlock == keccak256(secret));

        h = computeMerkleRoot(locked_encoded, merkle_proof);

        require(counterparty.locksroot == h);

        // This implementation allows for each transfer to be set only once, so
        // it's safe to update the transferred_amount in place.
        //
        // Once third parties are allowed to update the counter party transfer
        // (#293, #182) the locksroot may change, if the locksroot change the
        // transferred_amount must be reset and locks must be re-withdrawn, so
        // this is also safe.
        //
        // This may be problematic if an update changes the transferred_amount
        // but not the locksroot, since the locks don't need to be
        // re-withdrawn, the difference in the transferred_amount must be
        // accounted for.
        counterparty.transferred_amount += amount;
    }

    /// @notice Settles the balance between the two parties
    /// @dev Settles the balances of the two parties fo the channel
    /// @return The participants with netted balances
    function settle(Data storage self)
        isClosed(self)
        timeoutOver(self)
        public
    {
        uint8 closing_index;
        uint8 counter_index;
        uint256 total_deposit;
        uint256 counter_net;
        uint256 closer_amount;
        uint256 counter_amount;

        closing_index = index_or_throw(self, self.closing_address);
        counter_index = 1 - closing_index;

        Participant storage closing_party = self.participants[closing_index];
        Participant storage counter_party = self.participants[counter_index];

        counter_net = (
            counter_party.balance
            + closing_party.transferred_amount
            - counter_party.transferred_amount
        );

        // Direct token transfers done through the token `transfer` function
        // cannot be accounted for, these superfluous tokens will be burned,
        // this is because there is no way to tell which participant (if any)
        // had ownership over the token.
        total_deposit = closing_party.balance + counter_party.balance;

        // When the closing party does not provide the counter party transfer,
        // the `counter_net` may be larger than the `total_deposit`, without
        // the min the token transfer fail and the token is locked.
        counter_amount = min(counter_net, total_deposit);

        // When the counter party does not provide the closing party transfer,
        // then `counter_amount` may be negative and the transfer fails, force
        // the value to 0.
        counter_amount = max(counter_amount, 0);

        // At this point `counter_amount` is between [0,total_deposit], so this
        // is safe.
        closer_amount = total_deposit - counter_amount;

        if (counter_amount > 0) {
            require(self.token.transfer(counter_party.node_address, counter_amount));
        }

        if (closer_amount > 0) {
            require(self.token.transfer(closing_party.node_address, closer_amount));
        }

        selfdestruct(0x00000000000000000000);
    }

    function index_or_throw(Data storage self, address participant_address)
        constant
        private
        returns (uint8)
    {
        uint8 n;
        // Return index of participant, or throw
        n = self.participant_index[participant_address];
        assert(n != 0);
        return n - 1;
    }

    // NOTES:
    //
    // - The EVM is a big-endian, byte addressing machine, with 32bytes/256bits
    //   words.
    // - The Ethereum Contract ABI specifies that variable length types have a
    //   32bytes prefix to define the variable size.
    // - Solidity has additional data types that are narrower than 32bytes
    //   (e.g. uint128 uses a half word).
    // - Solidity uses the *least-significant* bits of the word to store the
    //   values of a narrower type.
    //
    // GENERAL APPROACH:
    //
    // Add to the message pointer the number of bytes required to move the
    // address so that the target data is at the end of the 32bytes word.
    //
    // EXAMPLE:
    //
    // To decode the cmdid, consider this initial state:
    //
    //
    //     v- pointer word start
    //     [ 32 bytes length prefix ][ cmdid ] ----
    //                              ^- pointer word end
    //
    //
    // Because the cmdid has 1 byte length the type uint8 is used, the decoder
    // needs to move the pointer so the cmdid is at the end of the pointer
    // word.
    //
    //
    //             v- pointer word start [moved 1byte ahead]
    //     [ 32 bytes length prefix ][ cmdid ] ----
    //                                       ^- pointer word end
    //
    //
    // Now the data of the cmdid can be loaded to the uint8 variable.
    //
    // REFERENCES:
    // - https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
    // - http://solidity.readthedocs.io/en/develop/assembly.html

    function decodeLock(bytes lock)
        pure
        internal
        returns (uint64 expiration, uint amount, bytes32 hashlock)
    {
        require(lock.length == 72);

        // Lock format:
        // [0:8] expiration
        // [8:40] amount
        // [40:72] hashlock
        assembly {
            expiration := mload(add(lock, 8))
            amount := mload(add(lock, 40))
            hashlock := mload(add(lock, 72))
        }
    }

    function signatureSplit(bytes signature)
        pure
        internal
        returns (bytes32 r, bytes32 s, uint8 v)
    {
        // The signature format is a compact form of:
        //   {bytes32 r}{bytes32 s}{uint8 v}
        // Compact means, uint8 is not padded to 32 bytes.
        assembly {
            r := mload(add(signature, 32))
            s := mload(add(signature, 64))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(signature, 65)), 0xff)
        }

        require(v == 27 || v == 28);
    }

    function computeMerkleRoot(bytes lock, bytes merkle_proof)
        pure
        internal
        returns (bytes32)
    {
        require(merkle_proof.length % 32 == 0);

        uint i;
        bytes32 h;
        bytes32 el;

        h = keccak256(lock);
        for (i = 32; i <= merkle_proof.length; i += 32) {
            assembly {
                el := mload(add(merkle_proof, i))
            }

            if (h < el) {
                h = keccak256(h, el);
            } else {
                h = keccak256(el, h);
            }
        }

        return h;
    }

    function min(uint a, uint b) pure internal returns (uint)
    {
        return a > b ? b : a;
    }

    function max(uint a, uint b) pure internal returns (uint)
    {
        return a > b ? a : b;
    }
}